15ac07c24739dbd58719fdee0e62651061786923
[platform/core/connectivity/net-config.git] / src / wifi-power.c
1 /*
2  * Network Configuration Module
3  *
4  * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <vconf.h>
23 #include <vconf-keys.h>
24 #include <wifi-direct.h>
25
26 #include "wifi.h"
27 #include "log.h"
28 #include "wifi.h"
29 #include "util.h"
30 #include "netdbus.h"
31 #include "neterror.h"
32 #include "netconfig.h"
33 #include "emulator.h"
34 #include "network-statistics.h"
35 #include "wifi-background-scan.h"
36 #include "wifi-power.h"
37 #include "wifi-state.h"
38 #include "mdm-private.h"
39 #include "wifi-agent.h"
40 #include "wifi-eap-config.h"
41
42
43 #define WLAN_DRIVER_SCRIPT "/usr/bin/wlan.sh"
44
45 static gboolean power_in_progress = FALSE;
46 static gboolean fm_waiting = FALSE;
47
48 static void __netconfig_wifi_technology_reply(DBusPendingCall *call, void *data)
49 {
50         DBusMessage *message;
51
52         message = dbus_pending_call_steal_reply(call);
53
54         if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) {
55                 ERR("%s", dbus_message_get_error_name(message));
56
57                 if (dbus_message_is_error(message,
58                                 CONNMAN_ERROR_INTERFACE ".AlreadyEnabled") == TRUE) {
59                         netconfig_wifi_update_power_state(TRUE);
60                         power_in_progress = FALSE;
61                 } else if (dbus_message_is_error(message,
62                                 CONNMAN_ERROR_INTERFACE ".AlreadyDisabled") == TRUE) {
63                         netconfig_wifi_update_power_state(FALSE);
64                         power_in_progress = FALSE;
65                 }
66         } else {
67                 DBG("Successfully requested");
68         }
69
70         dbus_message_unref(message);
71         dbus_pending_call_unref(call);
72 }
73
74 static gboolean __netconfig_wifi_enable_technology(void)
75 {
76         gboolean reply = FALSE;
77         char param0[] = "string:Powered";
78         char param1[] = "variant:boolean:true";
79         char *param_array[] = { NULL, NULL, NULL };
80
81         param_array[0] = param0;
82         param_array[1] = param1;
83
84         reply = netconfig_invoke_dbus_method_nonblock(CONNMAN_SERVICE,
85                         CONNMAN_WIFI_TECHNOLOGY_PREFIX, CONNMAN_TECHNOLOGY_INTERFACE,
86                         "SetProperty", param_array, __netconfig_wifi_technology_reply);
87
88         if (reply != TRUE)
89                 ERR("Fail to enable Wi-Fi");
90
91         return reply;
92 }
93
94 static gboolean __netconfig_wifi_disable_technology(void)
95 {
96         gboolean reply = FALSE;
97         char param0[] = "string:Powered";
98         char param1[] = "variant:boolean:false";
99         char *param_array[] = { NULL, NULL, NULL };
100
101         param_array[0] = param0;
102         param_array[1] = param1;
103
104         reply = netconfig_invoke_dbus_method_nonblock(CONNMAN_SERVICE,
105                         CONNMAN_WIFI_TECHNOLOGY_PREFIX, CONNMAN_TECHNOLOGY_INTERFACE,
106                         "SetProperty", param_array, __netconfig_wifi_technology_reply);
107
108         if (reply != TRUE)
109                 ERR("Fail to disable Wi-Fi");
110
111         return reply;
112 }
113
114 static gboolean __netconfig_wifi_load_driver(void)
115 {
116         gboolean rv = FALSE;
117         const char *path = WLAN_DRIVER_SCRIPT;
118         char *const args[] = { "wlan.sh", "start", NULL };
119         char *const envs[] = { NULL };
120
121         if (netconfig_emulator_is_emulated() == TRUE)
122                 return rv;
123
124         rv = netconfig_execute_file(path, args, envs);
125         if (rv != TRUE) {
126                 DBG("Failed to load wireless device driver");
127                 return FALSE;
128         }
129
130         DBG("Successfully loaded wireless device driver");
131         return TRUE;
132 }
133
134 gboolean netconfig_wifi_remove_driver(void)
135 {
136         gboolean rv = FALSE;
137         const char *path = WLAN_DRIVER_SCRIPT;
138         char *const args[] = { "wlan.sh", "stop", NULL };
139         char *const env[] = { NULL };
140
141         if (netconfig_emulator_is_emulated() == TRUE)
142                 return rv;
143
144         rv = netconfig_execute_file(path, args, env);
145         if (rv != TRUE) {
146                 DBG("Failed to remove wireless device driver");
147                 return FALSE;
148         }
149
150         DBG("Successfully removed wireless device driver");
151         return TRUE;
152 }
153
154 static int __netconfig_wifi_try_to_load_driver(void);
155 static gboolean __netconfig_wifi_try_to_remove_driver(void);
156
157 void netconfig_wifi_notify_power_completed(gboolean power_on)
158 {
159         DBusMessage *signal;
160         DBusConnection *connection;
161         DBusError error;
162         char *sig_name;
163
164         if (power_on)
165                 sig_name = "PowerOnCompleted";
166         else
167                 sig_name = "PowerOffCompleted";
168
169         dbus_error_init(&error);
170
171         connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
172         if (connection == NULL) {
173                 ERR("Error!!! Failed to get system DBus, error [%s]", error.message);
174                 dbus_error_free(&error);
175                 return;
176         }
177
178         signal = dbus_message_new_signal(NETCONFIG_WIFI_PATH,
179                                         NETCONFIG_WIFI_INTERFACE, sig_name);
180         if (signal == NULL)
181                 return;
182
183         dbus_connection_send(connection, signal, NULL);
184
185         dbus_message_unref(signal);
186         dbus_connection_unref(connection);
187
188         INFO("(%s)", sig_name);
189 }
190
191 static void __netconfig_wifi_direct_state_cb(int error_code,
192                 wifi_direct_device_state_e device_state, void *user_data)
193 {
194         wifi_direct_unset_device_state_changed_cb();
195         wifi_direct_deinitialize();
196
197         if (device_state == WIFI_DIRECT_DEVICE_STATE_DEACTIVATED) {
198                 if (__netconfig_wifi_try_to_load_driver() < 0) {
199                         power_in_progress = FALSE;
200
201                         /* TODO: error report */
202
203                         return;
204                 }
205         }
206 }
207
208 static gboolean __netconfig_wifi_direct_power_off(void)
209 {
210         DBG("Wi-Fi direct is turning off");
211
212         if (wifi_direct_initialize() < 0)
213                 return FALSE;
214
215         if (wifi_direct_set_device_state_changed_cb(
216                         __netconfig_wifi_direct_state_cb, NULL) < 0)
217                 return FALSE;
218
219         if (wifi_direct_deactivate() < 0)
220                 return FALSE;
221
222         return TRUE;
223 }
224
225 static int __netconfig_wifi_try_to_load_driver(void)
226 {
227         if (netconfig_is_wifi_allowed() != TRUE)
228                 return -EPERM;
229
230         if (netconfig_is_wifi_tethering_on() == TRUE) {
231                 /* TODO: Wi-Fi tethering turns off here */
232                 /* return TRUE; */
233                 return -EBUSY;
234         }
235
236         if (netconfig_is_wifi_direct_on() == TRUE) {
237                 if (__netconfig_wifi_direct_power_off() == TRUE) {
238                         power_in_progress = TRUE;
239                         return -EINPROGRESS;
240                 } else
241                         return -EBUSY;
242         }
243
244         if (__netconfig_wifi_load_driver() != TRUE) {
245                 netconfig_wifi_remove_driver();
246
247                 return -EIO;
248         }
249
250         if (__netconfig_wifi_enable_technology() != TRUE) {
251                 netconfig_wifi_remove_driver();
252                 return -EIO;
253         }
254
255         power_in_progress = TRUE;
256
257         return 0;
258 }
259
260 static gboolean __netconfig_wifi_try_to_remove_driver(void)
261 {
262         netconfig_wifi_device_picker_service_stop();
263
264         netconfig_wifi_statistics_update_powered_off();
265
266         if (__netconfig_wifi_disable_technology() != TRUE)
267                 return FALSE;
268
269         power_in_progress = TRUE;
270
271         return TRUE;
272 }
273
274 static void __netconfig_wifi_airplane_mode(keynode_t* node,
275                 void* user_data)
276 {
277         int value = 0;
278         int wifi_state = 0;
279         static gboolean powered_off_by_flightmode = FALSE;
280
281         if (power_in_progress) {
282                 fm_waiting = TRUE;
283                 return;
284         }
285
286         fm_waiting = FALSE;
287
288         vconf_get_bool(VCONFKEY_TELEPHONY_FLIGHT_MODE, &value);
289         vconf_get_int(VCONFKEY_WIFI_STATE, &wifi_state);
290
291         DBG("flight mode %s", value > 0 ? "ON" : "OFF");
292         DBG("Wi-Fi state %d, Wi-Fi was off by flight mode %s",
293                         wifi_state, powered_off_by_flightmode == TRUE ? "Yes" : "No");
294
295         if (value > 0) {
296                 /* flight mode enabled */
297                 if (wifi_state == VCONFKEY_WIFI_OFF)
298                         return;
299
300                 DBG("Turning Wi-Fi off");
301
302                 __netconfig_wifi_try_to_remove_driver();
303
304                 powered_off_by_flightmode = TRUE;
305         } else if (value == 0) {
306                 /* flight mode disabled */
307                 if (wifi_state > VCONFKEY_WIFI_OFF)
308                         return;
309
310                 if (powered_off_by_flightmode != TRUE)
311                         return;
312
313                 __netconfig_wifi_try_to_load_driver();
314
315                 powered_off_by_flightmode = FALSE;
316         } else
317                 DBG("Invalid value (%d)", value);
318 }
319
320 static void __netconfig_wifi_pm_state_mode(keynode_t* node,
321                 void* user_data)
322 {
323         int value = -1;
324         int wifi_state = 0;
325         static int prev_state = VCONFKEY_PM_STATE_NORMAL;
326
327         /*** vconf-keys.h ***
328          *              VCONFKEY_PM_STATE_NORMAL = 1,
329          *              VCONFKEY_PM_STATE_LCDDIM,
330          *              VCONFKEY_PM_STATE_LCDOFF,
331          *              VCONFKEY_PM_STATE_SLEEP
332          */
333
334         if(vconf_get_int(VCONFKEY_WIFI_STATE, &wifi_state) == 0) {
335                 DBG("wifi state : %d (0 off / 1 on / 2 connected)", wifi_state);
336                 if(wifi_state <= VCONFKEY_WIFI_OFF)
337                         return;
338         }
339
340         if(vconf_get_int(VCONFKEY_PM_STATE, &value) < 0) {
341                 ERR("VCONFKEY_PM_STATE get failed");
342                 return;
343         }
344
345         DBG("Old state: %d, current: %d", prev_state, value);
346
347         if((value == VCONFKEY_PM_STATE_NORMAL) && (prev_state >= VCONFKEY_PM_STATE_LCDOFF)) {
348                 DBG("PM state : Wake UP!");
349
350                 netconfig_wifi_bgscan_stop();
351                 netconfig_wifi_bgscan_start();
352         }
353
354         prev_state = value;
355 }
356
357 void netconfig_set_power_in_progress(gboolean in_progress)
358 {
359         power_in_progress = in_progress;
360 }
361
362 void netconfig_check_fm_waiting(void)
363 {
364         if (fm_waiting)
365                 __netconfig_wifi_airplane_mode(NULL, NULL);
366 }
367
368 void netconfig_wifi_power_configuration(void)
369 {
370         int wifi_last_power_state = 0;
371
372         vconf_notify_key_changed(VCONFKEY_TELEPHONY_FLIGHT_MODE,
373                         __netconfig_wifi_airplane_mode, NULL);
374
375         vconf_notify_key_changed(VCONFKEY_PM_STATE,
376                         __netconfig_wifi_pm_state_mode, NULL);
377
378         vconf_get_int(VCONF_WIFI_LAST_POWER_STATE, &wifi_last_power_state);
379
380         if (wifi_last_power_state == WIFI_POWER_ON) {
381                 DBG("Turn Wi-Fi on automatically");
382
383                 __netconfig_wifi_try_to_load_driver();
384         }
385 }
386
387 gboolean netconfig_iface_wifi_load_driver(NetconfigWifi *wifi, GError **error)
388 {
389         DBG("Wi-Fi power on requested");
390
391         g_return_val_if_fail(wifi != NULL, FALSE);
392
393         int err;
394
395         if (netconfig_is_wifi_allowed() != TRUE) {
396                 netconfig_error_security_restricted(error);
397
398                 return FALSE;
399         }
400
401         if (power_in_progress) {
402                 netconfig_error_wifi_driver_failed(error);
403                 return FALSE;
404         }
405
406         err = __netconfig_wifi_try_to_load_driver();
407         if (err < 0) {
408                 if (err == -EINPROGRESS)
409                         netconfig_error_wifi_load_inprogress(error);
410                 else
411                         netconfig_error_wifi_driver_failed(error);
412
413                 return FALSE;
414         }
415
416         return TRUE;
417 }
418
419 gboolean netconfig_iface_wifi_remove_driver(NetconfigWifi *wifi, GError **error)
420 {
421         DBG("Wi-Fi power off requested");
422
423         g_return_val_if_fail(wifi != NULL, FALSE);
424
425         if (power_in_progress) {
426                 netconfig_error_wifi_driver_failed(error);
427                 return FALSE;
428         }
429
430         if (__netconfig_wifi_try_to_remove_driver() != TRUE) {
431                 netconfig_error_wifi_driver_failed(error);
432
433                 return FALSE;
434         }
435
436         return TRUE;
437 }