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 s_indi_assert(NULL != strg_vconf);
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));
125 void __s_indi_unregister_vconf_key(enum tcore_storage_key key, TcorePlugin *indi_plugin, const char *cp_name)
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);
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));
137 void __s_indi_add_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin)
139 gchar *cp_name = NULL;
140 s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
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");
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)));
151 void __s_indi_remove_modem_plugin(TcorePlugin *indi_plugin, TcorePlugin *modem_plugin)
153 const char *cp_name = NULL;
154 s_indi_private_info *priv_info = __s_indi_get_priv_info(indi_plugin);
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);
160 if (g_hash_table_remove(priv_info->state_info, cp_name))
161 s_indi_log_ex(cp_name, "Removed");
164 CoreObject *__s_indi_fetch_ps_co(TcorePlugin *plugin)
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);
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);
178 s_indi_cp_state_info_type *__s_indi_alloc_state_info(CoreObject *co_ps)
180 s_indi_cp_state_info_type *state_info = s_indi_malloc0(sizeof(s_indi_cp_state_info_type));
182 err("Memory allocation failed!!");
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;
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);
199 s_indi_dev_state_info_type *__s_indi_alloc_device_state(CoreObject *ps_context, s_indi_cp_state_info_type *parent)
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!!");
207 dev_state->ps_context = ps_context;
208 dev_state->parent = parent;
212 s_indi_private_info *__s_indi_get_priv_info(TcorePlugin *plugin)
214 s_indi_private_info *priv_info = tcore_plugin_ref_user_data(plugin);
215 s_indi_assert(NULL != priv_info);
219 gboolean __s_indi_start_updater(TcorePlugin *indi_plugin, gchar *cp_name)
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);
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);
231 if (state_info->src != NULL) {
232 dbg("Another one is in progress");
233 s_indi_free(cp_name);
237 S_INDI_ALLOC_USER_DATA(cb_data, indi_plugin, cp_name);
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);
248 gboolean __s_indi_update_callback(__s_indi_cb_user_data *data)
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;
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;
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;
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;
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);
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");
300 /** @todo: Check if needs to be read atomically */
301 pf = fopen(S_INDI_PROC_FILE, "r");
303 err("indicator fail to open file(%s), errno(%d)", S_INDI_PROC_FILE, errno);
307 /* Skip first line */
308 rv = fgets(buff, sizeof(buff), pf);
310 err("fail to read file or reach EOF, plz check %s", S_INDI_PROC_FILE);
314 /* Skip second line */
315 rv = fgets(buff, sizeof(buff), pf);
317 err("fail to read file or reach EOF, plz check %s", S_INDI_PROC_FILE);
321 /* Update all devices of state_info */
322 while (fgets(buff, sizeof(buff), pf)) {
323 gchar *ifname = buff, *entry = NULL;
325 /* Skip whitespaces */
326 while (*ifname == ' ')
329 /* Terminate to read ifname */
330 entry = strrchr(ifname, ':');
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 ? */
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;
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;
369 rx_changes_total = rx_curr_total - rx_prev_total;
370 tx_changes_total = tx_curr_total - tx_prev_total;
372 if (rx_changes_total)
373 cp_state |= S_INDI_TRANSFER_RX;
375 if (tx_changes_total)
376 cp_state |= S_INDI_TRANSFER_TX;
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);
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);
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));
394 s_indi_log_txrx(modem_id, "VCONF LAST- RX: [%d] TX: [%d]", (int)(rx_curr_total/1000ULL), (int)(tx_curr_total/1000ULL));
400 return G_SOURCE_CONTINUE; /* Revisit after S_INDI_UPDATE_INTERVAL */
403 dbg("indicator is stopped");
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);
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);
415 state_info->src = NULL;
416 state_info->cp_trans_state = S_INDI_TRANSFER_NORMAL;
417 state_info->ps_state = S_INDI_CELLULAR_OFF;
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);
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);
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;
434 S_INDI_FREE_USER_DATA(data);
435 return G_SOURCE_REMOVE;
438 void __s_indi_state_info_value_destroy_notification(gpointer data)
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);
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);
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;
466 err("Unhandled CP Name %s", cp_name);
467 s_indi_assert_not_reached();
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);
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);
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);
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));
491 void __s_indi_dev_info_value_destroy_notification(gpointer data)
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);
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;
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);
510 void __s_indi_refresh_modems(TcorePlugin *indi_plugin)
512 GSList *mp_list = tcore_server_get_modem_plugin_list(tcore_plugin_ref_server(indi_plugin));
514 dbg("Processing %u present modems", g_slist_length(mp_list));
516 for (tmp = mp_list; tmp; tmp = tmp->next)
517 __s_indi_add_modem_plugin(indi_plugin, tmp->data);
519 g_slist_free(mp_list);
522 void s_indi_storage_key_callback(enum tcore_storage_key key, void *value, void *user_data)
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);
528 s_indi_assert(NULL != tmp);
531 case STORAGE_KEY_PM_STATE: {
534 gint pm_state = S_INDI_ZERO;
536 if (!g_variant_is_of_type(tmp, G_VARIANT_TYPE_INT32)) {
537 err("Wrong variant data type");
541 pm_state = g_variant_get_int32(tmp);
543 dbg("PM state Value:[%d]", pm_state);
545 g_hash_table_iter_init(&iter, priv_info->state_info);
546 while (g_hash_table_iter_next(&iter, &key, &value)) {
548 state_info->dormant_info.lcd_state = pm_state;
554 s_indi_assert_not_reached();
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)
561 struct tnoti_modem_power *modem_power = data;
562 s_indi_assert(modem_power != NULL);
564 S_INDI_NOT_USED(server);
565 S_INDI_NOT_USED(command);
566 S_INDI_NOT_USED(data_len);
568 CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_MODEM, TCORE_HOOK_RETURN_CONTINUE);
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);
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");
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;
585 /* Remove all device states since PS releasing all contexts */
586 g_hash_table_remove_all(state_info->device_info);
589 return TCORE_HOOK_RETURN_CONTINUE;
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)
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);
601 s_indi_assert(cstatus != NULL);
603 S_INDI_NOT_USED(command);
604 S_INDI_NOT_USED(data_len);
606 CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_PS, TCORE_HOOK_RETURN_CONTINUE);
608 cp_name = tcore_server_get_cp_name_by_plugin(tcore_object_ref_plugin(source));
609 s_indi_assert(NULL != cp_name);
611 s_indi_log_ex(cp_name, "cid(%d) state(%d) reason(%d)",
612 cstatus->context_id, cstatus->state, cstatus->result);
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;
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;
624 s_indi_assert(source == state_info->co_ps);
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;
637 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_CONNECT]");
639 /* Fetch context with internet/tethering role */
640 l_context = tcore_ps_ref_context_by_id(source, cstatus->context_id);
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;
648 l_context = l_context->next;
652 err("INTERNET/TETHERING/MMS role not found");
653 return TCORE_HOOK_RETURN_CONTINUE;
656 dev_name = tcore_context_get_ipv4_devname(co_context); /* glib allocator */
657 s_indi_assert(NULL != dev_name);
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");
663 return TCORE_HOOK_RETURN_CONTINUE;
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;
672 err("Un-handled CP");
674 s_indi_assert_not_reached();
675 return TCORE_HOOK_RETURN_CONTINUE;
679 strg_vconf = tcore_server_find_storage(server, S_INDI_VCONF_STORAGE_NAME);
680 s_indi_assert(NULL != strg_vconf);
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);
686 return TCORE_HOOK_RETURN_CONTINUE;
687 else if (state_info->roaming_status && !roaming_allowed)
688 return TCORE_HOOK_RETURN_CONTINUE;
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);
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;
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);
708 __s_indi_start_updater(indi_plugin, s_indi_strdup(cp_name));
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);
714 s_indi_log_ex(cp_name, "PS Call state - [PS_CALL_NO_CARRIER]");
716 /* Remove all related contexts */
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);
723 l_context = l_context->next;
727 return TCORE_HOOK_RETURN_CONTINUE;
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)
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;
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);
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);
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;
755 roaming_status = state_info->roaming_status = regist_status->roaming_status;
756 gsm_dtm_support = tcore_network_get_gsm_dtm_support(source);
758 s_indi_log_ex(cp_name, "roam_status: [0x%x] dtm_support: [0x%x]",
759 roaming_status, gsm_dtm_support);
762 return TCORE_HOOK_RETURN_CONTINUE;
764 active = (g_hash_table_size(state_info->device_info) >= S_INDI_ONE) ? TRUE : FALSE;
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;
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;
781 err("Un-handled CP");
782 s_indi_assert_not_reached();
783 return TCORE_HOOK_RETURN_CONTINUE;
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);
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);
794 * If Roaming is NOT allowed and indication provides Roaming
795 * status Available, there is a mismatch.
796 * Set Cellular state OFF.
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);
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
809 state_info->ps_state = S_INDI_CELLULAR_OFF; /* Update cache */
811 s_indi_log_ex(cp_name, "PS Call status - [DISCONNECTED]");
812 return TCORE_HOOK_RETURN_CONTINUE;
815 if (svc_type < NETWORK_SERVICE_TYPE_2G || svc_type > NETWORK_SERVICE_TYPE_2_5G_EDGE)
816 return TCORE_HOOK_RETURN_CONTINUE;
818 co_list = tcore_plugin_get_core_objects_bytype(tcore_object_ref_plugin(source), CORE_OBJECT_TYPE_CALL);
820 err("[ error ] co_list : NULL");
821 return TCORE_HOOK_RETURN_CONTINUE;
823 s_indi_assert(g_slist_length(co_list) == S_INDI_ONE);
825 co_call = (CoreObject *)co_list->data;
826 g_slist_free(co_list);
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);
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 */
841 return TCORE_HOOK_RETURN_CONTINUE;
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)
847 CoreObject *co_network = NULL;
848 enum tcore_storage_key vconf_key;
850 enum telephony_network_service_type svc_type;
851 gboolean show_icon = TRUE;
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");
858 co_network = tcore_plugin_ref_core_object(tcore_object_ref_plugin(state_info->co_ps),
859 CORE_OBJECT_TYPE_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");
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;
876 s_indi_assert_not_reached();
880 tcore_network_get_service_type(co_network, &svc_type);
883 case NETWORK_SERVICE_TYPE_2G:
884 case NETWORK_SERVICE_TYPE_2_5G:
885 case NETWORK_SERVICE_TYPE_2_5G_EDGE:
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))
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);
902 if (show_icon == TRUE) {
903 if (state_info->ps_state == S_INDI_CELLULAR_OFF) {
904 state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
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");
918 state_info->ps_state = S_INDI_CELLULAR_CONNECTED;
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;
930 s_indi_log_ex(cp_name, "Unexpected command: [0x%x]", command);
931 s_indi_assert_not_reached();
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"))));
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)
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;
961 S_INDI_NOT_USED(data_len);
962 S_INDI_NOT_USED(data);
964 CORE_OBJECT_CHECK_RETURN(source, CORE_OBJECT_TYPE_CALL, TCORE_HOOK_RETURN_CONTINUE);
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)) {
972 (void)__s_indi_handle_voice_call_status(server, source, command,
973 cp_name, state_info);
976 return TCORE_HOOK_RETURN_CONTINUE;
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)
982 s_indi_assert(NULL != data);
983 s_indi_assert(NULL != user_data);
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);
990 __s_indi_add_modem_plugin(user_data, data);
992 return TCORE_HOOK_RETURN_CONTINUE;
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)
998 s_indi_assert(NULL != data);
999 s_indi_assert(NULL != user_data);
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);
1006 __s_indi_remove_modem_plugin(user_data, data);
1008 return TCORE_HOOK_RETURN_CONTINUE;
1011 gboolean s_indi_init(TcorePlugin *plugin)
1013 Server *server = NULL;
1014 s_indi_private_info *priv_info = NULL;
1016 priv_info = s_indi_malloc0(sizeof(*priv_info));
1017 if (priv_info == NULL) {
1018 err("Memory allocation failed!!");
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);
1027 server = tcore_plugin_ref_server(plugin);
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);
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");
1041 /* Register vconf key callbacks */
1042 __s_indi_register_vconf_key(STORAGE_KEY_PM_STATE, plugin, NULL);
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);
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);
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);
1059 /* Add existing Modems */
1060 __s_indi_refresh_modems(plugin);
1065 s_indi_deinit(plugin);
1069 void s_indi_deinit(TcorePlugin *plugin)
1071 Server *server = NULL;
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);
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);
1086 /* Remove key callback */
1087 __s_indi_unregister_vconf_key(STORAGE_KEY_PM_STATE, plugin, NULL);
1089 /* Destroy all watched modems */
1090 iter = g_hash_table_get_values(priv_info->state_info);
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);
1096 iter = g_list_delete_link(iter, iter);
1099 /* Decrement hash table reference */
1100 g_hash_table_destroy(priv_info->state_info);
1101 g_hash_table_destroy(priv_info->vconf_info);
1104 s_indi_free(priv_info);
1105 tcore_plugin_link_user_data(plugin, NULL);