tizen 2.3.1 release
[kernel/api/system-resource.git] / src / network / datausage-quota-processing.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 /*
21  * @file datausage-quota-processing.c
22  *
23  * @desc Quota processing implementation.
24  *      This implementation updates used quota table and determine
25  *      moment of time for blocking.
26  *
27  * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
28  *
29  */
30
31 #include <glib.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sqlite3.h>
35 #include <inttypes.h>
36
37 #include "database.h"
38 #include "data_usage.h"
39 #include "macro.h"
40 #include "protocol-info.h"
41 #include "resourced.h"
42 #include "notification.h"
43 #include "storage.h"
44 #include "trace.h"
45 #include "roaming.h"
46 #include "datausage-restriction.h"
47 #include "datausage-vconf-common.h"
48
49 static GTree *quotas;
50 static sqlite3_stmt *select_stmt;
51 static sqlite3_stmt *insert_stmt;
52 static sqlite3_stmt *clear_effective_stmt;
53
54 static const char select_query[] = "SELECT qt.binpath, qt.sent_quota, qt.rcv_quota, "\
55         "qt.snd_warning_threshold, qt.rcv_warning_threshold, "\
56         "sent_used_quota, rcv_used_quota, qt.start_time AS quota_start_time, "\
57         "qt.time_period AS quota_period, efq.start_time AS effective_start, "\
58         "efq.finish_time AS effective_finish, qt.iftype AS iftype, " \
59         "qt.roaming, "\
60         "efq.state, "\
61         "qt.ROWID "\
62         "FROM quotas AS qt "\
63         "LEFT OUTER JOIN effective_quotas AS efq ON (qt.binpath = efq.binpath "\
64         "AND qt.iftype = efq.iftype AND qt.roaming = efq.roaming) "\
65         "GROUP BY qt.binpath, qt.iftype, qt.sent_quota, qt.rcv_quota, " \
66         "qt.roaming";
67
68 static const char insert_query[] = "REPLACE INTO effective_quotas " \
69         "(binpath, sent_used_quota, rcv_used_quota, " \
70         "start_time, finish_time, iftype, roaming, state) " \
71         " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
72
73 static const char clear_effective_quota_query[] = "DELETE FROM effective_quotas " \
74         " WHERE binpath = ? AND iftype = ? AND roaming = ?";
75
76 enum resourced_quota_state {
77         RESOURCED_QUOTA_UNKNOWN,        /**< undefined/initial state */
78         RESOURCED_QUOTA_APPLIED,        /**< enabled/applied state */
79         RESOURCED_QUOTA_REVERTED,       /**< disabled/reverted state */
80 };
81
82 struct quota {
83         int quota_id;
84         int64_t send_quota;
85         int64_t rcv_quota;
86         int64_t sent_used_quota;
87         int64_t rcv_used_quota;
88         int snd_warning_threshold;
89         int rcv_warning_threshold;
90         int start_time;
91         int time_period;
92         int real_start;
93         int real_finish;
94         enum resourced_quota_state state;
95 };
96
97 struct quota_key {
98         const char *app_id;
99         resourced_iface_type iftype;
100         resourced_roaming_type roaming;
101 };
102
103 typedef enum {
104         DROP_UNDEF = 0,
105         DROP_NEED = 1,
106         DROP_NO_NEED = 2
107 } drop_decision;
108
109 static void obtain_and_keep_quotas(sqlite3_stmt *query)
110 {
111         int rc = 0;
112         struct quota *value = 0;
113         struct quota_key *key = 0;
114
115         if (!query) {
116                 _D("Can not update quotas: empty query");
117                 return;
118         }
119
120         do {
121                 rc = sqlite3_step(query);
122
123                 if (rc == SQLITE_ERROR) {
124                         _E("Error updating quotas %s", sqlite3_errmsg(resourced_get_database()));
125                         return;
126                 } else if (rc == SQLITE_ROW) {
127                         value = g_new0(struct quota, 1);
128                         if (!value) {
129                                 _E("Can't allocate value for quota");
130                                 return;
131                         }
132
133                         key = g_new0(struct quota_key, 1);
134                         if (!key) {
135                                 _E("Can't allocate key for quota");
136                                 goto free_value;
137                         }
138
139                         key->app_id = strdup((char *)sqlite3_column_text(
140                                 query, 0));
141                         key->iftype = sqlite3_column_int(
142                                 query, 11);
143                         key->roaming = sqlite3_column_int(
144                                 query, 12);
145
146                         value->send_quota = sqlite3_column_int64(
147                                 query, 1);
148                         value->rcv_quota = sqlite3_column_int64(
149                                 query, 2);
150                         value->snd_warning_threshold = sqlite3_column_int64(
151                                 query, 3);
152                         value->rcv_warning_threshold = sqlite3_column_int64(
153                                 query, 4);
154                         value->sent_used_quota = sqlite3_column_int64(
155                                 query, 5);
156                         value->rcv_used_quota = sqlite3_column_int64(
157                                 query, 6);
158                         value->start_time = sqlite3_column_int64(
159                                 query, 7);
160                         value->time_period = sqlite3_column_int64(
161                                 query, 8);
162                         value->real_start = sqlite3_column_int64(
163                                 query, 9);
164                         value->real_finish = sqlite3_column_int64(
165                                 query, 10);
166                         value->state = sqlite3_column_int64(
167                                 query, 13);
168                         value->quota_id = sqlite3_column_int(
169                                 query, 14);
170
171                         g_tree_insert(quotas, key, value);
172                 }
173         } while (rc == SQLITE_ROW);
174
175         return;
176 free_value:
177         if (value)
178                 g_free(value);
179 }
180
181 static gint compare_quota_key(gconstpointer a, gconstpointer b,
182         gpointer UNUSED user_data)
183 {
184         const struct quota_key *key1 = a;
185         const struct quota_key *key2 = b;
186         /* the first part of the key is equal compare second */
187         return strcmp(key1->app_id, key2->app_id) ||
188                 key1->iftype - key2->iftype ||
189                 key1->roaming - key2->roaming;
190 }
191
192 #define quota_key_destructor g_free
193 #define quota_destructor g_free
194
195 static void _clear_effective_quota(const char *app_id,
196         const resourced_iface_type iftype,
197         const resourced_roaming_type roaming)
198 {
199         if (sqlite3_bind_text(clear_effective_stmt, 1, app_id, -1,
200                           SQLITE_TRANSIENT) != SQLITE_OK) {
201                 _SE("Can not bind app_id:%s for preparing statement:%s",
202                         app_id, sqlite3_errmsg(resourced_get_database()));
203                 return;
204         }
205
206         if (sqlite3_bind_int(clear_effective_stmt, 2, iftype)
207                 != SQLITE_OK) {
208                 _E("Can not bind iftype:%d for preparing statement:%s",
209                         iftype, sqlite3_errmsg(resourced_get_database()));
210                 return;
211         }
212
213         if (sqlite3_bind_int(clear_effective_stmt, 3, roaming)
214                 != SQLITE_OK) {
215                 _E("Can not bind roaming:%d for preparing statement:%s",
216                         roaming, sqlite3_errmsg(resourced_get_database()));
217                 return;
218         }
219
220         if (sqlite3_step(clear_effective_stmt) != SQLITE_DONE)
221                 _E("Failed to clear effective quotas %s",
222                 sqlite3_errmsg(resourced_get_database()));
223         sqlite3_reset(clear_effective_stmt);
224 }
225
226 static inline int _is_period_devisible(const int time_period,
227                                      data_usage_quota_period_t quota_period)
228 {
229         return time_period > quota_period &&
230                 time_period % RESOURCED_PERIOD_MONTH == 0;
231 }
232
233 /**
234  * @desc Define period base on stored in data base time interval
235  * @return time period
236  */
237 static data_usage_quota_period_t _define_period(const int time_period, int *quantity)
238 {
239         if (quantity == 0)
240                 return RESOURCED_PERIOD_UNDEF;
241
242         if (_is_period_devisible(time_period, RESOURCED_PERIOD_MONTH)) {
243                 *quantity = time_period / RESOURCED_PERIOD_MONTH;
244                 return RESOURCED_PERIOD_MONTH;
245         }
246
247         if (_is_period_devisible(time_period, RESOURCED_PERIOD_MONTH)) {
248                 *quantity = time_period / RESOURCED_PERIOD_MONTH;
249                 return RESOURCED_PERIOD_MONTH;
250         }
251
252         if (_is_period_devisible(time_period, RESOURCED_PERIOD_WEEK)) {
253                 *quantity = time_period / RESOURCED_PERIOD_WEEK;
254                 return RESOURCED_PERIOD_WEEK;
255         }
256
257         if (_is_period_devisible(time_period, RESOURCED_PERIOD_DAY)) {
258                 *quantity = time_period / RESOURCED_PERIOD_DAY;
259                 return RESOURCED_PERIOD_DAY;
260         }
261
262         if (_is_period_devisible(time_period, RESOURCED_PERIOD_HOUR)) {
263                 *quantity = time_period / RESOURCED_PERIOD_HOUR;
264                 return RESOURCED_PERIOD_HOUR;
265         }
266
267         *quantity = time_period;
268         return RESOURCED_PERIOD_UNDEF;
269 }
270
271
272 static time_t _get_finish_time(const time_t start_time, const int time_period)
273 {
274         int quantity = 0;
275         struct tm new_start;
276
277         if (gmtime_r((const time_t *)&start_time, &new_start) == NULL)
278                 return (time_t)(-1);
279
280         switch (_define_period(time_period, &quantity)) {
281         case RESOURCED_PERIOD_UNDEF:
282                 return start_time + time_period;
283         case RESOURCED_PERIOD_HOUR:
284                 new_start.tm_hour += quantity;
285         break;
286         case RESOURCED_PERIOD_DAY:
287                 new_start.tm_mday += quantity;
288         break;
289         case RESOURCED_PERIOD_WEEK:
290                 new_start.tm_mday += quantity * 7;
291         break;
292         case RESOURCED_PERIOD_MONTH:
293                 new_start.tm_mon += quantity;
294         break;
295         }
296
297         /* normilize */
298         return mktime(&new_start);
299 }
300
301 struct data_usage_context {
302         int64_t sent_used_quota;
303         int64_t rcv_used_quota;
304         resourced_roaming_type roaming;
305 };
306
307 static resourced_cb_ret data_usage_details_cb(const data_usage_info *info,
308                                                void *user_data)
309 {
310         struct data_usage_context *context =
311                 (struct data_usage_context *)user_data;
312
313         if (!context ||
314             (context->roaming != RESOURCED_ROAMING_UNKNOWN &&
315              context->roaming != info->roaming))
316                 return RESOURCED_CONTINUE;
317
318         context->sent_used_quota = info->foreground.cnt.incoming_bytes;
319         context->rcv_used_quota = info->foreground.cnt.outgoing_bytes;
320         return RESOURCED_CANCEL; /* only one entry allowed */
321 }
322
323 static void _record_quota(const struct quota_key *key,
324                           const struct quota *app_quota)
325 {
326         if (!key || !app_quota) {
327                 _E("Please, provide valid argument.");
328                 return;
329         }
330
331         if (!app_quota->sent_used_quota &&
332             !app_quota->rcv_used_quota) {
333                 _D("Nothing to store for effective quota.");
334                 return;
335         }
336
337         if (sqlite3_bind_text(insert_stmt, 1, key->app_id, -1,
338                           SQLITE_STATIC) != SQLITE_OK) {
339                 _SE("Can not bind app_id:%s for preparing statement",
340                         key->app_id);
341                 return;
342         }
343
344         if (sqlite3_bind_int64(insert_stmt, 2, app_quota->sent_used_quota)
345                 != SQLITE_OK) {
346                 _E("Can not bind sent_used_quota:%lld for preparing statement",
347                         app_quota->sent_used_quota);
348                 return;
349         }
350
351         if (sqlite3_bind_int64(insert_stmt, 3, app_quota->rcv_used_quota)
352                 != SQLITE_OK) {
353                 _E("Can not bind rcv_used_quota:%lld for preparing statement",
354                         app_quota->rcv_used_quota);
355                 return;
356         }
357
358         if (sqlite3_bind_int64(insert_stmt, 4, app_quota->real_start)
359                 != SQLITE_OK) {
360                 _E("Can not bind start_time:%d for preparing statement",
361                         app_quota->real_start);
362                 return;
363         }
364
365         if (sqlite3_bind_int64(insert_stmt, 5, app_quota->real_finish)
366                 != SQLITE_OK) {
367                 _E("Can not bind finish_time:%d for preparing statement",
368                         app_quota->real_finish);
369                 return;
370         }
371
372         if (sqlite3_bind_int(insert_stmt, 6, key->iftype)
373                 != SQLITE_OK) {
374                 _E("Can not bind iftype:%d for preparing statement",
375                         key->iftype);
376                 return;
377         }
378
379         if (sqlite3_bind_int(insert_stmt, 7, key->roaming)
380                 != SQLITE_OK) {
381                 _E("Can not bind roaming:%d for preparing statement",
382                         key->roaming);
383                 return;
384         }
385
386         if (sqlite3_bind_int(insert_stmt, 8, app_quota->state)
387                 != SQLITE_OK) {
388                 _E("Can not bind state:%d for preparing statement",
389                         app_quota->state);
390                 return;
391         }
392
393         if (sqlite3_step(insert_stmt) != SQLITE_DONE)
394                 _D("Failed to record quotas %s", sqlite3_errmsg(resourced_get_database()));
395         sqlite3_reset(insert_stmt);
396 }
397
398 static void _set_effective_quota(const char *app_id,
399         const resourced_iface_type iftype, const time_t start_time,
400         const int time_period,
401         const resourced_roaming_type roaming)
402 {
403         data_usage_selection_rule rule = {0,};
404         struct data_usage_context out_context = {0,};
405         struct quota_key key_quota = {
406                 .app_id = app_id,
407                 .iftype = iftype,
408                 .roaming = roaming,
409         };
410         struct quota app_quota = {0,};
411         const time_t cur_time = time(0);
412         char buf[28];
413
414         if (cur_time < start_time) {
415                 _D("No need to update effective quota!");
416                 return;
417         }
418
419         out_context.roaming = roaming;
420         rule.from = start_time;
421         rule.to = cur_time;
422         rule.iftype = iftype;
423
424         if (data_usage_details_foreach(app_id, &rule, data_usage_details_cb,
425                                        &out_context) != RESOURCED_ERROR_NONE) {
426                 _E("Cant obtain sent_used_quota/rcv_used_quota");
427                 return;
428         }
429
430         if (ctime_r(&start_time, buf) == NULL) {
431                 _E("Input parameter start_time is invalid");
432                 return;
433         }
434
435         _SD("Get counted traffic for appid:%s, per"
436                 "%s, incoming:%d, outgoing:%d", app_id, buf,
437                 out_context.rcv_used_quota, out_context.sent_used_quota);
438
439         app_quota.sent_used_quota = out_context.sent_used_quota;
440         app_quota.rcv_used_quota = out_context.rcv_used_quota;
441         app_quota.real_start = start_time;
442         app_quota.real_finish = _get_finish_time(start_time, time_period);
443         app_quota.state = RESOURCED_QUOTA_APPLIED;
444         _record_quota(&key_quota, &app_quota);
445 }
446
447 void update_quota_state(const char *app_id,
448                         const resourced_iface_type iftype,
449                         const time_t start_time,
450                         const int time_period,
451                         const resourced_roaming_type roaming)
452 {
453         struct quota_key key;
454         struct quota *tree_value;
455
456         if (!app_id) {
457                 _SE("app_id must be not NULL");
458                 return;
459         }
460
461         key.app_id = app_id;
462         key.iftype = iftype;
463         key.roaming = roaming;
464         tree_value = (struct quota *)g_tree_search(quotas,
465             (GCompareFunc)compare_quota_key, &key);
466
467         if (tree_value && tree_value->state == RESOURCED_QUOTA_APPLIED) {
468                 _SD("Removing quota and restriction for %s,%d", app_id, iftype);
469                 /* Restrictions can't be separated */
470                 remove_restriction_local(app_id, iftype);
471                 g_tree_remove(quotas, (gconstpointer*)(&key));
472                 _clear_effective_quota(app_id, iftype, roaming);
473
474                 if (start_time && time_period)
475                         _set_effective_quota(app_id, iftype, start_time,
476                         time_period, roaming);
477         } else
478                 _SD("There is no quota %s,%d in tree", app_id, iftype);
479 }
480
481 static resourced_ret_c _init_quotas(void)
482 {
483         execute_once {
484                 quotas = g_tree_new_full(compare_quota_key, NULL,
485                         quota_key_destructor, quota_destructor);
486         }
487
488         if (!resourced_get_database())
489                 return RESOURCED_ERROR_DB_FAILED;
490
491         if (select_stmt && insert_stmt)
492                 return RESOURCED_ERROR_NONE;
493
494         if (sqlite3_prepare_v2(resourced_get_database(),
495                                select_query, -1, &select_stmt,
496                                NULL) != SQLITE_OK) {
497                 _E("Error preparing query: %s, \
498                    %s\n", select_query, sqlite3_errmsg(resourced_get_database()));
499                 goto handle_error;
500         }
501
502         if (sqlite3_prepare_v2(resourced_get_database(),
503                                insert_query, -1, &insert_stmt,
504                                NULL) != SQLITE_OK) {
505                 _E("Error preparing query: %s, \
506                    %s\n", insert_query, sqlite3_errmsg(resourced_get_database()));
507                 goto handle_error;
508         }
509
510         if (sqlite3_prepare_v2(resourced_get_database(),
511                                clear_effective_quota_query,
512                                -1, &clear_effective_stmt,
513                                NULL) != SQLITE_OK) {
514                 _E("Error preparing query: %s, \
515                    %s\n", clear_effective_quota_query,
516                         sqlite3_errmsg(resourced_get_database()));
517                 goto handle_error;
518         }
519
520         return RESOURCED_ERROR_NONE;
521 handle_error:
522         /* Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op */
523         sqlite3_finalize(select_stmt);
524         sqlite3_finalize(insert_stmt);
525         sqlite3_finalize(clear_effective_stmt);
526         return RESOURCED_ERROR_DB_FAILED;
527 }
528
529
530 /**
531  * Update quotas tree, where app_id will the key
532  */
533 static resourced_ret_c _update_quotas(void)
534 {
535         const resourced_ret_c ret = _init_quotas();
536         if (ret != RESOURCED_ERROR_NONE) {
537                 _E("Failed to init quotas");
538                 return ret;
539         }
540
541         obtain_and_keep_quotas(select_stmt);
542         return RESOURCED_ERROR_NONE;
543 }
544
545 static const int64_t quota_gap_value[RESOURCED_IFACE_ALL] = {
546         5000,   /* ~4.5MB UNKNOWN */
547         5000,   /* ~3MB RESOURCED_IFACE_DATACALL */
548         6000000,        /* ~6MB RESOURCED_IFACE_WIFI */
549         5000000,        /* ~100MB RESOURCED_IFACE_WIRED */
550         6000000,        /* ~6MB RESOURCED_IFACE_BLUETOOTH */
551 };
552
553 static const int64_t quota_datacall_gap_value[RESOURCED_PROTOCOL_MAX_ELEM] = {
554         5000,  /* RESOURCED_PROTOCOL_NONE */
555         5000,  /* RESOURCED_PROTOCOL_DATACALL_NOSVC */
556         5000,  /* RESOURCED_PROTOCOL_DATACALL_EMERGENCY */
557         5000,  /* RESOURCED_PROTOCOL_DATACALL_SEARCH */
558         5000,  /* RESOURCED_PROTOCOL_DATACALL_2G */
559         5000,  /* RESOURCED_PROTOCOL_DATACALL_2_5G #GPRS 40 kbit/s in practice */
560         18750, /* RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE 150 kbit/s in practice */
561         400000, /* RESOURCED_PROTOCOL_DATACALL_3G, 7Mb/s on QC device */
562         475000, /* RESOURCED_PROTOCOL_DATACALL_HSDPA */
563         5000000,/* RESOURCED_PROTOCOL_DATACALL_LTE */
564 };
565
566 /*
567  * @desc this function returns valud per second
568  */
569 static int64_t _get_quota_gap(const resourced_iface_type iftype)
570 {
571
572         const resourced_hw_net_protocol_type proto = get_hw_net_protocol_type(iftype);
573
574         if (proto != RESOURCED_PROTOCOL_NONE)
575                 return quota_datacall_gap_value[proto];
576
577         if (iftype > RESOURCED_IFACE_UNKNOWN &&
578             iftype < RESOURCED_IFACE_ALL)
579                 return quota_gap_value[iftype];
580
581         return quota_gap_value[RESOURCED_IFACE_UNKNOWN];
582 }
583
584 int _is_under_restriction(const int64_t send_delta,
585         const int64_t rcv_delta,
586         const resourced_iface_type iftype,
587         int update_period)
588 {
589         /* multiply on 2, due  */
590         const int64_t quota_gap = _get_quota_gap(iftype) * update_period;
591
592         _D("send_delta %"PRId64" rcv_delta%"PRId64" quota_gap %"PRId64""
593                  "update_period %d ",
594                 send_delta, rcv_delta, quota_gap, update_period);
595         return send_delta <= quota_gap ||
596                 rcv_delta <= quota_gap;
597 }
598
599 inline void _check_warning_threshold(const int64_t send_delta, const int64_t rcv_delta,
600         struct quota *app_quota, const char *appid)
601 {
602         ret_msg_if(!app_quota, "Please provide valid pointer");
603
604         if (send_delta <= app_quota->snd_warning_threshold ||
605             rcv_delta <= app_quota->rcv_warning_threshold) {
606                 app_quota->snd_warning_threshold = 0;
607                 app_quota->rcv_warning_threshold = 0;
608                 send_restriction_warn_notification(appid);
609         }
610 }
611
612 inline static int _get_warning_limit(int64_t limit, int threshold)
613 {
614         if (limit < threshold) {
615                 _E("Warning threshold is greater than limit!");
616                 return WARNING_THRESHOLD_DEFAULT; /* 0 means kernel will
617                                                 not procced it*/
618         }
619         return limit - threshold;
620 }
621
622 static int cast_restriction_limit(int64_t delta)
623 {
624         if (delta < 0)
625                 return 0;
626         if (delta > INT_MAX)
627                 return INT_MAX;
628         return delta;
629 }
630
631 static gboolean check_and_apply_node(gpointer key,
632                                      gpointer value, gpointer user_data)
633 {
634         struct quota *app_quota = value;
635         struct quota_key *key_quota = key;
636         int64_t send_delta, rcv_delta;
637         struct daemon_opts *opts = (struct daemon_opts *)user_data;
638         resourced_net_restrictions rst = { RESOURCED_STATE_UNKNOWN,
639                                            RESOURCED_IFACE_UNKNOWN };
640
641         /* do not check already applied quota*/
642         if (app_quota->state == RESOURCED_QUOTA_APPLIED)
643                 return FALSE;
644
645         send_delta = app_quota->send_quota - app_quota->sent_used_quota;
646         rcv_delta = app_quota->rcv_quota - app_quota->rcv_used_quota;
647
648         if (app_quota->send_quota <= 0 || app_quota->rcv_quota <= 0)
649                 send_restriction_notification(key_quota->app_id);
650         else
651                 _check_warning_threshold(send_delta, rcv_delta, app_quota,
652                         key_quota->app_id);
653
654         if (_is_under_restriction(send_delta, rcv_delta, key_quota->iftype,
655                 opts->update_period) &&
656             (key_quota->roaming == RESOURCED_ROAMING_UNKNOWN ||
657              key_quota->roaming == get_roaming())) {
658                 if (!strcmp(key_quota->app_id, TETHERING_APP_NAME) &&
659                     (send_delta > 0 || rcv_delta > 0))
660                         /* in the case of tethering we send
661                            restriction only that must apply now */
662                         return FALSE;
663
664                 rst.send_limit = cast_restriction_limit(send_delta);
665                 rst.rcv_limit = cast_restriction_limit(rcv_delta);
666                 rst.snd_warning_limit = _get_warning_limit(
667                         rst.send_limit, app_quota->snd_warning_threshold);
668                 rst.rcv_warning_limit = _get_warning_limit(
669                         rst.rcv_limit, app_quota->rcv_warning_threshold);
670
671                 _SD("Applying quota for %s, iftype %d", key_quota->app_id,
672                     key_quota->iftype);
673                 rst.iftype = key_quota->iftype;
674
675                 if (proc_keep_restriction(key_quota->app_id,
676                                               app_quota->quota_id, &rst,
677                                               RST_SET) == RESOURCED_ERROR_NONE) {
678                         app_quota->state = RESOURCED_QUOTA_APPLIED;
679                         _D("Restriction was applied successfully.");
680                 }
681         }
682
683         return FALSE; /* continue iteration */
684 }
685
686 static void check_and_apply_quota(volatile struct daemon_opts *opts)
687 {
688         g_tree_foreach(quotas, check_and_apply_node, (void *)opts);
689 }
690
691 struct update_all_arg
692 {
693         resourced_iface_type iftype;
694         struct application_stat *app_stat;
695 };
696
697 static gboolean update_pseudo_app_entry(gpointer key,
698         gpointer value, gpointer user_data)
699 {
700         struct update_all_arg *arg = (struct
701                 update_all_arg *)user_data;
702         const struct quota_key *qkey = (const struct
703                 quota_key *)key;
704
705         /* handle case for network interfaces*/
706         if ((!strcmp(qkey->app_id, RESOURCED_ALL_APP) &&
707              (qkey->iftype == RESOURCED_IFACE_UNKNOWN ||
708               qkey->iftype == RESOURCED_IFACE_ALL ||
709               qkey->iftype == arg->iftype) &&
710              (qkey->roaming == RESOURCED_ROAMING_UNKNOWN ||
711               qkey->roaming == arg->app_stat->is_roaming)) ||
712             !strcmp(qkey->app_id, TETHERING_APP_NAME)) {
713                 struct quota *total_quota = (struct quota *)value;
714                 /* update it */
715                 total_quota->sent_used_quota += arg->app_stat->delta_snd;
716                 total_quota->rcv_used_quota += arg->app_stat->delta_rcv;
717                 arg->app_stat->delta_snd = 0;
718                 arg->app_stat->delta_rcv = 0;
719                 _D("update total_quota tx:%"PRId64";rx:%"PRId64" iftype %d ifindex %d\n",
720                    total_quota->sent_used_quota, total_quota->rcv_used_quota,
721                         arg->iftype, arg->app_stat->ifindex);
722
723         }
724
725         return FALSE;
726 }
727
728 static void update_all_app_quotas(struct update_all_arg *update_all_arg)
729 {
730         /* Now RESOURCED_ALL_APP can contain many iftypes */
731         g_tree_foreach(quotas, update_pseudo_app_entry, update_all_arg);
732 }
733
734 static void update_traffic_quota(const struct quota_key *quota_key,
735                                  uint32_t *snd_count,
736                                  uint32_t *rcv_count)
737 {
738         struct quota *found_quota = g_tree_lookup(quotas, quota_key);
739
740         if (!found_quota)
741                 return;
742         if (time(0) < found_quota->start_time) {
743                 _D("No need to update effective quota!");
744                 return;
745         }
746         found_quota->sent_used_quota += *snd_count;
747         found_quota->rcv_used_quota += *rcv_count;
748         _D("update total_quota tx:%"PRId64";rx:%"PRId64"\n",
749            found_quota->sent_used_quota, found_quota->rcv_used_quota);
750
751         _D("delta_rcv %d app_id %s\n", *rcv_count, quota_key->app_id);
752         *snd_count = 0;
753         *rcv_count = 0;
754         return;
755 }
756
757 static gboolean update_each_quota(gpointer key, gpointer value,
758         gpointer UNUSED userdata)
759 {
760         const struct classid_iftype_key *app_key =
761                 (const struct classid_iftype_key *)key;
762         struct application_stat *app_stat =
763                 (struct application_stat *)value;
764         struct update_all_arg arg = {
765                 .iftype = app_key->iftype,
766                 .app_stat = app_stat
767         };
768         struct quota_key qkey;
769
770         /* We should handle cases of RESOURCED_ALL_APP or TETHERING_APP_NAME
771            in separate way due it's not comming with statistics from kernel */
772         update_all_app_quotas(&arg);
773
774         if (!app_stat->application_id)
775                 return FALSE;
776
777         qkey.app_id = app_stat->application_id;
778         qkey.iftype = app_key->iftype;
779         qkey.roaming = app_stat->is_roaming;
780         update_traffic_quota(&qkey, &app_stat->delta_snd,
781                              &app_stat->delta_rcv);
782         return FALSE;
783 }
784
785 static void actualize_quota_table(struct application_stat_tree *apps)
786 {
787         g_tree_foreach((GTree *)apps->tree, update_each_quota, NULL);
788 }
789
790 /**
791  * @desc Assume app_quota is not null
792  */
793 static void calculate_finish_time(struct quota *app_quota)
794 {
795         if (!app_quota || app_quota->real_finish)
796                 return;
797
798         if (!app_quota->real_start)
799                 app_quota->real_start = time(0);
800
801         app_quota->real_finish = _get_finish_time(app_quota->real_start,
802                 app_quota->time_period);
803 }
804
805 /**
806  * @desc Reset quota. This function sets new real_start based on fihish time.
807  * Assume app_quota is set and  time(0) < app_quota->real_finish
808  */
809 static void reset_quota(struct quota *app_quota)
810 {
811         _D("reset_quota called");
812         app_quota->real_start = app_quota->real_finish;
813         app_quota->real_finish = 0;
814         app_quota->sent_used_quota = app_quota->rcv_used_quota = 0;
815         restriction_set_status(RESTRICTION_STATE_UNSET);
816 }
817
818 /**
819  * @desc Remove restriction if needed
820  */
821 static void drop_restriction(const struct quota_key *qkey, struct quota *app_quota)
822 {
823         if (!app_quota || !qkey) {
824                 _E("Please provide valid arguments!");
825                 return;
826         }
827
828         /* We can revert only applied quotas */
829         if (app_quota->state != RESOURCED_QUOTA_APPLIED)
830                 return;
831
832         _SD("Removing restriction of quota for %s,%d", qkey->app_id,
833             qkey->iftype);
834         if (remove_restriction_local(qkey->app_id, qkey->iftype)
835             == RESOURCED_ERROR_NONE)
836                 app_quota->state = RESOURCED_QUOTA_REVERTED;
837 }
838
839 /**
840  * @desc This function actualize current quotas states. It calculate new
841  *  finish time and remove restriction if exists.
842  */
843 static gboolean flush_quota_node(gpointer key,
844         gpointer value, gpointer UNUSED user_data)
845 {
846         struct quota *app_quota = value;
847         struct quota_key *key_quota = key;
848
849         if (!app_quota || !key_quota->app_id)
850                 return FALSE; /* continue iteration even
851                 current data is empty */
852
853         calculate_finish_time(app_quota);
854
855         _record_quota(key_quota, app_quota);
856         /* It's time to reset */
857         if (time(0) >= app_quota->real_finish) {
858                 drop_restriction(key_quota, app_quota);
859                 reset_quota(app_quota);
860         }
861         return FALSE;
862 }
863
864 /**
865  * Save to database effective quota
866  */
867 void flush_quota_table(void)
868 {
869         g_tree_foreach(quotas, flush_quota_node, NULL);
870 }
871
872 static void finalize_statement(sqlite3_stmt **stmt)
873 {
874         if (*stmt) {
875                 sqlite3_finalize(*stmt);
876                 *stmt = NULL;
877         }
878 }
879
880 resourced_ret_c process_quota(struct application_stat_tree *apps,
881         volatile struct daemon_opts *opts)
882 {
883         /* For first initialization */
884         static int quota_updated;
885
886         if (opts && opts->is_update_quota) {
887                 const int error = _update_quotas();
888                 if (error)
889                         return error;
890                 quota_updated = 1;
891         }
892
893         actualize_quota_table(apps);
894
895         check_and_apply_quota(opts);
896
897         /* finilize state */
898         if (opts && opts->is_update_quota && quota_updated) {
899                 opts->is_update_quota = 0;
900                 quota_updated = 0;
901         }
902         return RESOURCED_ERROR_NONE;
903 }
904
905 /**
906  * Release  statement
907  */
908 void finalize_quotas(void)
909 {
910         finalize_statement(&insert_stmt);
911         finalize_statement(&select_stmt);
912         finalize_statement(&clear_effective_stmt);
913         g_tree_destroy(quotas);
914 }
915