tizen 2.3.1 release
[framework/appfw/alarm-manager.git] / alarm-manager-schedule.c
1 /*
2  *  alarm-manager
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Venkatesha Sarpangala <sarpangala.v@samsung.com>, Jayoun Lee <airjany@samsung.com>,
7  * Sewook Park <sewook7.park@samsung.com>, Jaeho Lee <jaeho81.lee@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */
22
23 #define _BSD_SOURCE             /*localtime_r requires */
24 #include<stdio.h>
25 #include<stdlib.h>
26 #include<time.h>
27 #include<signal.h>
28 #include<string.h>
29 #include<sys/types.h>
30
31 #include<glib.h>
32
33 #include"alarm.h"
34 #include"alarm-internal.h"
35 #define WAKEUP_ALARM_APP_ID "org.tizen.alarm.ALARM"     /*alarm ui
36                                                            application's alarm's dbus_service name instead of 21 value */
37 #define DST_TIME_DIFF 1
38
39 extern __alarm_server_context_t alarm_context;
40 extern GSList *g_scheduled_alarm_list;
41
42 static time_t __alarm_next_duetime_once(__alarm_info_t *__alarm_info);
43 static time_t __alarm_next_duetime_repeat(__alarm_info_t *__alarm_info);
44 static time_t __alarm_next_duetime_annually(__alarm_info_t *__alarm_info);
45 static time_t __alarm_next_duetime_monthly(__alarm_info_t *__alarm_info);
46 static time_t __alarm_next_duetime_weekly(__alarm_info_t *__alarm_info);
47 static bool __find_next_alarm_to_be_scheduled(time_t *min_due_time);
48 bool _alarm_schedule(void);
49
50 bool _clear_scheduled_alarm_list()
51 {
52         g_slist_free_full(g_scheduled_alarm_list, g_free);
53         g_scheduled_alarm_list = NULL;
54
55         return true;
56 }
57
58 bool _init_scheduled_alarm_list()
59 {
60         _clear_scheduled_alarm_list();
61
62         return true;
63 }
64
65 bool _add_to_scheduled_alarm_list(__alarm_info_t *__alarm_info)
66 {
67 /*
68  *      20080328. Sewook Park(sewook7.park@samsung.com)
69  *      When multiple alarms are expired at same time, dbus rpc call for alarm
70  *      ui should be invoked first.(Ui conflicting manager cannot manage the
71  *      different kinds of alarm popups(wake up alarm/org alarm) correctly,
72  *      when they are displayed at same time)So when arranging the schedule
73  *      alarm list, wake up alarm element is located ahead.
74  */
75
76         bool prior = false;
77         gint count = 0;
78         GSList *iter = NULL;
79         __scheduled_alarm_t *alarm = NULL;
80         __scheduled_alarm_t *entry = NULL;
81
82         alarm = g_malloc(sizeof(__scheduled_alarm_t));
83         if (alarm == NULL) {
84                 return false;
85         }
86
87         alarm->used = true;
88         alarm->alarm_id = __alarm_info->alarm_id;
89         alarm->pid = __alarm_info->pid;
90         alarm->__alarm_info = __alarm_info;
91
92         SECURE_LOGD("%s :alarm->pid =%d, app_service_name=%s(%u)\n",
93                         __FUNCTION__, alarm->pid,
94                         g_quark_to_string(alarm->__alarm_info->quark_app_service_name),
95                         alarm->__alarm_info->quark_app_service_name);
96
97         if (alarm->__alarm_info->quark_app_service_name != g_quark_from_string(WAKEUP_ALARM_APP_ID)) {
98                 g_scheduled_alarm_list = g_slist_append(g_scheduled_alarm_list, alarm);
99         }
100         else {
101                 for (iter = g_scheduled_alarm_list; iter != NULL; iter = g_slist_next(iter)) {
102                         count++;
103                         entry = iter->data;
104                         if (entry->__alarm_info->quark_app_service_name != g_quark_from_string(WAKEUP_ALARM_APP_ID)) {
105                                 prior = true;
106                                 break;
107                         }
108                 }
109
110                 if (!prior) {
111                         g_scheduled_alarm_list = g_slist_append(g_scheduled_alarm_list, alarm);
112                         ALARM_MGR_LOG_PRINT("appended : prior is %d\tcount is %d\n", prior, count);
113                 }
114                 else {
115                         g_scheduled_alarm_list = g_slist_insert(g_scheduled_alarm_list, alarm, count - 1);
116                         ALARM_MGR_LOG_PRINT("appended : prior is %d\tcount is %d\n", prior, count);
117                 }
118         }
119
120         return true;
121 }
122
123 bool _remove_from_scheduled_alarm_list(int pid, alarm_id_t alarm_id)
124 {
125         bool result = false;
126         GSList *iter = NULL;
127         __scheduled_alarm_t *alarm = NULL;
128
129         for (iter = g_scheduled_alarm_list; iter != NULL; iter = g_slist_next(iter)) {
130                 alarm = iter->data;
131                 if (alarm->alarm_id == alarm_id) {
132                         g_scheduled_alarm_list = g_slist_remove(g_scheduled_alarm_list, iter->data);
133                         g_free(alarm);
134                         result = true;
135                         break;
136                 }
137         }
138
139         if (g_slist_length(g_scheduled_alarm_list) == 0) {
140                 alarm_context.c_due_time = -1;
141         }
142
143         return result;
144 }
145
146 static time_t __alarm_next_duetime_once(__alarm_info_t *__alarm_info)
147 {
148         time_t due_time = 0;
149         time_t due_time_tmp = 0;
150         time_t current_time = 0;
151         struct tm duetime_tm;
152         struct tm tmp_tm;
153         int current_dst = 0;
154
155         alarm_info_t *alarm_info = &__alarm_info->alarm_info;
156         alarm_date_t *start = &alarm_info->start;
157
158         tzset();
159         time(&current_time);
160         localtime_r(&current_time, &duetime_tm);
161         duetime_tm.tm_hour = start->hour;
162         duetime_tm.tm_min = start->min;
163         duetime_tm.tm_sec = start->sec;
164
165         current_dst = duetime_tm.tm_isdst;
166         duetime_tm.tm_isdst = -1;
167
168         if (start->year == 0 && start->month == 0 && start->day == 0)
169                 /*any date */  {
170                 due_time = mktime(&duetime_tm);
171                 if (!(due_time > current_time)) {
172                         due_time = due_time + 60 * 60 * 24;
173                 }
174         } else  /*specific date*/ {
175                 duetime_tm.tm_year = start->year - 1900;
176                 duetime_tm.tm_mon = start->month - 1;
177                 duetime_tm.tm_mday = start->day;
178                 due_time = mktime(&duetime_tm);
179         }
180
181         if (due_time <= current_time) {
182                 ALARM_MGR_EXCEPTION_PRINT("duetime is less than or equal to current time. current_dst = %d", current_dst);
183                 duetime_tm.tm_isdst = 0;        // DST off
184
185                 due_time_tmp = mktime(&duetime_tm);
186                 localtime_r(&due_time_tmp, &tmp_tm);
187
188                 ALARM_MGR_LOG_PRINT("%d:%d:%d. duetime = %d", tmp_tm.tm_hour, tmp_tm.tm_min, tmp_tm.tm_sec, due_time);
189                 if (tmp_tm.tm_hour == start->hour && tmp_tm.tm_min == start->min && tmp_tm.tm_sec == start->sec ) {
190                         due_time = due_time_tmp;
191                         ALARM_MGR_EXCEPTION_PRINT("due_time = %d",due_time);
192                 }
193         }
194         else {
195                 localtime_r(&due_time, &tmp_tm);
196                 ALARM_MGR_LOG_PRINT("%d:%d:%d. current_dst = %d, duetime_dst = %d", tmp_tm.tm_hour, tmp_tm.tm_min, tmp_tm.tm_sec, current_dst, tmp_tm.tm_isdst);
197
198                 if (current_dst == 1 && tmp_tm.tm_isdst == 1 && tmp_tm.tm_hour == start->hour + 1) {
199                         // When the calculated duetime is forwarded 1hour due to DST, Adds 23hours.
200                         due_time += 60 * 60 * 23;
201                         localtime_r(&due_time, &duetime_tm);
202                         ALARM_MGR_EXCEPTION_PRINT("due_time = %d",due_time);
203                 }
204         }
205
206         ALARM_MGR_EXCEPTION_PRINT("Final due_time = %d, %s",due_time, ctime(&due_time));
207         return due_time;
208 }
209
210 static time_t __alarm_next_duetime_repeat(__alarm_info_t *__alarm_info)
211 {
212         time_t due_time = 0;
213         time_t current_time = 0;
214         struct tm duetime_tm;
215
216         alarm_info_t *alarm_info = &__alarm_info->alarm_info;
217         alarm_date_t *start = &alarm_info->start;
218
219         time(&current_time);
220         /*localtime_r(&current_time, &duetime_tm); */
221
222         duetime_tm.tm_hour = start->hour;
223         duetime_tm.tm_min = start->min;
224         duetime_tm.tm_sec = start->sec;
225
226         duetime_tm.tm_year = start->year - 1900;
227         duetime_tm.tm_mon = start->month - 1;
228         duetime_tm.tm_mday = start->day;
229         duetime_tm.tm_isdst = -1;
230
231         due_time = mktime(&duetime_tm);
232
233         while (__alarm_info->start > due_time || current_time >= due_time) {
234                 due_time += alarm_info->mode.u_interval.interval;
235
236                 /* due_time = mktime(&duetime_tm); */
237         }
238
239         if (due_time - current_time < 10)
240                 due_time += alarm_info->mode.u_interval.interval;
241
242         localtime_r(&due_time, &duetime_tm);
243
244         start->year = duetime_tm.tm_year + 1900;
245         start->month = duetime_tm.tm_mon + 1;
246         start->day = duetime_tm.tm_mday;
247         start->hour = duetime_tm.tm_hour;
248         start->min = duetime_tm.tm_min;
249         start->sec = duetime_tm.tm_sec;
250
251         return due_time;
252
253 }
254
255 static time_t __alarm_next_duetime_annually(__alarm_info_t *__alarm_info)
256 {
257         time_t due_time = 0;
258         time_t current_time = 0;
259         struct tm duetime_tm;
260
261         alarm_info_t *alarm_info = &__alarm_info->alarm_info;
262         alarm_date_t *start = &alarm_info->start;
263
264         time(&current_time);
265         localtime_r(&current_time, &duetime_tm);
266         duetime_tm.tm_hour = start->hour;
267         duetime_tm.tm_min = start->min;
268         duetime_tm.tm_sec = start->sec;
269
270         if (start->year != 0) {
271                 duetime_tm.tm_year = start->year - 1900;
272         }
273
274         duetime_tm.tm_mon = start->month - 1;
275         duetime_tm.tm_mday = start->day;
276
277         due_time = mktime(&duetime_tm);
278
279         while (__alarm_info->start > due_time || current_time > due_time) {
280                 duetime_tm.tm_year += 1;
281                 due_time = mktime(&duetime_tm);
282         }
283
284         return due_time;
285
286 }
287
288 static time_t __alarm_next_duetime_monthly(__alarm_info_t *__alarm_info)
289 {
290         time_t due_time = 0;
291         time_t current_time = 0;
292         struct tm duetime_tm;
293
294         alarm_info_t *alarm_info = &__alarm_info->alarm_info;
295         alarm_date_t *start = &alarm_info->start;
296
297         time(&current_time);
298         localtime_r(&current_time, &duetime_tm);
299         duetime_tm.tm_hour = start->hour;
300         duetime_tm.tm_min = start->min;
301         duetime_tm.tm_sec = start->sec;
302
303         if (start->year != 0) {
304                 duetime_tm.tm_year = start->year - 1900;
305         }
306
307         if (start->month != 0) {
308
309                 duetime_tm.tm_mon = start->month - 1;
310         }
311
312         duetime_tm.tm_mday = start->day;
313
314         due_time = mktime(&duetime_tm);
315
316         while (__alarm_info->start > due_time || current_time > due_time) {
317                 duetime_tm.tm_mon += 1;
318                 if (duetime_tm.tm_mon == 12) {
319                         duetime_tm.tm_mon = 0;
320                         duetime_tm.tm_year += 1;
321                 }
322                 due_time = mktime(&duetime_tm);
323         }
324
325         return due_time;
326
327 }
328
329 static time_t __alarm_next_duetime_weekly(__alarm_info_t *__alarm_info)
330 {
331         time_t due_time = 0;
332         time_t current_time = 0;
333         struct tm duetime_tm;
334         struct tm tmp_tm;
335         int wday;
336         int current_dst = 0;
337         struct tm before_tm;
338         struct tm after_tm;
339
340         alarm_info_t *alarm_info = &__alarm_info->alarm_info;
341         alarm_date_t *start = &alarm_info->start;
342         alarm_mode_t *mode = &alarm_info->mode;
343
344         tzset();
345         time(&current_time);
346         localtime_r(&current_time, &duetime_tm);
347         wday = duetime_tm.tm_wday;
348         duetime_tm.tm_hour = start->hour;
349         duetime_tm.tm_min = start->min;
350         duetime_tm.tm_sec = start->sec;
351         current_dst = duetime_tm.tm_isdst;
352
353         duetime_tm.tm_isdst = -1;
354
355         if (__alarm_info->start != 0) {
356                 if (__alarm_info->start >= current_time)        {
357                         duetime_tm.tm_year = start->year - 1900;
358                         duetime_tm.tm_mon = start->month - 1;
359                         duetime_tm.tm_mday = start->day;
360                 }
361         }
362
363         due_time = mktime(&duetime_tm);
364         localtime_r(&due_time, &tmp_tm);
365         ALARM_MGR_EXCEPTION_PRINT("%d:%d:%d. duetime = %d, isdst = %d", tmp_tm.tm_hour, tmp_tm.tm_min, tmp_tm.tm_sec, due_time, tmp_tm.tm_isdst);
366
367         if (due_time <= current_time) {
368                 ALARM_MGR_EXCEPTION_PRINT("duetime is less than or equal to current time. current_dst = %d", current_dst);
369                 duetime_tm.tm_isdst = 0;
370
371                 due_time = mktime(&duetime_tm);
372                 localtime_r(&due_time, &tmp_tm);
373
374                 SECURE_LOGD("%d:%d:%d. duetime = %d", tmp_tm.tm_hour, tmp_tm.tm_min, tmp_tm.tm_sec, due_time);
375                 if (tmp_tm.tm_hour != start->hour || tmp_tm.tm_min != start->min || tmp_tm.tm_sec != start->sec ) {
376                         duetime_tm.tm_hour = start->hour;
377                         duetime_tm.tm_min = start->min;
378                         duetime_tm.tm_sec = start->sec;
379                         duetime_tm.tm_isdst = -1;
380                         due_time = mktime(&duetime_tm);
381                         ALARM_MGR_EXCEPTION_PRINT("due_time = %d",due_time);
382                 }
383         }
384         else {
385                 if (current_dst == 1 && tmp_tm.tm_isdst == 1 && tmp_tm.tm_hour == start->hour + 1) {
386                         // When the calculated duetime is forwarded 1hour due to DST, Adds 23hours.
387                         due_time += 60 * 60 * 23;
388                         localtime_r(&due_time, &duetime_tm);
389                         ALARM_MGR_EXCEPTION_PRINT("due_time = %d",due_time);
390                 }
391         }
392
393         // Gets the dst before calculating the duedate as interval
394         localtime_r(&due_time, &before_tm);
395         SECURE_LOGD("before_dst = %d", before_tm.tm_isdst);
396
397         wday = duetime_tm.tm_wday;
398
399         ALARM_MGR_EXCEPTION_PRINT("current_time(%d) due_time(%d)", current_time, due_time);
400
401         /* CQ defect(72810) : only one time alarm function is not working
402            under all recurrence_disabled. */
403         if (due_time > current_time && mode->u_interval.day_of_week == 0) {
404                 return due_time;
405         }
406
407         if (current_time >= due_time || !(mode->u_interval.day_of_week & 1 << wday)) {
408                 int day = wday + 1;
409                 int interval = 1;
410                 /*this week */
411
412                 if (day == 7) {
413                         day = 0;
414                 }
415
416                 while (!(mode->u_interval.day_of_week & 1 << day) && interval < 8) {
417                         day += 1;
418                         interval += 1;
419
420                         if (day == 7) {
421                                 day = 0;
422                         }
423                 }
424
425                 ALARM_MGR_LOG_PRINT("interval : %d\n", interval);
426                 due_time += 60 * 60 * 24 * interval;
427         }
428
429         // Gets the dst after calculating the duedate as interval
430         localtime_r(&due_time, &after_tm);
431         SECURE_LOGD("after_dst = %d", after_tm.tm_isdst);
432
433         // Revise the duetime as difference in tm_isdst
434         if (before_tm.tm_isdst == 1 && after_tm.tm_isdst == 0) {
435                 due_time += 60 * 60;    // Add an hour
436         } else if (before_tm.tm_isdst == 0 && after_tm.tm_isdst == 1) {
437                 due_time -= 60 * 60;    // Subtract an hour
438         }
439
440         ALARM_MGR_EXCEPTION_PRINT("Final due_time = %d", due_time);
441         return due_time;
442 }
443
444 time_t _alarm_next_duetime(__alarm_info_t *__alarm_info)
445 {
446         int is_dst=0;
447         time_t current_time = 0;
448         time_t due_time = 0;
449         struct tm *cur_tm = NULL ;
450         struct tm *due_tm = NULL ;
451
452         alarm_info_t *alarm_info = &__alarm_info->alarm_info;
453         alarm_mode_t *mode = &alarm_info->mode;
454
455         time(&current_time);
456         cur_tm = localtime(&current_time);
457         if (cur_tm && cur_tm->tm_isdst > 0)
458                 is_dst = 1;
459
460         ALARM_MGR_LOG_PRINT("mode->repeat is %d\n", mode->repeat);
461
462         if (mode->repeat == ALARM_REPEAT_MODE_ONCE) {
463                 due_time = __alarm_next_duetime_once(__alarm_info);
464         } else if (mode->repeat == ALARM_REPEAT_MODE_REPEAT) {
465                 due_time = __alarm_next_duetime_repeat(__alarm_info);
466         } else if (mode->repeat == ALARM_REPEAT_MODE_ANNUALLY) {
467                 due_time = __alarm_next_duetime_annually(__alarm_info);
468         } else if (mode->repeat == ALARM_REPEAT_MODE_MONTHLY) {
469                 due_time = __alarm_next_duetime_monthly(__alarm_info);
470         } else if (mode->repeat == ALARM_REPEAT_MODE_WEEKLY) {
471                 due_time = __alarm_next_duetime_weekly(__alarm_info);
472         } else {
473                 ALARM_MGR_EXCEPTION_PRINT("repeat mode(%d) is wrong\n",
474                                           mode->repeat);
475                 return 0;
476         }
477
478         if (mode->repeat != ALARM_REPEAT_MODE_WEEKLY && mode->repeat != ALARM_REPEAT_MODE_ONCE) {
479                 due_tm = localtime(&due_time);
480                 if (is_dst==0 && due_tm && due_tm->tm_isdst==1){
481                                 ALARM_MGR_LOG_PRINT("DST alarm found, enable\n");
482                                 due_tm->tm_hour = due_tm->tm_hour - DST_TIME_DIFF;
483                 } else if (is_dst==1 && due_tm && due_tm->tm_isdst==0){
484                                 ALARM_MGR_LOG_PRINT("DST alarm found. disable\n");
485                                 due_tm->tm_hour = due_tm->tm_hour + DST_TIME_DIFF;
486                 }
487
488                 if (due_tm)
489                         due_time = mktime(due_tm);
490         }
491
492          ALARM_MGR_EXCEPTION_PRINT("alarm_id: %d, next duetime: %d", __alarm_info->alarm_id, due_time);
493
494         if (__alarm_info->end != 0 && __alarm_info->end < due_time) {
495                 ALARM_MGR_LOG_PRINT("due time > end time");
496                 __alarm_info->due_time = 0;
497                 return 0;
498         }
499         __alarm_info->due_time = due_time;
500
501         return due_time;
502 }
503
504 static bool __find_next_alarm_to_be_scheduled(time_t *min_due_time)
505 {
506         time_t current_time;
507         time_t min_time = -1;
508         time_t due_time;
509         GSList *iter = NULL;
510         __alarm_info_t *entry = NULL;
511
512         time(&current_time);
513
514         for (iter = alarm_context.alarms; iter != NULL;
515              iter = g_slist_next(iter)) {
516                 entry = iter->data;
517                 due_time = entry->due_time;
518
519                 double interval = 0;
520
521                 SECURE_LOGD("alarm[%d] with duetime(%u) at current(%u) pid: (%d)\n",
522                         entry->alarm_id, due_time, current_time, entry->pid);
523                 if (due_time == 0)      /*0 means this alarm has been disabled*/ {
524                         continue;
525                 }
526
527                 interval = difftime(due_time, current_time);
528
529                 if (interval <= 0)      /*2008.08.06 when the alarm expires, it may makes an error.*/ {
530                         ALARM_MGR_EXCEPTION_PRINT("The duetime of alarm(%d) is OVER.", entry->alarm_id);
531                         continue;
532                 }
533
534                 interval = difftime(due_time, min_time);
535
536                 if ((interval < 0) || min_time == -1) {
537                         min_time = due_time;
538                 }
539
540         }
541
542         *min_due_time = min_time;
543         return true;
544 }
545
546 bool _alarm_schedule()
547 {
548         time_t due_time = 0;
549         time_t min_time = 0;
550         GSList *iter = NULL;
551         __alarm_info_t *entry = NULL;
552
553         __find_next_alarm_to_be_scheduled(&min_time);
554
555         if (min_time == -1) {
556                 ALARM_MGR_LOG_PRINT("[alarm-server][schedule]: There is no alarm to be scheduled.");
557         } else {
558                 for (iter = alarm_context.alarms; iter != NULL; iter = g_slist_next(iter)) {
559                         entry = iter->data;
560                         due_time = entry->due_time;
561
562                         if (due_time == min_time) {
563                                 _add_to_scheduled_alarm_list(entry);
564                         }
565                 }
566                 _alarm_set_timer(&alarm_context, alarm_context.timer, min_time);
567         }
568
569         return true;
570 }