4 * Copyright (c) 2014 Samsung Electronics Co. Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
32 #include <co_context.h>
34 #include <co_network.h>
37 #include "s_indi_main.h"
38 #include "s_indi_util.h"
39 #include "s_indi_log.h"
41 #define S_INDI_UPDATE_INTERVAL 1
42 #define S_INDI_PROC_FILE "/proc/net/dev"
44 #define S_INDI_VCONF_STORAGE_NAME "vconf"
46 #define S_INDI_ALLOC_USER_DATA(data, plugin, cp) \
48 data = s_indi_malloc0(sizeof(__s_indi_cb_user_data)); \
50 err("Memory allocation failed!!"); \
54 data->indi_plugin = plugin; \
58 #define S_INDI_FREE_USER_DATA(data) \
60 s_indi_free(data->cp_name); \
65 TcorePlugin *indi_plugin;
67 } __s_indi_cb_user_data;
70 GHashTable *state_info; /* HashTable of s_indi_cp_state_info_type with key = cp_name */
72 GHashTable *vconf_info; /* Mapping of enum tcore_storage_key to cp_name */
73 } s_indi_private_info;
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);
89 /***************** VCONF Callbacks *****************/
90 static void s_indi_storage_key_callback(enum tcore_storage_key key, void *value, void *user_data);
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);
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);
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);
113 void __s_indi_register_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name)
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!!!");
121 s_indi_assert(NULL != strg_vconf);
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));
129 void __s_indi_unregister_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name)
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!!!");
137 s_indi_assert(NULL != strg_vconf);
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));
145 void __s_indi_add_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin)
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!!!");
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");
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)));
163 void __s_indi_remove_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin)
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!!!");
171 s_indi_assert(NULL != priv_info->state_info);
173 cp_name = tcore_server_get_cp_name_by_plugin(modem_plugin);
174 s_indi_assert(NULL != cp_name);
176 if (g_hash_table_remove(priv_info->state_info, cp_name))
177 s_indi_log_ex(cp_name, "Removed");
180 CoreObject *__s_indi_fetch_ps_co(TcorePlugin *plugin)
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);
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);
194 s_indi_cp_state_info_type *__s_indi_alloc_state_info(CoreObject *co_ps)
196 s_indi_cp_state_info_type *state_info = s_indi_malloc0(sizeof(s_indi_cp_state_info_type));
198 err("Memory allocation failed!!");
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;
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);
215 s_indi_dev_state_info_type *__s_indi_alloc_device_state(CoreObject *ps_context, s_indi_cp_state_info_type *parent)
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!!");
223 dev_state->ps_context = ps_context;
224 dev_state->parent = parent;
228 s_indi_private_info *__s_indi_get_priv_info(TcorePlugin *plugin)
230 s_indi_private_info *priv_info = tcore_plugin_ref_user_data(plugin);
231 s_indi_assert(NULL != priv_info);
235 gboolean __s_indi_start_updater(TcorePlugin *indi_plugin, gchar *cp_name)
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!!!");
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);
251 if (state_info->src != NULL) {
252 dbg("Another one is in progress");
253 s_indi_free(cp_name);
257 S_INDI_ALLOC_USER_DATA(cb_data, indi_plugin, cp_name);
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);
268 gboolean __s_indi_update_callback(__s_indi_cb_user_data *data)
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;
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;
286 if (priv_info == NULL) {
287 err("priv_info is NULL!!!");
288 return G_SOURCE_REMOVE;
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;
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;
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);
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");
325 /** @todo: Check if needs to be read atomically */
326 pf = fopen(S_INDI_PROC_FILE, "r");
328 err("indicator fail to open file(%s), errno(%d)", S_INDI_PROC_FILE, errno);
332 /* Skip first line */
333 rv = fgets(buff, sizeof(buff), pf);
335 err("fail to read file or reach EOF, plz check %s", S_INDI_PROC_FILE);
339 /* Skip second line */
340 rv = fgets(buff, sizeof(buff), pf);
342 err("fail to read file or reach EOF, plz check %s", S_INDI_PROC_FILE);
346 /* Update all devices of state_info */
347 while (fgets(buff, sizeof(buff), pf)) {
348 gchar *ifname = buff, *entry = NULL;
350 /* Skip whitespaces */
351 while (*ifname == ' ')
354 /* Terminate to read ifname */
355 entry = strrchr(ifname, ':');
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 ? */
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;
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;
394 rx_changes_total = rx_curr_total - rx_prev_total;
395 tx_changes_total = tx_curr_total - tx_prev_total;
397 if (rx_changes_total)
398 cp_state |= S_INDI_TRANSFER_RX;
400 if (tx_changes_total)
401 cp_state |= S_INDI_TRANSFER_TX;
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);
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);
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));
419 s_indi_log_txrx(modem_id, "VCONF LAST- RX: [%d] TX: [%d]", (int)(rx_curr_total/1000ULL), (int)(tx_curr_total/1000ULL));
425 return G_SOURCE_CONTINUE; /* Revisit after S_INDI_UPDATE_INTERVAL */
428 dbg("indicator is stopped");
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);
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);
440 state_info->src = NULL;
441 state_info->cp_trans_state = S_INDI_TRANSFER_NORMAL;
442 state_info->ps_state = S_INDI_CELLULAR_OFF;
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);
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);
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;
459 S_INDI_FREE_USER_DATA(data);
460 return G_SOURCE_REMOVE;
463 void __s_indi_state_info_value_destroy_notification(gpointer data)
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);
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);
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;
491 err("Unhandled CP Name %s", cp_name);
492 s_indi_assert_not_reached();
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);
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);
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);
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));
516 void __s_indi_dev_info_value_destroy_notification(gpointer data)
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);
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;
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);
535 void __s_indi_refresh_modems(TcorePlugin *indi_plugin)
537 GSList *mp_list = tcore_server_get_modem_plugin_list(tcore_plugin_ref_server(indi_plugin));
539 dbg("Processing %u present modems", g_slist_length(mp_list));
541 for (tmp = mp_list; tmp; tmp = tmp->next)
542 __s_indi_add_modem_plugin(indi_plugin, tmp->data);
544 g_slist_free(mp_list);
547 void s_indi_storage_key_callback(enum tcore_storage_key key, void *value, void *user_data)
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!!!");
557 s_indi_assert(NULL != tmp);
560 case STORAGE_KEY_PM_STATE: {
563 gint pm_state = S_INDI_ZERO;
565 if (!g_variant_is_of_type(tmp, G_VARIANT_TYPE_INT32)) {
566 err("Wrong variant data type");
570 pm_state = g_variant_get_int32(tmp);
572 dbg("PM state Value:[%d]", pm_state);
574 g_hash_table_iter_init(&iter, priv_info->state_info);
575 while (g_hash_table_iter_next(&iter, &key, &value)) {
577 state_info->dormant_info.lcd_state = pm_state;
583 s_indi_assert_not_reached();
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)
590 struct tnoti_modem_power *modem_power = data;
591 s_indi_assert(modem_power != NULL);
593 S_INDI_NOT_USED(server);
594 S_INDI_NOT_USED(command);
595 S_INDI_NOT_USED(data_len);
597 CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_MODEM, TCORE_HOOK_RETURN_CONTINUE);
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;
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");
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;
618 /* Remove all device states since PS releasing all contexts */
619 g_hash_table_remove_all(state_info->device_info);
622 return TCORE_HOOK_RETURN_CONTINUE;
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)
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;
638 s_indi_assert(cstatus != NULL);
640 S_INDI_NOT_USED(command);
641 S_INDI_NOT_USED(data_len);
643 CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_PS, TCORE_HOOK_RETURN_CONTINUE);
645 cp_name = tcore_server_get_cp_name_by_plugin(tcore_object_ref_plugin(source));
646 s_indi_assert(NULL != cp_name);
648 s_indi_log_ex(cp_name, "cid(%d) state(%d) reason(%d)",
649 cstatus->context_id, cstatus->state, cstatus->result);
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;
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;
661 s_indi_assert(source == state_info->co_ps);
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;
674 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_CONNECT]");
676 /* Fetch context with internet/tethering role */
677 l_context = tcore_ps_ref_context_by_id(source, cstatus->context_id);
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;
685 l_context = l_context->next;
689 err("INTERNET/TETHERING/MMS role not found");
690 return TCORE_HOOK_RETURN_CONTINUE;
693 dev_name = tcore_context_get_ipv4_devname(co_context); /* glib allocator */
694 s_indi_assert(NULL != dev_name);
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");
700 return TCORE_HOOK_RETURN_CONTINUE;
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;
709 err("Un-handled CP");
711 s_indi_assert_not_reached();
712 return TCORE_HOOK_RETURN_CONTINUE;
716 strg_vconf = tcore_server_find_storage(server, S_INDI_VCONF_STORAGE_NAME);
717 s_indi_assert(NULL != strg_vconf);
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);
723 return TCORE_HOOK_RETURN_CONTINUE;
724 else if (state_info->roaming_status && !roaming_allowed)
725 return TCORE_HOOK_RETURN_CONTINUE;
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);
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;
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);
745 __s_indi_start_updater(indi_plugin, s_indi_strdup(cp_name));
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);
751 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_NO_CARRIER]");
753 /* Remove all related contexts */
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);
760 l_context = l_context->next;
764 return TCORE_HOOK_RETURN_CONTINUE;
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)
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;
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;
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);
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;
796 roaming_status = state_info->roaming_status = regist_status->roaming_status;
797 gsm_dtm_support = tcore_network_get_gsm_dtm_support(source);
799 s_indi_log_ex(cp_name, "roam_status: [0x%x] dtm_support: [0x%x]",
800 roaming_status, gsm_dtm_support);
803 return TCORE_HOOK_RETURN_CONTINUE;
805 active = (g_hash_table_size(state_info->device_info) >= S_INDI_ONE) ? TRUE : FALSE;
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;
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;
822 err("Un-handled CP");
823 s_indi_assert_not_reached();
824 return TCORE_HOOK_RETURN_CONTINUE;
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);
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);
835 * If Roaming is NOT allowed and indication provides Roaming
836 * status Available, there is a mismatch.
837 * Set Cellular state OFF.
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);
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
850 state_info->ps_state = S_INDI_CELLULAR_OFF; /* Update cache */
852 s_indi_log_ex(cp_name, "PS Call status - [DISCONNECTED]");
853 return TCORE_HOOK_RETURN_CONTINUE;
856 if (svc_type < NETWORK_SERVICE_TYPE_2G || svc_type > NETWORK_SERVICE_TYPE_2_5G_EDGE)
857 return TCORE_HOOK_RETURN_CONTINUE;
859 co_list = tcore_plugin_get_core_objects_bytype(tcore_object_ref_plugin(source), CORE_OBJECT_TYPE_CALL);
861 err("[ error ] co_list : NULL");
862 return TCORE_HOOK_RETURN_CONTINUE;
864 s_indi_assert(g_slist_length(co_list) == S_INDI_ONE);
866 co_call = (CoreObject *)co_list->data;
867 g_slist_free(co_list);
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);
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 */
882 return TCORE_HOOK_RETURN_CONTINUE;
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)
888 CoreObject *co_network = NULL;
889 enum tcore_storage_key vconf_key;
891 enum telephony_network_service_type svc_type;
892 gboolean show_icon = TRUE;
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");
899 co_network = tcore_plugin_ref_core_object(tcore_object_ref_plugin(state_info->co_ps),
900 CORE_OBJECT_TYPE_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");
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;
917 s_indi_assert_not_reached();
921 tcore_network_get_service_type(co_network, &svc_type);
924 case NETWORK_SERVICE_TYPE_2G:
925 case NETWORK_SERVICE_TYPE_2_5G:
926 case NETWORK_SERVICE_TYPE_2_5G_EDGE:
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))
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);
943 if (show_icon == TRUE) {
944 if (state_info->ps_state == S_INDI_CELLULAR_OFF) {
945 state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
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");
959 state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
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;
971 s_indi_log_ex(cp_name, "Unexpected command: [0x%x]", command);
972 s_indi_assert_not_reached();
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"))));
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)
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;
1000 gpointer key, value;
1002 S_INDI_NOT_USED(data_len);
1003 S_INDI_NOT_USED(data);
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;
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)) {
1017 (void)__s_indi_handle_voice_call_status(server, source, command,
1018 cp_name, state_info);
1021 return TCORE_HOOK_RETURN_CONTINUE;
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)
1027 s_indi_assert(NULL != data);
1028 s_indi_assert(NULL != user_data);
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);
1035 __s_indi_add_modem_plugin(user_data, data);
1037 return TCORE_HOOK_RETURN_CONTINUE;
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)
1043 s_indi_assert(NULL != data);
1044 s_indi_assert(NULL != user_data);
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);
1051 __s_indi_remove_modem_plugin(user_data, data);
1053 return TCORE_HOOK_RETURN_CONTINUE;
1056 gboolean s_indi_init(TcorePlugin *plugin)
1058 Server *server = NULL;
1059 s_indi_private_info *priv_info = NULL;
1061 priv_info = s_indi_malloc0(sizeof(*priv_info));
1062 if (priv_info == NULL) {
1063 err("Memory allocation failed!!");
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);
1072 server = tcore_plugin_ref_server(plugin);
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);
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");
1086 /* Register vconf key callbacks */
1087 __s_indi_register_vconf_key(STORAGE_KEY_PM_STATE, plugin, NULL);
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);
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);
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);
1104 /* Add existing Modems */
1105 __s_indi_refresh_modems(plugin);
1110 s_indi_deinit(plugin);
1114 void s_indi_deinit(TcorePlugin *plugin)
1116 Server *server = NULL;
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);
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);
1131 /* Remove key callback */
1132 __s_indi_unregister_vconf_key(STORAGE_KEY_PM_STATE, plugin, NULL);
1134 /* Destroy all watched modems */
1135 if (priv_info == NULL) {
1136 err("priv_info is NULL!!!");
1139 iter = g_hash_table_get_values(priv_info->state_info);
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);
1145 iter = g_list_delete_link(iter, iter);
1148 /* Decrement hash table reference */
1149 g_hash_table_destroy(priv_info->state_info);
1150 g_hash_table_destroy(priv_info->vconf_info);
1153 s_indi_free(priv_info);
1154 tcore_plugin_link_user_data(plugin, NULL);