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);
135 *request = popp->request;
137 *state = popp->state;
138 if (noti_priv_id != NULL)
139 *noti_priv_id = popp->noti_priv_id;
140 if (received_size != NULL)
141 *received_size = popp->received_size;
142 if (file_size != NULL)
143 *file_size = popp->file_size;
148 } while (*queue != NULL); // if meet the tail of queue
149 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_mutex);
153 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)
156 TRACE_ERROR("check memory address of queue");
160 TRACE_ERROR("check client memory address");
164 int lock = CLIENT_MUTEX_TRYLOCK(&g_dp_notification_queue_ongoing_mutex);
166 TRACE_DEBUG("skip queue is used by other thread");
169 if (queue == NULL || *queue == NULL) {
170 //TRACE_DEBUG("queue empty");
171 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
174 // get a head of queue
177 dp_notification_queue_fmt *popp = *queue;
179 if (popp->slot == NULL) {
180 TRACE_DEBUG("queue error slot:%p id:%d", popp->slot, popp->id);
184 *request = popp->request;
186 *state = popp->state;
187 if (received_size != NULL)
188 *received_size = popp->received_size;
189 if (file_size != NULL)
190 *file_size = popp->file_size;
195 } while (*queue != NULL); // if meet the tail of queue
196 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
200 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)
203 TRACE_ERROR("check memory address of queue");
206 if (slot == NULL || request == NULL) {
207 TRACE_ERROR("check client and request memory address");
209 } else if (id <= 0) {
210 TRACE_ERROR("check slot or download id:%d", id);
215 CLIENT_MUTEX_LOCK(&g_dp_notification_queue_ongoing_mutex);
216 // search the tail of queue
218 dp_notification_queue_fmt *tailp = *queue;
219 dp_notification_queue_fmt *prevp = NULL;
220 for (; tailp != NULL; i++) {
221 if (tailp->slot == slot && tailp->request == request) {
222 if (tailp->id == id && tailp->state == state && tailp->type == type) {
223 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
230 dp_notification_queue_fmt *new_queue = (dp_notification_queue_fmt *)malloc(sizeof(dp_notification_queue_fmt));
231 if (new_queue != NULL) {
232 new_queue->slot = slot;
234 new_queue->state = state;
235 new_queue->received_size = received_size;
236 new_queue->file_size = file_size;
237 new_queue->type = type;
238 new_queue->request = request;
239 new_queue->next = NULL;
243 prevp->next = new_queue;
246 //TRACE_DEBUG("queue push %d id:%d", i, id);
247 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
251 void __dp_notification_queue_clear(dp_notification_queue_fmt **queue, const int id)
254 TRACE_ERROR("check memory address of queue");
257 CLIENT_MUTEX_LOCK(&g_dp_notification_queue_mutex);
259 dp_notification_queue_fmt *tailp = *queue;
260 dp_notification_queue_fmt *prevp = NULL;
261 for (; tailp != NULL; i++) {
262 if (tailp->id == id) {
265 *queue = tailp->next;
267 prevp->next = tailp->next;
268 TRACE_DEBUG("queue clear this %d id:%d", i, tailp->id);
275 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_mutex);
278 int dp_notification_manager_clear_notification(void *slot, void *request, const dp_noti_type type)
280 dp_request_fmt *requestp = request;
281 if (request == NULL) {
282 TRACE_DEBUG("check address request:%p id:%d",
283 request, (request == NULL ? 0 : requestp->id));
286 __dp_notification_queue_clear(&g_dp_notification, requestp->id);
287 TRACE_DEBUG("push clear id:%d noti_priv_id:%d", requestp->id, requestp->noti_priv_id);
288 if (__dp_notification_queue_push(&g_dp_notification_clear, slot, request, requestp->id, requestp->state, -1, 0, 0, type) < 0) {
289 TRACE_ERROR("failed to push to notification id:%d", requestp->id);
292 dp_notification_manager_wake_up();
296 int dp_notification_manager_push_notification(void *slot, void *request, const dp_noti_type type)
298 dp_request_fmt *requestp = request;
299 if (slot == NULL || request == NULL) {
300 TRACE_DEBUG("check address client:%p request:%p id:%d", slot,
301 request, (request == NULL ? 0 : requestp->id));
304 // TRACE_DEBUG("push noti id:%d noti_priv_id:%d type:%d", requestp->id, requestp->noti_priv_id, type);
305 if (type == DP_NOTIFICATION) {
306 __dp_notification_queue_clear(&g_dp_notification_ongoing, requestp->id);
307 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) {
308 TRACE_ERROR("failed to push to notification id:%d", requestp->id);
312 __dp_notification_queue_clear(&g_dp_notification, requestp->id);
313 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) {
314 TRACE_ERROR("failed to push to notification id:%d", requestp->id);
318 dp_notification_manager_wake_up();
322 static void __dp_notification_manager_check_notification()
326 int errorcode = DP_ERROR_NONE;
327 int download_id = -1;
329 dp_noti_type noti_type = -1;
330 dp_client_slots_fmt *slot = NULL;
331 dp_request_fmt *request = NULL;
333 if (__dp_notification_queue_pop(&g_dp_notification_clear, (void *)&slot, (void *)&request, &download_id, &state, NULL, NULL, NULL, ¬i_type) == 0) {
335 int noti_priv_id = -1;
336 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
337 if (request != NULL && request->id == download_id && request->noti_priv_id >= 0) {
338 noti_priv_id = request->noti_priv_id;
339 request->noti_priv_id = -1;
340 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)
341 TRACE_ERROR("failed to set priv_id id:%d error:%s", download_id, dp_print_errorcode(errorcode));
343 CLIENT_MUTEX_UNLOCK(&slot->mutex);
345 TRACE_DEBUG("clear ongoing id:%d noti_priv_id:%d type:%d", download_id, noti_priv_id, noti_type);
346 if (noti_priv_id >= 0) {
347 if (noti_type > DP_NOTIFICATION)
348 dp_notification_delete_ongoing(noti_priv_id);
350 dp_notification_delete(noti_priv_id);
356 int noti_priv_id = -1;
357 if (__dp_notification_queue_pop(&g_dp_notification, (void *)&slot, (void *)&request, &download_id, &state, ¬i_priv_id, NULL, NULL, ¬i_type) == 0) {
359 __dp_notification_queue_clear(&g_dp_notification_ongoing, download_id); // prevent new ongoing
360 if (noti_priv_id >= 0) {
361 TRACE_DEBUG("clear ongoing(%d) id:%d type:%d state:%d", noti_priv_id, download_id, noti_type, state);
362 dp_notification_delete_ongoing(noti_priv_id);
365 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
366 TRACE_DEBUG("notification id:%d type:%d state:%d", download_id, noti_type, state);
367 if (request != NULL && request->id == download_id && request->noti_priv_id >= 0) {
368 dp_notification_delete_ongoing(request->noti_priv_id);
369 request->noti_priv_id = -1;
371 dp_content_type content_type = DP_CONTENT_UNKNOWN;
373 content_type = request->content_type;
374 noti_priv_id = dp_notification_new(slot->client.dbhandle, download_id, state, content_type, slot->pkgname); // lazy API
375 TRACE_DEBUG("new notification(%d) id:%d type:%d state:%d", noti_priv_id, download_id, noti_type, state);
376 if (noti_priv_id < 0) {
377 TRACE_ERROR("failed to register notification for id:%d", download_id);
379 if (request != NULL && request->id == download_id)
380 request->noti_priv_id = noti_priv_id;
381 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)
382 TRACE_ERROR("failed to set priv_id id:%d error:%s", download_id, dp_print_errorcode(errorcode));
384 CLIENT_MUTEX_UNLOCK(&slot->mutex);
389 double received_size = 0;
390 double file_size = 0;
391 if (__dp_notification_queue_ongoing_pop(&g_dp_notification_ongoing, (void *)&slot, (void *)&request, &download_id, &state, &received_size, &file_size, ¬i_type) == 0) {
393 int noti_priv_id = -1;
395 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
396 if (request != NULL && request->id == download_id) {
397 request_id = download_id;
398 if (request->noti_priv_id >= 0)
399 noti_priv_id = request->noti_priv_id;
401 CLIENT_MUTEX_UNLOCK(&slot->mutex);
403 TRACE_ERROR("ongoing wrong address id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
407 if (request_id < 0) {
408 TRACE_ERROR("ongoing wrong info id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
413 if (noti_priv_id < 0 && noti_type > DP_NOTIFICATION_ONGOING) {
414 TRACE_DEBUG("ongoing precheck id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
415 noti_type = DP_NOTIFICATION_ONGOING;
418 TRACE_DEBUG("ongoing id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
420 char *subject = NULL;
421 if (noti_type == DP_NOTIFICATION || noti_type == DP_NOTIFICATION_ONGOING_UPDATE) {
423 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
424 if (request != NULL) {
425 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)
426 TRACE_ERROR("failed to get subject id:%d error:%s", download_id, dp_print_errorcode(errorcode));
427 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)
428 TRACE_ERROR("failed to get content_name id:%d error:%s", download_id, dp_print_errorcode(errorcode));
430 CLIENT_MUTEX_UNLOCK(&slot->mutex);
434 if (noti_type > DP_NOTIFICATION_ONGOING) { // update
435 if (noti_priv_id >= 0 && dp_notification_ongoing_update(noti_priv_id, received_size, file_size, subject) < 0)
436 TRACE_ERROR("failed to update ongoing for id:%d", download_id);
437 } else { // new ongoing
438 if (noti_priv_id >= 0) {
439 TRACE_DEBUG("clear ongoing id:%d noti_priv_id:%d", download_id, noti_priv_id);
440 dp_notification_delete(noti_priv_id);
441 dp_notification_delete_ongoing(noti_priv_id);
444 unsigned char *raws_buffer = NULL;
446 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
447 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)
448 TRACE_DEBUG("failed to get bundle raws id:%d error:%s", download_id, dp_print_errorcode(errorcode));
449 CLIENT_MUTEX_UNLOCK(&slot->mutex);
451 noti_priv_id = dp_notification_ongoing_new(slot->pkgname, subject, raws_buffer, length);
452 TRACE_DEBUG("new ongoing(%d) id:%d type:%d state:%d", noti_priv_id, download_id, noti_type, state);
454 if (noti_priv_id < 0) {
455 TRACE_ERROR("failed to update ongoing for id:%d", download_id);
457 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
459 request->noti_priv_id = noti_priv_id;
460 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)
461 TRACE_ERROR("failed to set priv_id id:%d error:%s", download_id, dp_print_errorcode(errorcode));
462 CLIENT_MUTEX_UNLOCK(&slot->mutex);
470 } while (pop_queue > 0);
473 static void *__dp_notification_manager(void *arg)
475 pthread_cond_init(&g_dp_notification_manager_cond, NULL);
477 dp_notification_set_locale();
479 while (g_dp_notification_manager_tid > 0) {
481 if (g_dp_notification_manager_tid <= 0) {
482 TRACE_DEBUG("notification-manager is closed by other thread");
486 // check wifi_direct first
487 __dp_notification_manager_check_notification();
489 CLIENT_MUTEX_LOCK(&g_dp_notification_manager_mutex);
490 pthread_cond_wait(&g_dp_notification_manager_cond, &g_dp_notification_manager_mutex);
491 CLIENT_MUTEX_UNLOCK(&g_dp_notification_manager_mutex);
494 TRACE_DEBUG("notification-manager's working is done");
495 dp_notification_clear_locale();
496 pthread_cond_destroy(&g_dp_notification_manager_cond);
501 static int __dp_notification_manager_start()
503 if (g_dp_notification_manager_tid == 0 ||
504 pthread_kill(g_dp_notification_manager_tid, 0) == ESRCH) {
505 TRACE_DEBUG("try to create notification-manager");
506 if (pthread_create(&g_dp_notification_manager_tid, NULL,
507 __dp_notification_manager, NULL) != 0) {
508 TRACE_ERROR("failed to create notification-manager");
515 void dp_notification_manager_wake_up()
517 if (g_dp_notification_manager_tid > 0) {
518 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_notification_manager_mutex);
520 pthread_cond_signal(&g_dp_notification_manager_cond);
521 CLIENT_MUTEX_UNLOCK(&g_dp_notification_manager_mutex);
524 __dp_notification_manager_start();
528 void dp_notification_manager_kill()
530 if (g_dp_notification_manager_tid > 0 &&
531 pthread_kill(g_dp_notification_manager_tid, 0) != ESRCH) {
532 //send signal to notification thread
535 tid = g_dp_notification_manager_tid;
536 CLIENT_MUTEX_LOCK(&g_dp_notification_manager_mutex);
537 g_dp_notification_manager_tid = 0;
538 pthread_cond_signal(&g_dp_notification_manager_cond);
539 CLIENT_MUTEX_UNLOCK(&g_dp_notification_manager_mutex);
540 pthread_join(tid, (void **)&status);
545 int dp_notification_manager_clear_notification(void *slot, void *request, const dp_noti_type type)
550 int dp_notification_manager_push_notification(void *slot, void *request, const dp_noti_type type)
555 void dp_notification_manager_wake_up()
560 void dp_notification_manager_kill()