Modify poweroff signal handling
[platform/core/telephony/tel-plugin-manager.git] / src / manager_modem.c
1 /*
2  * tel-plugin-manager
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Suresh Kumar N <suresh.n@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20
21 #include <string.h>
22 #include <glib.h>
23 #include <tcore.h>
24 #include <server.h>
25 #include <manager.h>
26 #include <plugin.h>
27 #include <core_object.h>
28 #include <user_request.h>
29 #include <co_call.h>
30
31 #include "manager_core.h"
32 #include "manager_util.h"
33 #include "manager_queue.h"
34 #include "manager_network.h"
35 #include "manager_modem.h"
36 #include "manager_call.h"
37 #include "manager_sim.h"
38 #include "internal/manager_internal.h"
39 #include "tfeature.h"
40
41 #include <vconf.h>
42 #include <system_info.h>
43
44 /*
45  * Timeout of 1 second for normal cases, we may have to consider 4 sec for Verizon.
46  */
47 #define MANAGER_FLIGHT_MODE_REQUEST_TIMEOUT     (1000)  /* 1 seconds */
48 #define MANAGER_MODEM_POWER_OFF_REQUEST_TIMEOUT (1000)  /* 1 seconds */
49
50 /*
51  * Timeout for 5 seconds for poweroff wait timer set
52  */
53 #define MANAGER_DEFAULT_TIMEOUT (5 * 1000)
54 #define MANAGER_RETRY_TIMEOUT   (1 * 1000)
55
56
57 /*
58  * Dbus interface info for power off wait timer handling
59  */
60 #define DEVICED_BUS_NAME                                        "org.tizen.system.deviced"
61 #define DEVICED_OBJECT_PATH                                     "/Org/Tizen/System/DeviceD"
62 #define DEVICED_INTERFACE_NAME              DEVICED_BUS_NAME
63
64 #define DEVICED_PATH_REBOOT                                     DEVICED_OBJECT_PATH"/Reboot"
65 #define DEVICED_PATH_POWEROFF                           DEVICED_OBJECT_PATH"/PowerOff"
66
67 #define DEVICED_INTERFACE_REBOOT                        DEVICED_INTERFACE_NAME".reboot"
68 #define DEVICED_INTERFACE_POWEROFF                      DEVICED_INTERFACE_NAME".PowerOff"
69
70 #define DBUS_METHOD_ADD_POWEROFF_WAIT           "AddPoweroffWait"
71 #define DBUS_METHOD_REMOVE_POWEROFF_WAIT        "RemovePoweroffWait"
72 #define DBUS_SIGNAL_POWEROFF_STATE                      "ChangeState"
73
74 static void __manager_modem_add_poweroff_wait(ModemPrivateInfo *modem_info);
75
76 static enum tcore_hook_return on_noti_hook_manager_call_status_idle(Server *server,
77                                                                        CoreObject *source, enum tcore_notification_command command,
78                                                                        unsigned int data_len, void *data, void *user_data)
79 {
80         TcorePlugin *modem_plugin = tcore_object_ref_plugin(source);
81         UserRequest *ur = user_data;
82
83         if (manager_call_is_during_call(modem_plugin)) {
84                 dbg("Call is still in progress");
85                 return TCORE_HOOK_RETURN_CONTINUE;
86         }
87
88         info("Call was disconnected. Dispatch pending ur(%p)", ur);
89
90         if (tcore_server_dispatch_request_ex(server, ur, TCORE_OPS_TYPE_CP) != TCORE_RETURN_SUCCESS) {
91                 err("Failed to dispatch request");
92         }
93
94         tcore_server_remove_notification_hook(server, on_noti_hook_manager_call_status_idle);
95
96         return TCORE_HOOK_RETURN_CONTINUE;
97 }
98
99 static void manager_modem_resp_hook_flight_mode(UserRequest *ur,
100                                                 enum tcore_response_command command,
101                                                 unsigned int data_len, const void *data, void *user_data)
102 {
103         TcorePlugin *manager_plugin = (TcorePlugin *)user_data;
104         PrivateData *priv_data = tcore_plugin_ref_user_data(manager_plugin);
105
106         priv_data->fm_processing_state = MANAGER_FLIGHT_PROCESSING_NONE;
107         info("flight mode request completed");
108 }
109
110 static enum tcore_manager_return __handle_flight_mode_end(Server *server, UserRequest *ur)
111 {
112         TcorePlugin *plugin_subs_other, *plugin_subs;
113         CoreObject *co_call, *co_call_other;
114         gboolean end_all_calls = FALSE;
115         Manager *manager = tcore_server_ref_manager(server);
116         TcorePlugin *manager_plugin = tcore_manager_get_plugin(manager);
117         PrivateData *priv_data = tcore_plugin_ref_user_data(manager_plugin);
118
119         /* In case of active calls, release all calls before sending flight mode request */
120         co_call = manager_util_get_core_object(server, ur, CORE_OBJECT_TYPE_CALL);
121         plugin_subs = tcore_object_ref_plugin(co_call);
122         plugin_subs_other = manager_util_get_other_subs_plugin(plugin_subs);
123         co_call_other = tcore_plugin_ref_core_object(plugin_subs_other, CORE_OBJECT_TYPE_CALL);
124
125         info("handle flight mode reqeust ");
126         if (priv_data->is_end_all_initiated == FALSE) {
127                 if (tcore_call_object_total_length(co_call) > 0) {
128                         end_all_calls = TRUE;
129                 } else if (tcore_call_object_total_length(co_call_other) > 0) {
130                         end_all_calls = TRUE;
131                         co_call = co_call_other;
132                         plugin_subs = plugin_subs_other;
133                 }
134         } else{
135                 priv_data->is_end_all_initiated = FALSE;
136         }
137
138         if (end_all_calls == TRUE) {
139
140                 UserRequest *new_ur = tcore_user_request_new(NULL, tcore_server_get_cp_name_by_plugin(plugin_subs));
141                 struct treq_call_end req = { 0, };
142
143                 info("Releasing all calls in case of flight mode request");
144
145                 req.type = CALL_END_TYPE_ALL;
146
147                 tcore_user_request_set_command(new_ur, TREQ_CALL_END);
148                 tcore_user_request_set_data(new_ur, sizeof(struct treq_call_end), &req);
149                 if (TCORE_RETURN_SUCCESS != tcore_server_dispatch_request(server, new_ur)) {
150                         err("END_ALL request failed!!r");
151                         tcore_user_request_free(new_ur);
152                 } else {
153                         info("queueing flight mode request");
154                         manager_queue_enqueue(manager, ur);
155                         return TCORE_MANAGER_RETURN_STOP;
156                 }
157         }
158
159         return TCORE_MANAGER_RETURN_CONTINUE;
160
161 }
162 enum tcore_manager_return manager_modem_process_request(Server *server,
163                                                         UserRequest *ur)
164 {
165         enum tcore_request_command command = tcore_user_request_get_command(ur);
166         enum tcore_manager_return manager_ret = TCORE_MANAGER_RETURN_CONTINUE;
167
168         switch (command) {
169         case TREQ_MODEM_SET_FLIGHTMODE: {
170                 struct treq_modem_set_flightmode *flight_mode = (struct treq_modem_set_flightmode *)tcore_user_request_ref_data(ur, 0);
171                 Manager *manager = tcore_server_ref_manager(server);
172                 TcorePlugin *manager_plugin = tcore_manager_get_plugin(manager);
173                 PrivateData *priv_data = tcore_plugin_ref_user_data(manager_plugin);
174
175                 info("flight mode request is in progress");
176                 /* Add response hook */
177                 tcore_user_request_set_response_hook(ur,
178                         manager_modem_resp_hook_flight_mode, manager_plugin);
179
180                 if (flight_mode->enable == 0) {
181                         dbg("Flight mode disable, not required to deregister IMS");
182                         priv_data->fm_processing_state = MANAGER_FLIGHT_PROCESSSING_DISABLE;
183
184                         return manager_ret;
185                 }
186
187                 priv_data->fm_processing_state = MANAGER_FLIGHT_PROCESSSING_ENABLE;
188                 /* In case calls are present, end all calls before processing flight mode request */
189                 if (__handle_flight_mode_end(server, ur) == TCORE_MANAGER_RETURN_STOP) {
190                         return TCORE_MANAGER_RETURN_STOP;
191                 }
192                 manager_ret = manager_util_process_ims(server, ur);
193         }
194         break;
195
196         case TREQ_MODEM_POWER_LOW:
197         case TREQ_MODEM_POWER_OFF: {
198                 if (tfeature_is_supported(TFEATURE_DEVICE_WEARABLE)) {
199                         TcorePlugin *modem_plugin = manager_util_get_modem_plugin(server, ur);
200                         if (manager_call_is_during_call(modem_plugin)) {
201                                 info("Call is in progress. Hold ur(%p)", ur);
202                                 tcore_server_add_notification_hook(server, TNOTI_CALL_STATUS_IDLE,
203                                                                                                         on_noti_hook_manager_call_status_idle, ur);
204                                 return TCORE_MANAGER_RETURN_STOP;
205                         }
206                 }
207
208                 manager_ret = manager_util_process_ims(server, ur);
209         }
210         break;
211
212         /*
213          * All other requests, would be routed to CP.
214          */
215         default:
216                 break;
217         }
218
219         return manager_ret;
220 }
221
222 static void __update_private_info(Manager *manager, CoreObject *co_modem, enum modem_state modem_status)
223 {
224         ModemBoard *mb = manager_core_get_modem_board(manager, tcore_object_ref_plugin(co_modem));
225
226         mb->modem_info->co_modem = co_modem;
227         mb->modem_info->modem_status = modem_status;
228
229 }
230
231 static void __on_resp_modem_poweroff(UserRequest *ur, enum tcore_response_command command,
232                                      unsigned int data_len, const void *data, void *user_data)
233 {
234         struct tresp_modem_power_off *poweroff_status = (struct tresp_modem_power_off *)data;
235         TcorePlugin *modem_plugin = (TcorePlugin *)user_data;
236
237         dbg("enter");
238
239         if (!poweroff_status || !modem_plugin) {
240                 err("NULL data : poweroff_status[%p], modem_plugin[%p]", poweroff_status, modem_plugin);
241                 return;
242         }
243
244 }
245
246 static void __request_modem_poweroff(TcorePlugin *modem_plugin)
247 {
248         struct treq_modem_power_off poweroff_cmd = {};
249         gboolean ret = FALSE;
250
251         dbg("enter");
252
253         if (G_UNLIKELY(!modem_plugin))
254                 return;
255
256         ret = manager_util_send_request(modem_plugin, TREQ_MODEM_POWER_OFF,
257                                         sizeof(poweroff_cmd), &poweroff_cmd, __on_resp_modem_poweroff, modem_plugin);
258
259         if (!ret) {
260                 err("Fail to request modem power off!!");
261         }
262
263         dbg("done");
264 }
265
266 static void __manager_modem_poweroff_signal_handler(GDBusConnection *connection,
267                                                              const gchar *sender_name, const gchar *object_path,
268                                                              const gchar *interface_name, const gchar *signal_name,
269                                                              GVariant *parameters, gpointer user_data)
270 {
271
272         ModemPrivateInfo *modem_info = user_data;
273         TcorePlugin *modem_plugin = NULL;
274         dbg("enter");
275
276         if (G_UNLIKELY(!modem_info))
277                 return;
278
279         if (g_strcmp0(signal_name, DBUS_SIGNAL_POWEROFF_STATE) == 0) {
280                 modem_plugin = tcore_object_ref_plugin(modem_info->co_modem);
281                 dbg("poweroff state changed is happened");
282                 __request_modem_poweroff(modem_plugin);
283         }
284         dbg("done");
285
286 }
287
288 static void __manager_modem_poweroff_subscribe_signal(ModemPrivateInfo *modem_info)
289 {
290         int ret;
291         char *profile = NULL;
292
293         dbg("enter");
294
295         if (G_UNLIKELY(!modem_info)) {
296                 err("Invalid data");
297                 return;
298         }
299
300         /*      Poweroff signal support should be executed only mobile and wearable profile.
301                 In case of TV or IoT profile etc... (dongle modem support case),
302                 dongle modem can be power off with udev_remove event.  */
303         ret = system_info_get_platform_string("tizen.org/feature/profile", &profile);
304         if (ret != SYSTEM_INFO_ERROR_NONE) {
305                 err("system_info_get_platform_string() failed!!! (%d,%s)", ret, get_error_message(ret));
306                 return;
307         }
308
309         dbg("profile: %s", profile);
310         if (g_strcmp0(profile, "mobile") && g_strcmp0(profile, "wearable")) {
311                 dbg("No need to subscribe poweroff signal handling to deviced");
312                 return;
313         }
314
315         if (!modem_info->sys_power.conn) {
316                 GError *error = NULL;
317
318                 modem_info->sys_power.conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
319                 if (modem_info->sys_power.conn == NULL) {
320                         err("GDBusconnection is NULL: [%s]", error->message);
321                         g_error_free(error);
322                         return;
323                 }
324                 modem_info->sys_power.set_poweroff_wait = FALSE;
325         }
326
327         if (!modem_info->sys_power.ca) {
328                 modem_info->sys_power.ca = g_cancellable_new();
329         }
330
331         if (modem_info->sys_power.subs_id_power_off) {
332                 dbg("g_dbus_connection_signal_subscribe() for poweroff_state has been already done.");
333                 return;
334         }
335
336         modem_info->sys_power.subs_id_power_off = g_dbus_connection_signal_subscribe(modem_info->sys_power.conn,
337                                                                                         NULL,
338                                                                                         DEVICED_INTERFACE_POWEROFF,
339                                                                                         DBUS_SIGNAL_POWEROFF_STATE,
340                                                                                         DEVICED_PATH_POWEROFF, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
341                                                                                         __manager_modem_poweroff_signal_handler, modem_info, NULL);
342
343
344         if (modem_info->sys_power.subs_id_power_off == 0) {
345                 err("g_dbus_connection_signal_subscribe() is failed.");
346                 g_object_unref(modem_info->sys_power.conn);
347         }
348
349         dbg("done");
350
351 }
352
353 static void __manager_modem_poweroff_unsubscribe_signal(ModemPrivateInfo *modem_info)
354 {
355         dbg("enter");
356
357         if (G_UNLIKELY(!modem_info)) {
358                 err("Invalid data");
359                 return;
360         }
361
362         if (modem_info->sys_power.subs_id_power_off > 0) {
363                 g_dbus_connection_signal_unsubscribe(modem_info->sys_power.conn, modem_info->sys_power.subs_id_power_off);
364                 modem_info->sys_power.subs_id_power_off = 0;
365         }
366
367         if (modem_info->sys_power.ca) {
368                 g_cancellable_cancel(modem_info->sys_power.ca);
369                 g_object_unref(modem_info->sys_power.ca);
370         }
371
372         if (modem_info->sys_power.conn) {
373                 g_object_unref(modem_info->sys_power.conn);
374                 modem_info->sys_power.conn = NULL;
375         }
376
377         modem_info->sys_power.set_poweroff_wait = FALSE;
378
379         dbg("done");
380
381 }
382
383 static void __manager_modem_add_poweroff_wait_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
384 {
385         GError *error = NULL;
386         GDBusConnection *conn = NULL;
387         int result = -1;
388         GVariant *dbus_result = NULL;
389         ModemPrivateInfo *modem_info = user_data;
390
391         dbg("enter");
392
393         conn = G_DBUS_CONNECTION(source_object);
394         dbus_result = g_dbus_connection_call_finish(conn, res, &error);
395         if (error) {
396                 err("Failed: %s", error->message);
397                 g_error_free(error);
398         } else {
399                 dbg("PoweroffWait Added Successufully");
400                 modem_info->sys_power.set_poweroff_wait = TRUE;
401         }
402
403         if (dbus_result) {
404                 g_variant_get(dbus_result, "(i)", &result);
405                 g_variant_unref(dbus_result);
406         }
407
408         dbg("result : %d", result);
409 }
410
411
412 static void __manager_modem_add_poweroff_wait(ModemPrivateInfo *modem_info)
413 {
414         dbg("enter");
415
416         if (G_UNLIKELY(!modem_info)) {
417                 err("Invalid data");
418                 return;
419         }
420
421         if (modem_info->sys_power.set_poweroff_wait == TRUE) {
422                 return;
423         }
424
425         if (modem_info->sys_power.conn) {
426                 g_dbus_connection_call(modem_info->sys_power.conn, DEVICED_BUS_NAME,
427                                         DEVICED_PATH_REBOOT, DEVICED_INTERFACE_REBOOT, DBUS_METHOD_ADD_POWEROFF_WAIT,
428                                         NULL, NULL, G_DBUS_CALL_FLAGS_NONE,
429                                         MANAGER_DEFAULT_TIMEOUT, modem_info->sys_power.ca,
430                                         __manager_modem_add_poweroff_wait_cb, modem_info);
431         }
432
433         dbg("done");
434
435         return;
436 }
437
438 static void __manager_modem_remove_poweroff_wait_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
439 {
440         GError *error = NULL;
441         GDBusConnection *conn = NULL;
442         int result = -1;
443         GVariant *dbus_result = NULL;
444         ModemPrivateInfo *modem_info = user_data;
445
446         dbg("enter");
447
448         conn = G_DBUS_CONNECTION(source_object);
449         dbus_result = g_dbus_connection_call_finish(conn, res, &error);
450         if (error) {
451                 err("Failed: %s", error->message);
452                 g_error_free(error);
453         } else {
454                 dbg("PoweroffWait Removed Successufully");
455                 modem_info->sys_power.set_poweroff_wait = FALSE;
456         }
457
458         if (dbus_result) {
459                 g_variant_get(dbus_result, "(i)", &result);
460                 g_variant_unref(dbus_result);
461         }
462
463         dbg("result : %d", result);
464 }
465
466 static void __manager_modem_remove_poweroff_wait(ModemPrivateInfo *modem_info)
467 {
468         dbg("enter");
469
470         if (G_UNLIKELY(!modem_info)) {
471                 err("Invalid data");
472                 return;
473         }
474
475         if (modem_info->sys_power.conn) {
476                 g_dbus_connection_call(modem_info->sys_power.conn, DEVICED_BUS_NAME,
477                                         DEVICED_PATH_REBOOT, DEVICED_INTERFACE_REBOOT, DBUS_METHOD_REMOVE_POWEROFF_WAIT,
478                                         NULL, NULL, G_DBUS_CALL_FLAGS_NONE,
479                                         MANAGER_DEFAULT_TIMEOUT, modem_info->sys_power.ca,
480                                         __manager_modem_remove_poweroff_wait_cb, NULL);
481
482                 modem_info->sys_power.set_poweroff_wait = FALSE;
483         }
484
485         return;
486 }
487
488 gboolean manager_modem_initialize_private_info(ModemBoard *mb)
489 {
490         dbg("enter");
491
492         if (G_UNLIKELY(!mb)) {
493                 err("Invalid data");
494                 return FALSE;
495         }
496
497         if (mb->modem_info)
498                 mb->modem_info->modem_status = MODEM_STATE_UNKNOWN;
499
500         mb->modem_info = g_malloc0(sizeof(struct manager_modem_private_info));
501         mb->modem_info->co_modem = tcore_plugin_ref_core_object(mb->modem_plugin, CORE_OBJECT_TYPE_MODEM);
502         mb->modem_info->modem_status = MODEM_STATE_UNKNOWN;
503
504         if (mb->index == MANAGER_MODEM_BOARD_INDEX_0) {
505                 dbg("__manager_power_subscribe_signal() for modem index (%d)", mb->index);
506                 __manager_modem_poweroff_subscribe_signal(mb->modem_info);
507         }
508
509         dbg("Initialized modem private info");
510         return TRUE;
511 }
512
513 gboolean manager_modem_clear_private_info(ModemBoard *mb)
514 {
515
516         dbg("enter");
517
518         if (G_UNLIKELY(!mb || !mb->modem_info)) {
519                 err("Invalid data");
520                 return FALSE;
521         }
522
523         if (mb->index == MANAGER_MODEM_BOARD_INDEX_0) {
524                 dbg("__manager_power_unsubscribe_signal() for modem index (%d)", mb->index);
525                 __manager_modem_poweroff_unsubscribe_signal(mb->modem_info);
526         }
527
528         g_free(mb->modem_info);
529         mb->modem_info = NULL;
530
531         dbg("done");
532
533         return TRUE;
534 }
535
536 enum tcore_manager_return manager_modem_process_notification(Manager *manager,
537                                                              CoreObject *source, enum tcore_notification_command command,
538                                                              unsigned int data_len, void *data)
539 {
540         enum tcore_manager_return ret = TCORE_MANAGER_RETURN_CONTINUE;
541         TcorePlugin *manager_plugin = tcore_manager_get_plugin(manager);
542         PrivateData *priv_data = tcore_plugin_ref_user_data(manager_plugin);
543
544         ModemBoard *mb = manager_core_get_modem_board(manager, tcore_object_ref_plugin(source));
545
546         if (!data || !priv_data || !mb || !mb->modem_info) {
547                 err("Invalid data");
548                 return ret;
549         }
550
551         switch (command) {
552         case TNOTI_MODEM_POWER: {
553                 const struct tnoti_modem_power *modem_power = data;
554
555                 switch (modem_power->state) {
556                 case MODEM_STATE_ERROR:
557                         dbg("Modem RESET happened");
558                         manager_network_process_modem_error(source);
559                         manager_call_process_modem_error(source);
560                         priv_data->fm_processing_state = MANAGER_FLIGHT_PROCESSING_NONE;
561                         break;
562
563                 case MODEM_STATE_ONLINE: {
564                         char *msg = g_strdup("setRadioPower on");
565                         dbg("Write MODEM_STATE to /sys/class/sec/bsp/boot_stat");
566                         manager_util_write_to_proc_file(msg, strlen(msg));
567                         g_free(msg);
568
569                         /* For executing CP detach process when Device Power off */
570                         /* It should be subscribed power off waiting timer to deviced */
571                         /* Poweroff timer should be added only one time, in case of modem index (0) */
572                         if (mb->index == MANAGER_MODEM_BOARD_INDEX_0) {
573                                 dbg("Should add poweroff timer in case of modem index(0)");
574                                 if (!mb->modem_info->sys_power.set_poweroff_wait) {
575                                         //__manager_power_subscribe_signal(mb->modem_info);
576                                         __manager_modem_add_poweroff_wait(mb->modem_info);
577                                 }
578                         }
579                 }
580                 break;
581
582                 case MODEM_STATE_OFFLINE: {
583                         dbg("MODEM_STATE has been changed to OFFLINE");
584
585                         /* Once MODEM has been power off successufully, */
586                         /* It should be removed  power off waiting timer from deviced */
587                         /* Poweroff timer should be removed only one time, in case of modem index (0) */
588                         if (mb->index == MANAGER_MODEM_BOARD_INDEX_0) {
589                                 dbg("Should removed poweroff timer in case of modem index (0)");
590
591                                 if (mb->modem_info->sys_power.set_poweroff_wait == TRUE) {
592                                         __manager_modem_remove_poweroff_wait(mb->modem_info);
593                                 }
594                         }
595                 }
596                 break;
597
598                 default:
599                         break;
600                 }
601                 __update_private_info(manager, source, modem_power->state);
602 #ifdef TIZEN_SUPPORT_SIM_PREFERRED_SUBSCRIPTION
603                 set_nw_dds_ds_on_boot(manager);
604 #endif
605         }
606         break;
607
608         default:
609                 break;
610         }
611
612         return ret;
613 }
614