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