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