37a5c5a114eee85e03858b8467c2078f78b684e0
[platform/core/telephony/tel-plugin-indicator.git] / src / s_indi_main.c
1 /*
2  * tel-plugin-indicator
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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23
24 #include <glib.h>
25 #include <gio/gio.h>
26
27 #include <tcore.h>
28 #include <server.h>
29 #include <plugin.h>
30 #include <storage.h>
31 #include <co_ps.h>
32 #include <co_context.h>
33 #include <co_sim.h>
34 #include <co_network.h>
35 #include <co_call.h>
36
37 #include "s_indi_main.h"
38 #include "s_indi_util.h"
39 #include "s_indi_log.h"
40
41 #define S_INDI_UPDATE_INTERVAL          1
42 #define S_INDI_PROC_FILE                        "/proc/net/dev"
43
44 #define S_INDI_VCONF_STORAGE_NAME               "vconf"
45
46 #define S_INDI_ALLOC_USER_DATA(data, plugin, cp) \
47         do { \
48                 data = s_indi_malloc0(sizeof(__s_indi_cb_user_data)); \
49                 if (data == NULL) { \
50                         err("Memory allocation failed!!"); \
51                         s_indi_free(cp); \
52                         return FALSE; \
53                 } \
54                 data->indi_plugin = plugin; \
55                 data->cp_name = cp; \
56         } while (0)
57
58 #define S_INDI_FREE_USER_DATA(data) \
59         do { \
60                 s_indi_free(data->cp_name); \
61                 s_indi_free(data); \
62         } while (0)
63
64 typedef struct {
65         TcorePlugin *indi_plugin;
66         gchar *cp_name;
67 } __s_indi_cb_user_data;
68
69 typedef struct {
70         GHashTable *state_info; /* HashTable of s_indi_cp_state_info_type with key = cp_name */
71
72         GHashTable *vconf_info; /* Mapping of enum tcore_storage_key to cp_name */
73 } s_indi_private_info;
74
75 /***************** HOOKS *****************/
76 static enum tcore_hook_return s_indi_on_hook_modem_plugin_removed(Server *server, CoreObject *source,
77         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data);
78 static enum tcore_hook_return s_indi_on_hook_modem_plugin_added(Server *server, CoreObject *source,
79         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data);
80 static enum tcore_hook_return s_indi_on_hook_voice_call_status(Server *server, CoreObject *source,
81         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data);
82 static enum tcore_hook_return s_indi_on_hook_net_register(Server *server, CoreObject *source,
83         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data);
84 static enum tcore_hook_return s_indi_on_hook_ps_call_status(Server *server, CoreObject *source,
85         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data);
86 static enum tcore_hook_return s_indi_on_hook_modem_power(Server *server, CoreObject *source,
87         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data);
88
89 /***************** VCONF Callbacks *****************/
90 static void s_indi_storage_key_callback(enum tcore_storage_key key, void *value, void *user_data);
91
92 /***************** Utilities: GDestroyNotifications *****************/
93 static void __s_indi_state_info_value_destroy_notification(gpointer data);
94 static void __s_indi_dev_info_value_destroy_notification(gpointer data);
95
96 /***************** Utilities: Indicator Plugin *****************/
97 static inline s_indi_private_info *__s_indi_get_priv_info(TcorePlugin *plugin);
98 static gboolean __s_indi_start_updater(TcorePlugin *indi_plugin, gchar *cp_name);
99 static gboolean __s_indi_update_callback(__s_indi_cb_user_data *data);
100 static void __s_indi_refresh_modems(TcorePlugin *indi_plugin);
101 static s_indi_cp_state_info_type *__s_indi_alloc_state_info(CoreObject *co_ps);
102 static s_indi_dev_state_info_type *__s_indi_alloc_device_state(CoreObject *ps_context, s_indi_cp_state_info_type *parent);
103 static CoreObject *__s_indi_fetch_ps_co(TcorePlugin *plugin);
104
105 static void __s_indi_add_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin);
106 static void __s_indi_remove_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin);
107 static void __s_indi_register_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name);
108 static void __s_indi_unregister_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name);
109 static gboolean __s_indi_handle_voice_call_status(Server *server, CoreObject *source,
110         enum tcore_notification_command command, const char *cp_name,
111         s_indi_cp_state_info_type *state_info);
112
113 void __s_indi_register_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name)
114 {
115         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
116         Storage *strg_vconf = tcore_server_find_storage(tcore_plugin_ref_server(indi_plugin), S_INDI_VCONF_STORAGE_NAME);
117         if (priv_info == NULL) {
118                 err("priv_info is NULL!!!");
119                 return;
120         }
121         s_indi_assert(NULL != strg_vconf);
122
123         /** NULL cp_name: subscription independent vconf key */
124         if (tcore_storage_set_key_callback(strg_vconf, key, s_indi_storage_key_callback, indi_plugin)
125                         && (NULL != cp_name))
126                 g_hash_table_insert(priv_info->vconf_info, GUINT_TO_POINTER(key), s_indi_strdup(cp_name));
127 }
128
129 void __s_indi_unregister_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name)
130 {
131         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
132         Storage *strg_vconf = tcore_server_find_storage(tcore_plugin_ref_server(indi_plugin), S_INDI_VCONF_STORAGE_NAME);
133         if (priv_info == NULL) {
134                 err("priv_info is NULL!!!");
135                 return;
136         }
137         s_indi_assert(NULL != strg_vconf);
138
139         /** NULL cp_name: subscription independent vconf key */
140         if (tcore_storage_remove_key_callback(strg_vconf, key, s_indi_storage_key_callback)
141                 && (NULL != cp_name))
142                 g_hash_table_remove(priv_info->vconf_info, GUINT_TO_POINTER(key));
143 }
144
145 void __s_indi_add_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin)
146 {
147         gchar *cp_name = NULL;
148         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
149         if (priv_info == NULL) {
150                 err("priv_info is NULL!!!");
151                 return;
152         }
153
154         /** @todo: It may be possible to use cp_name without duping as well */
155         cp_name = s_indi_strdup(tcore_server_get_cp_name_by_plugin(modem_plugin));
156         s_indi_assert(NULL != cp_name);
157         s_indi_log_ex(cp_name, "Added");
158
159         /** @todo: Check if key-value replacement is the intended behavior */
160         g_hash_table_insert(priv_info->state_info, cp_name, __s_indi_alloc_state_info(__s_indi_fetch_ps_co(modem_plugin)));
161 }
162
163 void __s_indi_remove_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin)
164 {
165         const char *cp_name = NULL;
166         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
167         if (priv_info == NULL) {
168                 err("priv_info is NULL!!!");
169                 return;
170         }
171         s_indi_assert(NULL != priv_info->state_info);
172
173         cp_name = tcore_server_get_cp_name_by_plugin(modem_plugin);
174         s_indi_assert(NULL != cp_name);
175
176         if (g_hash_table_remove(priv_info->state_info, cp_name))
177                 s_indi_log_ex(cp_name, "Removed");
178 }
179
180 CoreObject *__s_indi_fetch_ps_co(TcorePlugin *plugin)
181 {
182         CoreObject *co_ps = NULL;
183         GSList *co_list = tcore_plugin_get_core_objects_bytype(plugin, CORE_OBJECT_TYPE_PS);
184         s_indi_assert(co_list != NULL);
185         s_indi_assert(g_slist_length(co_list) == S_INDI_ONE);
186
187         co_ps = g_slist_nth_data(co_list, S_INDI_ZERO);
188         s_indi_assert(co_ps != NULL);
189         g_slist_free(co_list);
190
191         return co_ps;
192 }
193
194 s_indi_cp_state_info_type *__s_indi_alloc_state_info(CoreObject *co_ps)
195 {
196         s_indi_cp_state_info_type *state_info = s_indi_malloc0(sizeof(s_indi_cp_state_info_type));
197         if (!state_info) {
198                 err("Memory allocation failed!!");
199                 return NULL;
200         }
201         state_info->co_ps = co_ps;
202         state_info->ps_state = S_INDI_CELLULAR_UNKNOWN;
203         state_info->cp_trans_state = S_INDI_TRANSFER_UNKNOWN;
204         state_info->dormant_info.lcd_state = S_INDI_LCD_UNKNOWN;
205         state_info->rx_total = S_INDI_ZERO;
206         state_info->tx_total = S_INDI_ZERO;
207         state_info->dormant_info.parent = state_info;
208
209         /* tcore_context_get_ipv4_devname uses glib allocator so key should be freed using g_free() */
210         state_info->device_info = g_hash_table_new_full(g_str_hash, g_str_equal,
211                 g_free, __s_indi_dev_info_value_destroy_notification);
212         return state_info;
213 }
214
215 s_indi_dev_state_info_type *__s_indi_alloc_device_state(CoreObject *ps_context, s_indi_cp_state_info_type *parent)
216 {
217         s_indi_dev_state_info_type *dev_state = s_indi_malloc0(sizeof(s_indi_dev_state_info_type));
218         if (dev_state == NULL) {
219                 err("Memory allocation failed!!");
220                 return NULL;
221         }
222
223         dev_state->ps_context = ps_context;
224         dev_state->parent = parent;
225         return dev_state;
226 }
227
228 s_indi_private_info *__s_indi_get_priv_info(TcorePlugin *plugin)
229 {
230         s_indi_private_info *priv_info = tcore_plugin_ref_user_data(plugin);
231         s_indi_assert(NULL != priv_info);
232         return priv_info;
233 }
234
235 gboolean __s_indi_start_updater(TcorePlugin *indi_plugin, gchar *cp_name)
236 {
237         __s_indi_cb_user_data *cb_data = NULL;
238         s_indi_cp_state_info_type *state_info = NULL;
239         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
240         if (priv_info == NULL) {
241                 err("priv_info is NULL!!!");
242                 return FALSE;
243         }
244
245         if ((state_info = g_hash_table_lookup(priv_info->state_info, cp_name)) == NULL) {
246                 warn("CP [%s] Not Present", cp_name);
247                 s_indi_free(cp_name);
248                 return FALSE;
249         }
250
251         if (state_info->src != NULL) {
252                 dbg("Another one is in progress");
253                 s_indi_free(cp_name);
254                 return FALSE;
255         }
256
257         S_INDI_ALLOC_USER_DATA(cb_data, indi_plugin, cp_name);
258
259         dbg("indicator is starting");
260         state_info->src = g_timeout_source_new_seconds(S_INDI_UPDATE_INTERVAL);
261         g_source_set_callback(state_info->src, (GSourceFunc)__s_indi_update_callback, cb_data, NULL);
262         g_source_set_priority(state_info->src, G_PRIORITY_HIGH);
263         g_source_attach(state_info->src, NULL);
264         g_source_unref(state_info->src);
265         return TRUE;
266 }
267
268 gboolean __s_indi_update_callback(__s_indi_cb_user_data *data)
269 {
270 #define INDICATOR_BUFF_SIZE 4096
271         gchar buff[INDICATOR_BUFF_SIZE];
272         s_indi_cp_state_info_type *state_info = NULL;
273         s_indi_dev_state_info_type *dev_state = NULL;
274         TcorePlugin *indi_plugin = data->indi_plugin;
275         const char *cp_name = data->cp_name;
276         unsigned int modem_id;
277         FILE *pf = NULL;
278         gchar *rv = NULL;
279         unsigned long long rx_curr_total = S_INDI_ZERO, tx_curr_total = S_INDI_ZERO, rx_prev_total = S_INDI_ZERO, tx_prev_total = S_INDI_ZERO;
280         unsigned long long rx_changes_total = S_INDI_ZERO, tx_changes_total = S_INDI_ZERO;
281         s_indi_transfer_state cp_state = S_INDI_TRANSFER_NORMAL; /* Assume no activity */
282         enum tcore_storage_key key_last_rcv, key_last_snt, key_total_rcv, key_total_snt, key_service_state;
283         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
284         Storage *strg_vconf = NULL;
285
286         if (priv_info == NULL) {
287                 err("priv_info is NULL!!!");
288                 return G_SOURCE_REMOVE;
289         }
290
291         /* VCONF Mapper */
292         if (s_indi_str_has_suffix(cp_name, "0")) {
293                 key_last_rcv = STORAGE_KEY_CELLULAR_PKT_LAST_RCV;
294                 key_last_snt = STORAGE_KEY_CELLULAR_PKT_LAST_SNT;
295                 key_total_rcv = STORAGE_KEY_CELLULAR_PKT_TOTAL_RCV;
296                 key_total_snt = STORAGE_KEY_CELLULAR_PKT_TOTAL_SNT;
297                 key_service_state = STORAGE_KEY_PACKET_SERVICE_STATE;
298                 modem_id = MODEM_ID_PRIMARY;
299         } else if (s_indi_str_has_suffix(cp_name, "1")) {
300                 key_last_rcv = STORAGE_KEY_CELLULAR_PKT_LAST_RCV2;
301                 key_last_snt = STORAGE_KEY_CELLULAR_PKT_LAST_SNT2;
302                 key_total_rcv = STORAGE_KEY_CELLULAR_PKT_TOTAL_RCV2;
303                 key_total_snt = STORAGE_KEY_CELLULAR_PKT_TOTAL_SNT2;
304                 key_service_state = STORAGE_KEY_PACKET_SERVICE_STATE2;
305                 modem_id = MODEM_ID_SECONDARY;
306         } else {
307                 err("Unhandled CP Name %s", cp_name);
308                 s_indi_assert_not_reached();
309                 S_INDI_FREE_USER_DATA(data);
310                 return G_SOURCE_REMOVE;
311         }
312
313         /* Check CP Name presence */
314         if ((state_info = g_hash_table_lookup(priv_info->state_info, cp_name)) == NULL) {
315                 warn("%s CP is not present", cp_name);
316                 goto EXIT;
317         }
318
319         /* Check dev_state presence */
320         if (g_hash_table_size(state_info->device_info) == S_INDI_ZERO) {
321                 msg("Nothing to update, aborting timer");
322                 goto EXIT;
323         }
324
325         /** @todo: Check if needs to be read atomically */
326         pf = fopen(S_INDI_PROC_FILE, "r");
327         if (pf == NULL) {
328                 err("indicator fail to open file(%s), errno(%d)", S_INDI_PROC_FILE, errno);
329                 goto EXIT;
330         }
331
332         /* Skip first line */
333         rv = fgets(buff, sizeof(buff), pf);
334         if (!rv) {
335                 err("fail to read file or reach EOF, plz check %s", S_INDI_PROC_FILE);
336                 goto EXIT;
337         }
338
339         /* Skip second line */
340         rv = fgets(buff, sizeof(buff), pf);
341         if (!rv) {
342                 err("fail to read file or reach EOF, plz check %s", S_INDI_PROC_FILE);
343                 goto EXIT;
344         }
345
346         /* Update all devices of state_info */
347         while (fgets(buff, sizeof(buff), pf)) {
348                 gchar *ifname = buff, *entry = NULL;
349
350                 /* Skip whitespaces */
351                 while (*ifname == ' ')
352                         ifname++;
353
354                 /* Terminate to read ifname */
355                 entry = strrchr(ifname, ':');
356                 if (entry == NULL)
357                         goto EXIT;
358                 *entry++ = '\0';
359
360                 /* Read device_info */
361                 /* Takes care of the fix: Fix the PLM p131003-03182. Sha-ID: 65544f0be8e60ae3f964921755a1e83fa8e71441*/
362                 if ((dev_state = g_hash_table_lookup(state_info->device_info, ifname)) != NULL) {
363                         gint result = S_INDI_ZERO;
364                         unsigned long long rx_pkt = S_INDI_ZERO, tx_pkt = S_INDI_ZERO;
365                         /************************************************************************
366                         Sample Input of S_INDI_PROC_FILE
367                         ************************************************************************
368                         Inter-|   Receive                                                |  Transmit
369                          face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
370                             lo: 114955545   88409    0    0    0     0          0         0 114955545   88409    0    0    0     0       0          0
371                           eth0: 2714004148 6475059    0    0    0     0          0         0 72595891 8726308    0    0    0     0       0          0
372                         ************************************************************************/
373                         s_indi_log_v("Reading stats of interface %s", ifname);
374                         result = sscanf(entry, "%llu %*s %*s %*s %*s %*s %*s %*s %llu %*s %*s %*s %*s %*s %*s %*s", &rx_pkt, &tx_pkt);
375                         if (result <= S_INDI_ZERO) {
376                                 err("stats fail to get proc field => %d", result);
377                                 goto EXIT; /** @todo: REMOVE or CONTINUE ? */
378                         }
379
380                         /* Save per device */
381                         dev_state->prev_rx = dev_state->curr_rx;
382                         dev_state->prev_tx = dev_state->curr_tx;
383                         dev_state->curr_rx = rx_pkt;
384                         dev_state->curr_tx = tx_pkt;
385
386                         /* Compute CP totals */
387                         rx_curr_total += rx_pkt;
388                         tx_curr_total += tx_pkt;
389                         rx_prev_total += dev_state->prev_rx;
390                         tx_prev_total += dev_state->prev_tx;
391                 }
392         }
393
394         rx_changes_total = rx_curr_total - rx_prev_total;
395         tx_changes_total = tx_curr_total - tx_prev_total;
396
397         if (rx_changes_total)
398                 cp_state |= S_INDI_TRANSFER_RX;
399
400         if (tx_changes_total)
401                 cp_state |= S_INDI_TRANSFER_TX;
402
403         if (cp_state > 0)
404                 s_indi_log_txrx(modem_id, "Transfer State:[%d] RX: [%llu / %llu] TX: [%llu / %llu]",
405                         cp_state, rx_changes_total, rx_curr_total, tx_changes_total, tx_curr_total);
406
407         if (state_info->dormant_info.lcd_state < S_INDI_LCD_OFF) {
408                 if (state_info->cp_trans_state != cp_state) { /* New Transfer State */
409                         strg_vconf = tcore_server_find_storage(tcore_plugin_ref_server(indi_plugin), S_INDI_VCONF_STORAGE_NAME);
410                         s_indi_assert(NULL != strg_vconf);
411
412                         state_info->cp_trans_state = cp_state;
413                         tcore_storage_set_int(strg_vconf, STORAGE_KEY_PACKET_INDICATOR_STATE, cp_state);
414                         if (cp_state != S_INDI_TRANSFER_NORMAL) { /* Data activity */
415                                 /** @todo: VCONF needs upgrade to support llu */
416                                 tcore_storage_set_int(strg_vconf, key_last_rcv, (int)(rx_curr_total/1000ULL));
417                                 tcore_storage_set_int(strg_vconf, key_last_snt, (int)(tx_curr_total/1000ULL));
418
419                                 s_indi_log_txrx(modem_id, "VCONF LAST- RX: [%d] TX: [%d]", (int)(rx_curr_total/1000ULL), (int)(tx_curr_total/1000ULL));
420                         }
421                 }
422         }
423
424         fclose(pf);
425         return G_SOURCE_CONTINUE; /* Revisit after S_INDI_UPDATE_INTERVAL */
426
427 EXIT:
428         dbg("indicator is stopped");
429         if (pf) fclose(pf);
430
431         strg_vconf = tcore_server_find_storage(tcore_plugin_ref_server(indi_plugin), S_INDI_VCONF_STORAGE_NAME);
432         s_indi_assert(NULL != strg_vconf);
433
434         /* Update PS Call and indicator states */
435         tcore_storage_set_int(strg_vconf, STORAGE_KEY_PACKET_INDICATOR_STATE, S_INDI_TRANSFER_NORMAL);
436         tcore_storage_set_int(strg_vconf, key_service_state, S_INDI_CELLULAR_OFF);
437         dbg("PS Call status (%s) - [DISCONNECTED]", cp_name);
438
439         if (state_info) {
440                 state_info->src = NULL;
441                 state_info->cp_trans_state = S_INDI_TRANSFER_NORMAL;
442                 state_info->ps_state = S_INDI_CELLULAR_OFF;
443
444                 /* Update total VCONF before dying updator */
445                 tcore_storage_set_int(strg_vconf, key_total_rcv, tcore_storage_get_int(strg_vconf, key_total_rcv) + state_info->rx_total);
446                 tcore_storage_set_int(strg_vconf, key_total_snt, tcore_storage_get_int(strg_vconf, key_total_snt) + state_info->tx_total);
447
448                 /* After updating total vconf key, last vconf key needs to be reset */
449                 tcore_storage_set_int(strg_vconf, key_last_rcv, 0);
450                 tcore_storage_set_int(strg_vconf, key_last_snt, 0);
451
452                 /** @todo: VCONF needs upgrade to support llu */
453                 s_indi_log_txrx(modem_id, "VCONF TOTAL- RX: [%d] TX: [%d]",
454                         tcore_storage_get_int(strg_vconf, key_total_rcv), tcore_storage_get_int(strg_vconf, key_total_snt));
455                 state_info->rx_total = S_INDI_ZERO;
456                 state_info->tx_total = S_INDI_ZERO;
457         }
458
459         S_INDI_FREE_USER_DATA(data);
460         return G_SOURCE_REMOVE;
461 }
462
463 void __s_indi_state_info_value_destroy_notification(gpointer data)
464 {
465         s_indi_cp_state_info_type *state_info = data;
466         const char *cp_name = NULL;
467         Storage *strg_vconf = NULL;
468         enum tcore_storage_key key_last_rcv, key_last_snt, key_total_rcv, key_total_snt;
469         unsigned int modem_id;
470         s_indi_assert(NULL != state_info);
471         s_indi_assert(NULL != state_info->co_ps);
472
473         cp_name = tcore_server_get_cp_name_by_plugin(tcore_object_ref_plugin(state_info->co_ps));
474         s_indi_assert(NULL != cp_name);
475         dbg("CP Name: [%s]", cp_name);
476
477         /* VCONF Mapper */
478         if (s_indi_str_has_suffix(cp_name, "0")) {
479                 key_last_rcv = STORAGE_KEY_CELLULAR_PKT_LAST_RCV;
480                 key_last_snt = STORAGE_KEY_CELLULAR_PKT_LAST_SNT;
481                 key_total_rcv = STORAGE_KEY_CELLULAR_PKT_TOTAL_RCV;
482                 key_total_snt = STORAGE_KEY_CELLULAR_PKT_TOTAL_SNT;
483                 modem_id = MODEM_ID_PRIMARY;
484         } else if (s_indi_str_has_suffix(cp_name, "1")) {
485                 key_last_rcv = STORAGE_KEY_CELLULAR_PKT_LAST_RCV2;
486                 key_last_snt = STORAGE_KEY_CELLULAR_PKT_LAST_SNT2;
487                 key_total_rcv = STORAGE_KEY_CELLULAR_PKT_TOTAL_RCV2;
488                 key_total_snt = STORAGE_KEY_CELLULAR_PKT_TOTAL_SNT2;
489                 modem_id = MODEM_ID_SECONDARY;
490         } else {
491                 err("Unhandled CP Name %s", cp_name);
492                 s_indi_assert_not_reached();
493                 goto OUT;
494         }
495
496         /* Free device nodes */
497         g_hash_table_destroy(state_info->device_info);
498         strg_vconf = tcore_server_find_storage(tcore_plugin_ref_server(tcore_object_ref_plugin(state_info->co_ps)), S_INDI_VCONF_STORAGE_NAME);
499         s_indi_assert(NULL != strg_vconf);
500
501         /* Update VCONF before dying */
502         tcore_storage_set_int(strg_vconf, key_total_rcv, tcore_storage_get_int(strg_vconf, key_total_rcv) + state_info->rx_total);
503         tcore_storage_set_int(strg_vconf, key_total_snt, tcore_storage_get_int(strg_vconf, key_total_snt) + state_info->tx_total);
504
505         /* After updating total vconf key, last vconf key needs to be reset */
506         tcore_storage_set_int(strg_vconf, key_last_rcv, 0);
507         tcore_storage_set_int(strg_vconf, key_last_snt, 0);
508
509         s_indi_log_txrx(modem_id, "VCONF TOTAL -RX: [%d] TX: [%d]",
510                         tcore_storage_get_int(strg_vconf, key_total_rcv), tcore_storage_get_int(strg_vconf, key_total_snt));
511
512 OUT:
513         s_indi_free(data);
514 }
515
516 void __s_indi_dev_info_value_destroy_notification(gpointer data)
517 {
518         s_indi_dev_state_info_type *dev_state = data;
519         s_indi_cp_state_info_type *state_info = NULL;
520         s_indi_assert(NULL != dev_state);
521         state_info = dev_state->parent;
522         s_indi_assert(NULL != state_info);
523
524         /* Update parent before dying */
525         state_info->rx_total += dev_state->curr_rx/1000ULL;
526         state_info->tx_total += dev_state->curr_tx/1000ULL;
527
528         dbg("DYING after contributing [RX: %llu][TX: %llu] OUT OF [RX: %llu][TX: %llu]",
529                 dev_state->curr_rx/1000ULL, dev_state->curr_tx/1000ULL,
530                 state_info->rx_total, state_info->tx_total);
531
532         s_indi_free(data);
533 }
534
535 void __s_indi_refresh_modems(TcorePlugin *indi_plugin)
536 {
537         GSList *mp_list = tcore_server_get_modem_plugin_list(tcore_plugin_ref_server(indi_plugin));
538         GSList *tmp;
539         dbg("Processing %u present modems", g_slist_length(mp_list));
540
541         for (tmp = mp_list; tmp; tmp = tmp->next)
542                 __s_indi_add_modem_plugin(indi_plugin, tmp->data);
543
544         g_slist_free(mp_list);
545 }
546
547 void s_indi_storage_key_callback(enum tcore_storage_key key, void *value, void *user_data)
548 {
549         s_indi_cp_state_info_type *state_info = NULL;
550         GVariant *tmp = value;
551         s_indi_private_info *priv_info = __s_indi_get_priv_info(user_data);
552         if (priv_info == NULL) {
553                 err("priv_info is NULL!!!");
554                 return;
555         }
556
557         s_indi_assert(NULL != tmp);
558
559         switch (key) {
560         case STORAGE_KEY_PM_STATE: {
561                 GHashTableIter iter;
562                 gpointer key, value;
563                 gint pm_state = S_INDI_ZERO;
564
565                 if (!g_variant_is_of_type(tmp, G_VARIANT_TYPE_INT32)) {
566                         err("Wrong variant data type");
567                         return;
568                 }
569
570                 pm_state = g_variant_get_int32(tmp);
571
572                 dbg("PM state Value:[%d]", pm_state);
573
574                 g_hash_table_iter_init(&iter, priv_info->state_info);
575                 while (g_hash_table_iter_next(&iter, &key, &value)) {
576                         state_info = value;
577                         state_info->dormant_info.lcd_state = pm_state;
578                 }
579         }
580         break;
581
582         default:
583                 s_indi_assert_not_reached();
584         }
585 }
586
587 enum tcore_hook_return s_indi_on_hook_modem_power(Server *server, CoreObject *source,
588         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data)
589 {
590         struct tnoti_modem_power *modem_power = data;
591         s_indi_assert(modem_power != NULL);
592
593         S_INDI_NOT_USED(server);
594         S_INDI_NOT_USED(command);
595         S_INDI_NOT_USED(data_len);
596
597         CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_MODEM, TCORE_HOOK_RETURN_CONTINUE);
598
599         if (modem_power->state == MODEM_STATE_ERROR) { /* CP reset */
600                 TcorePlugin *indi_plugin = user_data;
601                 const char *cp_name = NULL;
602                 s_indi_cp_state_info_type *state_info = NULL;
603                 s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
604                 if (priv_info == NULL) {
605                         err("priv_info is NULL!!!");
606                         return TCORE_HOOK_RETURN_CONTINUE;
607                 }
608
609                 cp_name = tcore_server_get_cp_name_by_plugin(tcore_object_ref_plugin(source));
610                 s_indi_assert(NULL != cp_name);
611                 s_indi_log_ex(cp_name, "MODEM_STATE_ERROR");
612
613                 if ((state_info = g_hash_table_lookup(priv_info->state_info, cp_name)) == NULL) {
614                         warn("BAILING OUT: [%s] not found", cp_name);
615                         return TCORE_HOOK_RETURN_CONTINUE;
616                 }
617
618                 /* Remove all device states since PS releasing all contexts */
619                 g_hash_table_remove_all(state_info->device_info);
620         }
621
622         return TCORE_HOOK_RETURN_CONTINUE;
623 }
624
625 enum tcore_hook_return s_indi_on_hook_ps_call_status(Server *server, CoreObject *source,
626         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data)
627 {
628         struct tnoti_ps_call_status *cstatus = data;
629         TcorePlugin *indi_plugin = user_data;
630         const char *cp_name = NULL;
631         s_indi_cp_state_info_type *state_info = NULL;
632         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
633         if (priv_info == NULL) {
634                 err("priv_info is NULL!!!");
635                 return TCORE_HOOK_RETURN_CONTINUE;
636         }
637
638         s_indi_assert(cstatus != NULL);
639
640         S_INDI_NOT_USED(command);
641         S_INDI_NOT_USED(data_len);
642
643         CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_PS, TCORE_HOOK_RETURN_CONTINUE);
644
645         cp_name = tcore_server_get_cp_name_by_plugin(tcore_object_ref_plugin(source));
646         s_indi_assert(NULL != cp_name);
647
648         s_indi_log_ex(cp_name, "cid(%d) state(%d) reason(%d)",
649                 cstatus->context_id, cstatus->state, cstatus->result);
650
651         if (cstatus->state == S_INDI_PS_CALL_OK) {
652                 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_OK]");
653                 return TCORE_HOOK_RETURN_CONTINUE;
654         }
655
656         if ((state_info = g_hash_table_lookup(priv_info->state_info, cp_name)) == NULL) {
657                 warn("BAILING OUT: [%s] not found", cp_name);
658                 return TCORE_HOOK_RETURN_CONTINUE;
659         }
660
661         s_indi_assert(source == state_info->co_ps);
662
663         if (cstatus->state == S_INDI_PS_CALL_CONNECT) {
664                 GSList *l_context = NULL;
665                 CoreObject *co_context = NULL;
666                 gchar *dev_name = NULL;
667                 s_indi_dev_state_info_type *dev_state = NULL;
668                 enum tcore_storage_key key_service_state;
669                 Storage *strg_vconf = NULL;
670                 int role = CONTEXT_ROLE_UNKNOWN;
671                 gboolean data_allowed = FALSE;
672                 gboolean roaming_allowed = FALSE;
673
674                 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_CONNECT]");
675
676                 /* Fetch context with internet/tethering role */
677                 l_context = tcore_ps_ref_context_by_id(source, cstatus->context_id);
678                 while (l_context) {
679                         role = tcore_context_get_role(l_context->data);
680                         if (role == CONTEXT_ROLE_INTERNET || role == CONTEXT_ROLE_TETHERING || role == CONTEXT_ROLE_MMS) {
681                                 co_context = l_context->data;
682                                 break;
683                         }
684
685                         l_context = l_context->next;
686                 }
687
688                 if (!co_context) {
689                         err("INTERNET/TETHERING/MMS role not found");
690                         return TCORE_HOOK_RETURN_CONTINUE;
691                 }
692
693                 dev_name = tcore_context_get_ipv4_devname(co_context); /* glib allocator */
694                 s_indi_assert(NULL != dev_name);
695
696                 /* Check if dev_name already exists */
697                 if (g_hash_table_lookup(state_info->device_info, dev_name)) {
698                         dbg("default connection is already connected");
699                         g_free(dev_name);
700                         return TCORE_HOOK_RETURN_CONTINUE;
701                 }
702
703                 /* Update Cellular State */
704                 if (s_indi_str_has_suffix(cp_name, "0")) {
705                         key_service_state = STORAGE_KEY_PACKET_SERVICE_STATE;
706                 } else if (s_indi_str_has_suffix(cp_name, "1")) {
707                         key_service_state = STORAGE_KEY_PACKET_SERVICE_STATE2;
708                 } else {
709                         err("Un-handled CP");
710                         g_free(dev_name);
711                         s_indi_assert_not_reached();
712                         return TCORE_HOOK_RETURN_CONTINUE;
713                 }
714
715                 /* Fresh */
716                 strg_vconf = tcore_server_find_storage(server, S_INDI_VCONF_STORAGE_NAME);
717                 s_indi_assert(NULL != strg_vconf);
718
719                 data_allowed = tcore_storage_get_bool(strg_vconf, STORAGE_KEY_3G_ENABLE);
720                 roaming_allowed = tcore_storage_get_bool(strg_vconf, STORAGE_KEY_SETAPPL_STATE_DATA_ROAMING_BOOL);
721
722                 if (!data_allowed)
723                         return TCORE_HOOK_RETURN_CONTINUE;
724                 else if (state_info->roaming_status && !roaming_allowed)
725                         return TCORE_HOOK_RETURN_CONTINUE;
726
727                 /* Set Cellular state connected */
728                 if (role == CONTEXT_ROLE_INTERNET || role == CONTEXT_ROLE_TETHERING) {
729                         state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
730                         tcore_storage_set_int(strg_vconf, key_service_state, S_INDI_CELLULAR_CONNECTED);
731                 } else if (role == CONTEXT_ROLE_MMS) {
732                         state_info->ps_state = S_INDI_CELLULAR_MMS_CONNECTED;
733                         tcore_storage_set_int(strg_vconf, key_service_state, S_INDI_CELLULAR_MMS_CONNECTED);
734                 }
735
736                 /* Initialize Packet indicator to Normal */
737                 tcore_storage_set_int(strg_vconf, STORAGE_KEY_PACKET_INDICATOR_STATE, S_INDI_TRANSFER_NORMAL);
738                 state_info->cp_trans_state = S_INDI_TRANSFER_NORMAL;
739
740                 /* Create new dev state */
741                 dev_state = __s_indi_alloc_device_state(co_context, state_info);
742                 g_hash_table_insert(state_info->device_info, dev_name, dev_state);
743
744                 /* Start Updater */
745                 __s_indi_start_updater(indi_plugin, s_indi_strdup(cp_name));
746
747         } else if (cstatus->state == S_INDI_PS_CALL_NO_CARRIER) {
748                 gchar *dev_name = NULL;
749                 GSList *l_context = tcore_ps_ref_context_by_id(source, cstatus->context_id);
750
751                 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_NO_CARRIER]");
752
753                 /* Remove all related contexts */
754                 while (l_context) {
755                         dev_name = tcore_context_get_ipv4_devname(l_context->data);
756                         if (dev_name != NULL) {
757                                 g_hash_table_remove(state_info->device_info, dev_name);
758                                 g_free(dev_name);
759                         }
760                         l_context = l_context->next;
761                 }
762         }
763
764         return TCORE_HOOK_RETURN_CONTINUE;
765 }
766
767 enum tcore_hook_return s_indi_on_hook_net_register(Server *server, CoreObject *source,
768                 enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data)
769 {
770         TcorePlugin *indi_plugin = user_data;
771         const char *cp_name = NULL;
772         s_indi_cp_state_info_type *state_info = NULL;
773         gboolean active = FALSE;
774         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
775         gboolean gsm_dtm_support = FALSE;
776         struct tnoti_network_registration_status *regist_status = data;
777         int roaming_status = S_INDI_ZERO;
778
779         S_INDI_NOT_USED(command);
780         S_INDI_NOT_USED(data_len);
781         CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_NETWORK, TCORE_HOOK_RETURN_CONTINUE);
782         if (priv_info == NULL) {
783                 err("priv_info is NULL!!!");
784                 return TCORE_HOOK_RETURN_CONTINUE;
785         }
786
787         cp_name = tcore_server_get_cp_name_by_plugin(tcore_object_ref_plugin(source));
788         s_indi_assert(NULL != cp_name);
789         s_indi_assert(NULL != regist_status);
790
791         if ((state_info = g_hash_table_lookup(priv_info->state_info, cp_name)) == NULL) {
792                 warn("BAILING OUT: [%s] not found", cp_name);
793                 return TCORE_HOOK_RETURN_CONTINUE;
794         }
795
796         roaming_status = state_info->roaming_status = regist_status->roaming_status;
797         gsm_dtm_support = tcore_network_get_gsm_dtm_support(source);
798
799         s_indi_log_ex(cp_name, "roam_status: [0x%x] dtm_support: [0x%x]",
800                 roaming_status, gsm_dtm_support);
801
802         if (gsm_dtm_support)
803                 return TCORE_HOOK_RETURN_CONTINUE;
804
805         active = (g_hash_table_size(state_info->device_info) >= S_INDI_ONE) ? TRUE : FALSE;
806
807         if (active) {
808                 Storage *strg_vconf = NULL;
809                 GSList *co_list = NULL;
810                 CoreObject *co_call = NULL;
811                 unsigned int total_call_cnt = S_INDI_ZERO;
812                 enum telephony_network_service_type svc_type;
813                 enum tcore_storage_key key_service_state;
814                 gboolean roaming_allowed = FALSE;
815
816                 /* VCONF Mapper */
817                 if (s_indi_str_has_suffix(cp_name, "0")) {
818                         key_service_state = STORAGE_KEY_PACKET_SERVICE_STATE;
819                 } else if (s_indi_str_has_suffix(cp_name, "1")) {
820                         key_service_state = STORAGE_KEY_PACKET_SERVICE_STATE2;
821                 } else {
822                         err("Un-handled CP");
823                         s_indi_assert_not_reached();
824                         return TCORE_HOOK_RETURN_CONTINUE;
825                 }
826
827                 strg_vconf = tcore_server_find_storage(server, S_INDI_VCONF_STORAGE_NAME);
828                 roaming_allowed = tcore_storage_get_bool(strg_vconf, STORAGE_KEY_SETAPPL_STATE_DATA_ROAMING_BOOL);
829
830                 svc_type = regist_status->service_type;
831                 s_indi_log_ex(cp_name, "srvc_type(%d), roaming_allowed(%d)",
832                         svc_type, roaming_allowed);
833
834                 /**
835                  * If Roaming is NOT allowed and indication provides Roaming
836                  * status Available, there is a mismatch.
837                  * Set Cellular state OFF.
838                  */
839                 if (state_info->ps_state != S_INDI_CELLULAR_OFF && !roaming_allowed && roaming_status) {
840                         /* Set Cellular State OFF */
841                         tcore_storage_set_int(strg_vconf, key_service_state, S_INDI_CELLULAR_OFF);
842
843                         /*
844                          * Indicator need not know worry about roaming status. packet service plugin
845                          * should take care of de-activating the contexts when roaming is enabled and
846                          * network enters roaming. When all the contexts associated with that network
847                          * is de-activated. Indicator plugin will automatically ps status for that
848                          * CP to S_INDI_CELLULAR_OFF
849                          */
850                         state_info->ps_state = S_INDI_CELLULAR_OFF; /* Update cache */
851
852                         s_indi_log_ex(cp_name, "PS Call status - [DISCONNECTED]");
853                         return TCORE_HOOK_RETURN_CONTINUE;
854                 }
855
856                 if (svc_type < NETWORK_SERVICE_TYPE_2G || svc_type > NETWORK_SERVICE_TYPE_2_5G_EDGE)
857                         return TCORE_HOOK_RETURN_CONTINUE;
858
859                 co_list = tcore_plugin_get_core_objects_bytype(tcore_object_ref_plugin(source), CORE_OBJECT_TYPE_CALL);
860                 if (!co_list) {
861                         err("[ error ] co_list : NULL");
862                         return TCORE_HOOK_RETURN_CONTINUE;
863                 }
864                 s_indi_assert(g_slist_length(co_list) == S_INDI_ONE);
865
866                 co_call = (CoreObject *)co_list->data;
867                 g_slist_free(co_list);
868
869                 total_call_cnt = tcore_call_object_total_length(co_call);
870                 s_indi_log_ex(cp_name, "totall call cnt (%d)", total_call_cnt);
871
872                 if (total_call_cnt > S_INDI_ONE) {
873                         s_indi_cellular_state pkg_state = S_INDI_CELLULAR_UNKNOWN;
874                         pkg_state = tcore_storage_get_int(strg_vconf, key_service_state);
875                         if (pkg_state != S_INDI_CELLULAR_OFF) {
876                                 tcore_storage_set_int(strg_vconf, key_service_state, S_INDI_CELLULAR_OFF);
877                                 state_info->ps_state = S_INDI_CELLULAR_OFF; /* Update cache */
878                         }
879                 }
880         }
881
882         return TCORE_HOOK_RETURN_CONTINUE;
883 }
884
885 gboolean __s_indi_handle_voice_call_status(Server *server, CoreObject *source,
886         enum tcore_notification_command command, const char *cp_name, s_indi_cp_state_info_type *state_info)
887 {
888         CoreObject *co_network = NULL;
889         enum tcore_storage_key vconf_key;
890         Storage *strg_vconf;
891         enum telephony_network_service_type svc_type;
892         gboolean show_icon = TRUE;
893
894         if (g_hash_table_size(state_info->device_info) == S_INDI_ZERO) {
895                 s_indi_log_ex(cp_name, "PS state is already OFF");
896                 return TRUE;
897         }
898
899         co_network = tcore_plugin_ref_core_object(tcore_object_ref_plugin(state_info->co_ps),
900                                         CORE_OBJECT_TYPE_NETWORK);
901
902         if (co_network) {
903                 gboolean gsm_dtm_support = FALSE;
904                 gsm_dtm_support = tcore_network_get_gsm_dtm_support(co_network);
905                 if (gsm_dtm_support) {
906                         s_indi_log_ex(cp_name, "GSM DTM supported! UI need not be synchronized");
907                         return TRUE;
908                 }
909         }
910
911         /* Mapping VCONF keys */
912         if (s_indi_str_has_suffix(cp_name, "0")) {
913                 vconf_key = STORAGE_KEY_PACKET_SERVICE_STATE;
914         } else if (s_indi_str_has_suffix(cp_name, "1")) {
915                 vconf_key = STORAGE_KEY_PACKET_SERVICE_STATE2;
916         } else {
917                 s_indi_assert_not_reached();
918                 return TRUE;
919         }
920
921         tcore_network_get_service_type(co_network, &svc_type);
922
923         switch (svc_type) {
924         case NETWORK_SERVICE_TYPE_2G:
925         case NETWORK_SERVICE_TYPE_2_5G:
926         case NETWORK_SERVICE_TYPE_2_5G_EDGE:
927                 show_icon = FALSE;
928         break;
929
930         case NETWORK_SERVICE_TYPE_3G:
931         case NETWORK_SERVICE_TYPE_HSDPA:
932                 if (tcore_object_ref_plugin(co_network) != tcore_object_ref_plugin(source))
933                         show_icon = FALSE;
934         break;
935
936         default:
937         break;
938         }
939
940         s_indi_log_ex(cp_name, "RAT: [0x%x], ps_state: [0x%x], show_icon[%d]",
941                 svc_type, state_info->ps_state, show_icon);
942
943         if (show_icon == TRUE) {
944                 if (state_info->ps_state == S_INDI_CELLULAR_OFF) {
945                         state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
946                         goto OUT;
947                 }
948                 return TRUE;
949         }
950
951         switch (command) {
952         case TNOTI_CALL_STATUS_IDLE: {
953                 int total_call_cnt = S_INDI_ZERO;
954                 total_call_cnt = tcore_call_object_total_length(source);
955                 if (total_call_cnt > S_INDI_ONE) {
956                         s_indi_log_ex(cp_name, "Call is still connected");
957                         return TRUE;
958                 }
959                 state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
960         }
961         break;
962
963         case TNOTI_CALL_STATUS_DIALING:
964         case TNOTI_CALL_STATUS_INCOMING:
965         case TNOTI_CALL_STATUS_ACTIVE: {
966                 state_info->ps_state = S_INDI_CELLULAR_OFF;
967         }
968         break;
969
970         default: {
971                 s_indi_log_ex(cp_name, "Unexpected command: [0x%x]", command);
972                 s_indi_assert_not_reached();
973                 return TRUE;
974         }
975         }
976
977 OUT:
978         /* Update PS Call state */
979         strg_vconf = tcore_server_find_storage(server, S_INDI_VCONF_STORAGE_NAME);
980         if (state_info->ps_state != tcore_storage_get_int(strg_vconf, vconf_key)) {
981                 tcore_storage_set_int(strg_vconf, vconf_key, state_info->ps_state);
982                 s_indi_log_ex(cp_name, "PS Call status - [%s]",
983                         (state_info->ps_state == S_INDI_CELLULAR_CONNECTED ? "CONNECTED"
984                         : (state_info->ps_state == S_INDI_CELLULAR_OFF ? "DISCONNECTED"
985                         : (state_info->ps_state == S_INDI_CELLULAR_USING ? "IN USE" : "UNKNOWN"))));
986         }
987
988         return TRUE;
989 }
990
991 enum tcore_hook_return s_indi_on_hook_voice_call_status(Server *server, CoreObject *source,
992         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data)
993 {
994         TcorePlugin *indi_plugin = user_data;
995         s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
996         const char *cp_name = NULL;
997         s_indi_cp_state_info_type *state_info = NULL;
998
999         GHashTableIter iter;
1000         gpointer key, value;
1001
1002         S_INDI_NOT_USED(data_len);
1003         S_INDI_NOT_USED(data);
1004
1005         CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_CALL, TCORE_HOOK_RETURN_CONTINUE);
1006         if (priv_info == NULL) {
1007                 err("priv_info is NULL!!!");
1008                 return TCORE_HOOK_RETURN_CONTINUE;
1009         }
1010
1011         /* Update all modem states */
1012         g_hash_table_iter_init(&iter, priv_info->state_info);
1013         while (g_hash_table_iter_next(&iter, &key, &value)) {
1014                 cp_name = key;
1015                 state_info = value;
1016
1017                 (void)__s_indi_handle_voice_call_status(server, source, command,
1018                                 cp_name, state_info);
1019         }
1020
1021         return TCORE_HOOK_RETURN_CONTINUE;
1022 }
1023
1024 enum tcore_hook_return s_indi_on_hook_modem_plugin_added(Server *server, CoreObject *source,
1025         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data)
1026 {
1027         s_indi_assert(NULL != data);
1028         s_indi_assert(NULL != user_data);
1029
1030         S_INDI_NOT_USED(server);
1031         S_INDI_NOT_USED(source);
1032         S_INDI_NOT_USED(command);
1033         S_INDI_NOT_USED(data_len);
1034
1035         __s_indi_add_modem_plugin(user_data, data);
1036
1037         return TCORE_HOOK_RETURN_CONTINUE;
1038 }
1039
1040 enum tcore_hook_return s_indi_on_hook_modem_plugin_removed(Server *server, CoreObject *source,
1041         enum tcore_notification_command command, unsigned int data_len, void *data, void *user_data)
1042 {
1043         s_indi_assert(NULL != data);
1044         s_indi_assert(NULL != user_data);
1045
1046         S_INDI_NOT_USED(server);
1047         S_INDI_NOT_USED(source);
1048         S_INDI_NOT_USED(command);
1049         S_INDI_NOT_USED(data_len);
1050
1051         __s_indi_remove_modem_plugin(user_data, data);
1052
1053         return TCORE_HOOK_RETURN_CONTINUE;
1054 }
1055
1056 gboolean s_indi_init(TcorePlugin *plugin)
1057 {
1058         Server *server = NULL;
1059         s_indi_private_info *priv_info = NULL;
1060
1061         priv_info = s_indi_malloc0(sizeof(*priv_info));
1062         if (priv_info == NULL) {
1063                 err("Memory allocation failed!!");
1064                 return FALSE;
1065         }
1066         if (tcore_plugin_link_user_data(plugin, priv_info) != TCORE_RETURN_SUCCESS) {
1067                 err("Failed to link private data");
1068                 s_indi_free(priv_info);
1069                 return FALSE;
1070         }
1071
1072         server = tcore_plugin_ref_server(plugin);
1073
1074         /* Initialize VCONF => CP_NAME mapping */
1075         priv_info->vconf_info = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, s_indi_free_func);
1076
1077         /* Initialize State Information */
1078         priv_info->state_info = g_hash_table_new_full(g_str_hash, g_str_equal,
1079                 s_indi_free_func, __s_indi_state_info_value_destroy_notification);
1080         if (priv_info->state_info == NULL
1081                 || priv_info->vconf_info == NULL) {
1082                 err("Memory allocation problem! Bailing Out");
1083                 goto OUT;
1084         }
1085
1086         /* Register vconf key callbacks */
1087         __s_indi_register_vconf_key(STORAGE_KEY_PM_STATE, plugin, NULL);
1088
1089         /* Server Hooks*/
1090         tcore_server_add_notification_hook(server, TNOTI_MODEM_POWER, s_indi_on_hook_modem_power, plugin);
1091         tcore_server_add_notification_hook(server, TNOTI_PS_CALL_STATUS, s_indi_on_hook_ps_call_status, plugin);
1092         tcore_server_add_notification_hook(server, TNOTI_NETWORK_REGISTRATION_STATUS, s_indi_on_hook_net_register, plugin);
1093
1094         /* For 2G PS suspend/resume */
1095         tcore_server_add_notification_hook(server, TNOTI_CALL_STATUS_IDLE, s_indi_on_hook_voice_call_status, plugin);
1096         tcore_server_add_notification_hook(server, TNOTI_CALL_STATUS_DIALING, s_indi_on_hook_voice_call_status, plugin);
1097         tcore_server_add_notification_hook(server, TNOTI_CALL_STATUS_INCOMING, s_indi_on_hook_voice_call_status, plugin);
1098         tcore_server_add_notification_hook(server, TNOTI_CALL_STATUS_ACTIVE, s_indi_on_hook_voice_call_status, plugin);
1099
1100         /* For new Modems */
1101         tcore_server_add_notification_hook(server, TNOTI_SERVER_ADDED_MODEM_PLUGIN, s_indi_on_hook_modem_plugin_added, plugin);
1102         tcore_server_add_notification_hook(server, TNOTI_SERVER_REMOVED_MODEM_PLUGIN, s_indi_on_hook_modem_plugin_removed, plugin);
1103
1104         /* Add existing Modems */
1105         __s_indi_refresh_modems(plugin);
1106
1107         return TRUE;
1108
1109 OUT:
1110         s_indi_deinit(plugin);
1111         return FALSE;
1112 }
1113
1114 void s_indi_deinit(TcorePlugin *plugin)
1115 {
1116         Server *server = NULL;
1117         GList *iter;
1118         s_indi_cp_state_info_type *state_info = NULL;
1119         TcorePlugin *modem_plugin = NULL;
1120         s_indi_private_info *priv_info = __s_indi_get_priv_info(plugin);
1121
1122         /* Remove Hooks */
1123         server = tcore_plugin_ref_server(plugin);
1124         tcore_server_remove_notification_hook(server, s_indi_on_hook_modem_power);
1125         tcore_server_remove_notification_hook(server, s_indi_on_hook_ps_call_status);
1126         tcore_server_remove_notification_hook(server, s_indi_on_hook_net_register);
1127         tcore_server_remove_notification_hook(server, s_indi_on_hook_voice_call_status);
1128         tcore_server_remove_notification_hook(server, s_indi_on_hook_modem_plugin_added);
1129         tcore_server_remove_notification_hook(server, s_indi_on_hook_modem_plugin_removed);
1130
1131         /* Remove key callback */
1132         __s_indi_unregister_vconf_key(STORAGE_KEY_PM_STATE, plugin, NULL);
1133
1134         /* Destroy all watched modems */
1135         if (priv_info == NULL) {
1136                 err("priv_info is NULL!!!");
1137                 return;
1138         }
1139         iter = g_hash_table_get_values(priv_info->state_info);
1140         while (iter) {
1141                 state_info = iter->data;
1142                 modem_plugin = tcore_object_ref_plugin(state_info->co_ps);
1143                 __s_indi_remove_modem_plugin(plugin, modem_plugin);
1144
1145                 iter = g_list_delete_link(iter, iter);
1146         }
1147
1148         /* Decrement hash table reference */
1149         g_hash_table_destroy(priv_info->state_info);
1150         g_hash_table_destroy(priv_info->vconf_info);
1151
1152         /* Finalize */
1153         s_indi_free(priv_info);
1154         tcore_plugin_link_user_data(plugin, NULL);
1155 }