Initialize Tizen 2.3
[framework/appfw/alarm-manager.git] / alarm-manager-sync-scheduler.c
1
2 // Power savings by syncronizing start points of periodic network activitiy
3 // frequently triggered by applications during sleep and/or active state.
4 //
5
6 #include <math.h>
7 #include <aul.h>
8 #include"alarm-internal.h"
9
10 static bool g_white_list = true;
11 static bool g_white_list_plus_auto_add = false;
12 static bool g_app_sync_on = false;
13
14 #define SYNC_COPRIME_VALUE      60 // [s] 1 min
15 #define SYNC_MIN_VALUE          (5*60)  // [s] 5 min
16 #define SYNC_MAX_VALUE  (4*60*60)  // [s] 4 hr
17 #define INTERVAL_HOUR   (60*60)
18 #define INTERVAL_HALF_DAY       (12*60*60)
19
20 #define MAX_INT_VALUE   2147483647
21 #define APP_SYNC_LOG    1
22
23 #define CSC_BUFFER_SIZE 256
24
25 // [ms] global unit interval; Greatest Common Divisor of RepeatIntervals interested
26 static int g_interval_gcd = SYNC_MAX_VALUE;
27
28 static GSList* g_adjustable_repeating_alarms = NULL;
29 static GSList* g_target_packages = NULL;
30 //static GSList* g_csc_packages = NULL;
31
32 static int __gcd(int first_value, int second_value)
33 {
34         if (second_value == 0)
35                 return first_value;
36         else
37                 return __gcd(second_value, first_value % second_value);
38 }
39
40 void __convert_time_to_alarm_date_t(time_t time, alarm_date_t* alarm_date)
41 {
42         struct tm time_tm;
43         localtime_r(&time, &time_tm);
44
45         alarm_date->year = time_tm.tm_year + 1900;
46         alarm_date->month = time_tm.tm_mon + 1;
47         alarm_date->day = time_tm.tm_mday;
48         alarm_date->hour = time_tm.tm_hour;
49         alarm_date->min = time_tm.tm_min;
50         alarm_date->sec = time_tm.tm_sec;
51 }
52
53 void __convert_alarm_date_t_to_time(alarm_date_t* alarm_date, time_t* time)
54 {
55         struct tm alarm_tm = {0, };
56
57         alarm_tm.tm_year = alarm_date->year - 1900;
58         alarm_tm.tm_mon = alarm_date->month - 1;
59         alarm_tm.tm_mday = alarm_date->day;
60
61         alarm_tm.tm_hour = alarm_date->hour;
62         alarm_tm.tm_min = alarm_date->min;
63         alarm_tm.tm_sec = alarm_date->sec;
64
65         *time = mktime(&alarm_tm);
66 }
67
68 static int __compare_func(const char* a, const char* b)
69 {
70         if (0 == g_strcmp0(a, b)) {
71                 return 0;
72         }
73
74         return 1;
75 }
76
77 static bool __sync_scheduler_look_for_non_adjustable_alarm(GSList* alarmList, __alarm_info_t* __alarm_info)
78 {
79 // ±âÁ¸ alarm ¸®½ºÆ®¿¡¼­ µ¿ÀÏÇÑ alarm ÀÇ  original alarm ½Ã°£ÀÌ ÇöÀç ¿äû½Ã°£°ú °°À¸¸é return ture;
80
81         GSList *gs_iter = NULL;
82         __alarm_info_t *entry = NULL;
83
84         for (gs_iter = alarmList; gs_iter != NULL;
85                  gs_iter = g_slist_next(gs_iter)) {
86
87                 entry = gs_iter->data;
88
89                 if (__alarm_info->start == entry->start) {
90                         if (!g_strcmp0(g_quark_to_string(__alarm_info->quark_app_unique_name), g_quark_to_string(entry->quark_app_unique_name)) &&
91                                 !g_strcmp0(g_quark_to_string(__alarm_info->quark_app_service_name), g_quark_to_string(entry->quark_app_service_name)) &&
92                                 !g_strcmp0(g_quark_to_string(__alarm_info->quark_dst_service_name), g_quark_to_string(entry->quark_dst_service_name)) &&
93                                 !g_strcmp0(g_quark_to_string(__alarm_info->quark_bundle), g_quark_to_string(entry->quark_bundle))) {
94                                 ALARM_MGR_LOG_PRINT("This is non adjustable alarm");
95                                 return true;
96                         }
97                 }
98         }
99
100         return false;
101 }
102
103 static int __sync_scheduler_calculate_gcd_of_repeat_intervals(int interval_old, int interval_new)
104 {
105         int new_interval_gcd = interval_old;
106         int temp_interval_gcd = __gcd(interval_old, interval_new);
107
108         if (temp_interval_gcd > SYNC_COPRIME_VALUE) {
109                 if ((temp_interval_gcd % SYNC_MIN_VALUE) == 0) {
110                         new_interval_gcd = temp_interval_gcd;
111                 }
112         }
113
114         return new_interval_gcd;
115 }
116
117 // list Áß¿¡¼­ interval ÀÌ °°Àº °¡Àå  ÃÖ½ÅÀÇ alarm À» ¸®ÅÏ , °°Àº °ÍÀÌ ¾øÀ¸¸é ¹è¼ö interval À» °¡Áø alarm ÀÌ ¸®ÅÏ
118 static __alarm_info_t* __sync_scheduler_time_to_next_repeating_alarm(int interval)
119 {
120         int next_alarm = MAX_INT_VALUE;
121         int next_alarm_with_same_interval = MAX_INT_VALUE;
122         __alarm_info_t* alarm_result = NULL;
123         __alarm_info_t* alarm_result_with_same_interval = NULL;
124         GSList *gs_iter = NULL;
125         __alarm_info_t *entry = NULL;
126
127         bool is_int_same_as_gcd = (interval == g_interval_gcd);
128         time_t now_rtc;
129         time(&now_rtc);
130
131         for (gs_iter = g_adjustable_repeating_alarms; gs_iter != NULL;
132                  gs_iter = g_slist_next(gs_iter)) {
133                 time_t when = 0;
134                 entry = gs_iter->data;
135
136                 __convert_alarm_date_t_to_time(&entry->alarm_info.start, &when);
137
138                 if (now_rtc < (when + g_interval_gcd)) {        // Accept ealier time of one GCD interval
139                         // Look for the alarm with same interval as GCD
140                         if (is_int_same_as_gcd) {
141                                 if (when < next_alarm) {
142                                         next_alarm = when;
143                                         alarm_result = entry;
144                                 }
145                         }
146                         // Look for the alarm with same interval or multiples of interval
147                         else {
148                                 if (entry->alarm_info.mode.u_interval.interval != 0) {
149                                         if (entry->alarm_info.mode.u_interval.interval == interval) {
150                                                 if (when < next_alarm_with_same_interval) {
151                                                         next_alarm_with_same_interval = when;
152                                                         alarm_result_with_same_interval = entry;
153                                                 }
154                                         }
155                                         else if (((entry->alarm_info.mode.u_interval.interval > interval) && (entry->alarm_info.mode.u_interval.interval % interval == 0)) ||
156                                                          ((entry->alarm_info.mode.u_interval.interval < interval) && (interval % entry->alarm_info.mode.u_interval.interval == 0))) {
157                                                 if (when < next_alarm) {
158                                                         next_alarm = when;
159                                                         alarm_result = entry;
160                                                 }
161                                         }
162                                 }
163                         }
164                 }
165                 else {
166                         // Old alarms eventually left must be removed.
167                         if ((when + INTERVAL_HALF_DAY) < now_rtc) {
168                                 g_adjustable_repeating_alarms =
169                                         g_slist_remove(g_adjustable_repeating_alarms, gs_iter->data);
170                         }
171                 }
172
173         }
174
175
176         // Next alarm with same interval value goes first.
177         if (alarm_result_with_same_interval != NULL)
178                 alarm_result = alarm_result_with_same_interval;
179
180         return alarm_result;
181
182 }
183
184 //inputDistance = gcd or alarm's repeated interval
185 static void __sync_scheduler_adjust_alarm_time(__alarm_info_t* __alarm_info, int input_distance)
186 {
187         int next_alarm_when = MAX_INT_VALUE;
188         int distance = input_distance;
189         time_t new_time;
190
191         // Retreve the nearest alarm with same RepeatInterval or multiples of RepeatInterval
192         if (__alarm_info->alarm_info.mode.u_interval.interval != g_interval_gcd) {
193                 __alarm_info_t* a = __sync_scheduler_time_to_next_repeating_alarm(__alarm_info->alarm_info.mode.u_interval.interval);
194                 if (a != NULL) {
195                         __convert_alarm_date_t_to_time(&a->alarm_info.start, (time_t*)&next_alarm_when);
196                         // Same RepeatInterval or co-prime RepeatInterval with GCD
197                         if ((a->alarm_info.mode.u_interval.interval == __alarm_info->alarm_info.mode.u_interval.interval) ||
198                                 (__alarm_info->alarm_info.mode.u_interval.interval % g_interval_gcd != 0)) {
199                                 distance = __alarm_info->alarm_info.mode.u_interval.interval;
200                         }
201                         // Multiples of RepeatInterval
202                         else {
203                                 distance = __gcd(__alarm_info->alarm_info.mode.u_interval.interval, a->alarm_info.mode.u_interval.interval);
204                         }
205                 }
206         }
207         // Retreve the nearest alarm using GCD based RepeatInterval
208         if (next_alarm_when == MAX_INT_VALUE) {
209                 __alarm_info_t* a = __sync_scheduler_time_to_next_repeating_alarm(g_interval_gcd);
210                 if (a != NULL) {
211                         __convert_alarm_date_t_to_time(&a->alarm_info.start, (time_t*)&next_alarm_when);
212                 }
213         }
214
215         if (next_alarm_when != MAX_INT_VALUE) {
216                 ALARM_MGR_LOG_PRINT("next: %d", next_alarm_when);
217
218                 // If the requested alarm is after the very next alarm to be triggered,
219                 // place it somewhere aligned with the point that is one of multiples of
220                 // requested distance and nearest to the very next alarm.
221                 if (next_alarm_when <= __alarm_info->start) {
222                         int count = (__alarm_info->start - next_alarm_when) / distance;
223                         new_time = next_alarm_when + distance * count;
224                 }
225                 // If the requested alarm is before the very next alarm to be triggered,
226                 // find the earlier aligned point around the requested alarm
227                 else {
228                         int count = (next_alarm_when - __alarm_info->start) / distance;
229                         count++;  // move to one more earlier point
230                         new_time = next_alarm_when - distance * count;
231                 }
232
233                 __convert_time_to_alarm_date_t(new_time, &__alarm_info->alarm_info.start);
234                 ALARM_MGR_EXCEPTION_PRINT("AppSync original time : %s", ctime(&__alarm_info->start));
235                 ALARM_MGR_EXCEPTION_PRINT("AppSync change time : %s", ctime(&new_time));
236         }
237         else {
238                 ALARM_MGR_LOG_PRINT("next: MAX_INT_VALUE");
239         }
240 }
241
242 //csc ¿¡ ÀÇÇØ targetPackageList °¡ ±¸¼ºµÈ »óÅ¿¡¼­ white list ¿¡ ÇØ´ç package Á¸Àç È®ÀÎ
243 // target_package_list ¿¡¼­ appid °¡ Á¸ÀçÇϸé return true;
244 static bool __sync_scheduler_look_for_target_package(GSList* target_package_list, char* appid)
245 {
246         GSList* list = g_slist_find_custom(target_package_list, appid, (GCompareFunc)__compare_func);
247
248         if (appid[0] == 0) {
249                 return false;
250         }
251
252         if (NULL == list) {
253                 SECURE_LOGD("%s is NOT found in the app sync white list", appid);
254                 return false;
255         } else {
256                 SECURE_LOGD("%s is found in the app sync white list", appid);
257                 return true;
258         }
259 }
260
261 bool _sync_scheduler_app_sync_on()
262 {
263         return g_app_sync_on;
264 }
265
266 // CSC ¿Í Account ·Î ºÎÅÍ g_target_packages ±¸¼º
267 void _sync_scheduler_init()
268 {
269         int ret = 0;
270
271         char cscAppData[CSC_BUFFER_SIZE] = {0,};
272         char** cscAppSyncList = NULL;
273
274         g_target_packages = g_slist_alloc();
275
276         // Check the AppSync feature frm CSC
277 #if 0
278         if (csc_feature_get_bool(CSC_FEATURE_DEF_BOOL_FRAMEWORK_APP_SYNC_DISABLE) == CSC_FEATURE_BOOL_FALSE) {
279 #else
280         if (true) {
281 #endif
282                 g_app_sync_on = true;
283
284                 // TODO: Get app sync data from csc file (cscAppData)
285                 //ret = csc_feature_get_str(CSC_FEATURE_DEF_STR_ALARM_MANAGER_APP_SYNC, cscAppData, CSC_BUFFER_SIZE);
286
287                 cscAppSyncList = g_strsplit(cscAppData, ",", 0);
288
289                 // Check the whitelist mode
290                 if (0 == g_strcmp0(*cscAppSyncList, "whitelist")) {
291
292                         // Load Whitelist of target packages
293                         for (cscAppSyncList++; NULL != *cscAppSyncList; cscAppSyncList++) {
294                                 ALARM_MGR_LOG_PRINT("CSC data, %s", *cscAppSyncList);
295                                 g_target_packages = g_slist_append(g_target_packages, g_strdup(*cscAppSyncList));
296                         }
297                 } else if (0 == g_strcmp0(*cscAppSyncList, "blacklist")) {
298
299                         // Disable Whitelist depending on Blacklist selection.
300                         g_white_list = false;
301                         g_white_list_plus_auto_add = false;
302
303                         // Load Blacklist of target packages
304                         for (cscAppSyncList++; NULL != *cscAppSyncList; cscAppSyncList++) {
305                                 ALARM_MGR_LOG_PRINT("CSC data, %s", *cscAppSyncList);
306                                 g_target_packages = g_slist_append(g_target_packages, g_strdup(*cscAppSyncList));
307                         }
308                 } else { // default lists
309                         //g_target_packages = g_slist_append(g_target_packages, "com.samsung.helloworld");
310                 }
311
312                 // Free the csc list
313                 g_strfreev(cscAppSyncList);
314
315                 if (APP_SYNC_LOG) {
316                         GSList *gs_iter = NULL;
317                         int i = 0;
318                         for (gs_iter = g_target_packages; gs_iter != NULL;
319                                  gs_iter = g_slist_next(gs_iter), i++) {
320                                 SECURE_LOGD("target package [%d] : %s",
321                                         i, gs_iter->data);
322                         }
323                 }
324         } else {
325                 ALARM_MGR_EXCEPTION_PRINT("App sync is disabled", *cscAppSyncList);
326         }
327 }
328
329 void _sync_scheduler_repeating_alarms(__alarm_info_t* __alarm_info)
330 {
331         int ret;
332
333         // true ¸é ±âÁ¸°ú µ¿ÀÏÇÑ alarm ¿äûÀ̹ǷÎ, ¹«½Ã
334         bool is_non_adjustable_alarm = __sync_scheduler_look_for_non_adjustable_alarm(
335                                                 g_adjustable_repeating_alarms, __alarm_info);
336
337         // Remove this alarm if already scheduled.
338         // replace pre existed alarm and remove from the appsync list
339         //removeLocked(alarm.operation);
340
341         char appid[255] = {0,};
342         ret = aul_app_get_appid_bypid(__alarm_info->pid, appid, sizeof(appid));
343         if (ret != AUL_R_OK)
344                 ALARM_MGR_LOG_PRINT("Cannot get the appid");
345
346         if ( (is_non_adjustable_alarm == false) &&
347                 (__sync_scheduler_look_for_target_package(g_target_packages, appid) == g_white_list)) {
348
349                 if ((__alarm_info->alarm_info.mode.repeat == ALARM_REPEAT_MODE_REPEAT) &&
350                         (__alarm_info->alarm_info.mode.u_interval.interval >= SYNC_MIN_VALUE) &&
351                         (__alarm_info->alarm_info.mode.u_interval.interval <= SYNC_MAX_VALUE)) {
352                         g_interval_gcd = __sync_scheduler_calculate_gcd_of_repeat_intervals(g_interval_gcd,
353                                 __alarm_info->alarm_info.mode.u_interval.interval);
354
355                         // If new RepeatInterval belongs to multiples of gIntervalGcd,
356                         // the alarm will start at the nearest scheduling point
357                         // around the requested alarm time. The scheduling points are
358                         // calculated on the unit of gIntervalGcd from the next repeating alarm
359                         // to be triggered.
360                         if (__alarm_info->alarm_info.mode.u_interval.interval % g_interval_gcd == 0) {
361                                 __sync_scheduler_adjust_alarm_time(__alarm_info, g_interval_gcd);
362                                 g_adjustable_repeating_alarms = g_slist_append(g_adjustable_repeating_alarms, __alarm_info);
363                         }
364                         // If not, the alarm will start at the nearest scheduling point around
365                         // the requested alarm time. The scheduling points are calculated on
366                         // the unit of alarm.repeatInterval from the next repeating alarm to be triggered.
367                         else {
368                                 __sync_scheduler_adjust_alarm_time(__alarm_info, __alarm_info->alarm_info.mode.u_interval.interval);
369                         }
370                 }
371                 else if (__alarm_info->alarm_info.mode.repeat == ALARM_REPEAT_MODE_ONCE) {
372
373                         // The package of the alarms registered to account manager could be adjusted
374                         time_t now;
375                         time(&now);
376                         int distance_to_alarm = __alarm_info->start - now;
377                         int sync_tolerance_value = (distance_to_alarm >= (INTERVAL_HOUR - SYNC_MIN_VALUE)) ? 60 : 10; // [s] 30s or 5s
378                         int distance_to_alarm_rounded = (int) round((double)distance_to_alarm/(double)sync_tolerance_value) * sync_tolerance_value;
379
380                         // Optimize code for com.android.email is omitted.
381                         // Optimize code for com.google.android.gsf is omitted.
382
383                         // Adjust the alarm that occurs periodically in the range
384                         // between SYNC_MIN_VALUE and SYNC_MAX_VALUE
385                         if ((distance_to_alarm_rounded <= SYNC_MAX_VALUE) &&
386                                 (distance_to_alarm_rounded >= SYNC_MIN_VALUE) &&
387                                 (distance_to_alarm_rounded % SYNC_MIN_VALUE == 0)) {
388                                 __alarm_info_t* new_alarm;
389                                 g_interval_gcd = __sync_scheduler_calculate_gcd_of_repeat_intervals(g_interval_gcd, distance_to_alarm_rounded);
390                                 new_alarm = malloc(sizeof(__alarm_info_t));
391
392                                 memcpy(new_alarm, __alarm_info, sizeof(__alarm_info_t));
393                                 new_alarm->alarm_info.mode.u_interval.interval = distance_to_alarm_rounded;
394                                 __sync_scheduler_adjust_alarm_time(new_alarm, g_interval_gcd);
395                                 g_adjustable_repeating_alarms = g_slist_append(g_adjustable_repeating_alarms, new_alarm);
396                                 memcpy(&(__alarm_info->alarm_info.start), &(new_alarm->alarm_info.start), sizeof(alarm_date_t));
397                         }
398                 }
399                 if (APP_SYNC_LOG) {
400                         GSList *gs_iter = NULL;
401                         __alarm_info_t *entry = NULL;
402                         int i = 0;
403                         time_t due_time = 0;
404                         struct tm duetime_tm;
405                         alarm_date_t *start;
406
407                         for (gs_iter = g_adjustable_repeating_alarms; gs_iter != NULL;
408                                  gs_iter = g_slist_next(gs_iter), i++) {
409
410                                 entry = gs_iter->data;
411                                 start = &entry->alarm_info.start;
412
413                                 duetime_tm.tm_hour = start->hour;
414                                 duetime_tm.tm_min = start->min;
415                                 duetime_tm.tm_sec = start->sec;
416
417                                 duetime_tm.tm_year = start->year - 1900;
418                                 duetime_tm.tm_mon = start->month - 1;
419                                 duetime_tm.tm_mday = start->day;
420
421                                 due_time = mktime(&duetime_tm);
422
423                                 ALARM_MGR_LOG_PRINT("List[%d] : Interval %d Start %s",
424                                         i, entry->alarm_info.mode.u_interval.interval, ctime(&due_time));
425                         }
426                 }
427                 ALARM_MGR_LOG_PRINT("Interval GCD : %d", g_interval_gcd);
428         }
429 }
430
431 void _sync_scheduler_remove_repeating_alarm(alarm_id_t alarm_id)
432 {
433 // __alarm_delete ÇÔ¼ö¿¡¼­ È£Ãâ
434 //g_adjustable_repeating_alarms ¿¡¼­ alarm »èÁ¦
435         GSList *gs_iter = NULL;
436         __alarm_info_t *entry = NULL;
437
438         for (gs_iter = g_adjustable_repeating_alarms; gs_iter != NULL;
439                  gs_iter = g_slist_next(gs_iter)) {
440                 entry = gs_iter->data;
441
442                 if (entry->alarm_id == alarm_id) {
443                         g_adjustable_repeating_alarms =
444                                 g_slist_remove(g_adjustable_repeating_alarms, gs_iter->data);
445                 }
446         }
447 }
448