Block background scan while Doze Mode is set
[platform/core/connectivity/net-config.git] / src / wifi-background-scan.c
1 /*
2  * Network Configuration Module
3  *
4  * Copyright (c) 2000 - 2012 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 <glib.h>
21 #include <vconf.h>
22 #include <vconf-keys.h>
23
24 #include "log.h"
25 #include "util.h"
26 #include "netdbus.h"
27 #include "wifi-state.h"
28 #include "wifi-background-scan.h"
29
30 struct bgscan_data {
31         guint time;
32         guint mode;
33         guint timer_id;
34         gboolean paused;
35 };
36
37 static gboolean netconfig_wifi_scanning = FALSE;
38
39 static gboolean __netconfig_wifi_bgscan_next_scan(gpointer data);
40
41 static struct bgscan_data *__netconfig_wifi_bgscan_get_data(void)
42 {
43         static struct bgscan_data data = {
44                 SCAN_EXPONENTIAL_MIN, WIFI_BGSCAN_MODE_EXPONENTIAL, 0, FALSE};
45
46         return &data;
47 }
48
49 static gboolean __netconfig_wifi_bgscan_set_mode(guint mode)
50 {
51         if (mode != WIFI_BGSCAN_MODE_EXPONENTIAL && mode != WIFI_BGSCAN_MODE_PERIODIC) {
52                 ERR("Invalid scan mode [%d]", mode);
53                 return FALSE;
54         }
55
56         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
57         data->mode = mode;
58
59         return TRUE;
60 }
61
62 static gboolean _set_scan_reset_interval(guint interval)
63 {
64         if ((interval < SCAN_EXPONENTIAL_MIN) || (interval > SCAN_EXPONENTIAL_MAX)) {
65                 ERR("Invalid interval [%d]", interval);
66                 return FALSE;
67         }
68
69         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
70         data->time = interval;
71         return TRUE;
72 }
73
74 static guint _get_scan_reset_interval(void)
75 {
76         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
77
78         return data->time;;
79 }
80
81 static void __netconfig_wifi_scan_request_reply(GObject *source_object,
82                 GAsyncResult *res, gpointer user_data)
83 {
84         GVariant *reply;
85         GDBusConnection *conn = NULL;
86         GError *error = NULL;
87
88         conn = G_DBUS_CONNECTION(source_object);
89         reply = g_dbus_connection_call_finish(conn, res, &error);
90
91         if (reply == NULL) {
92                 if (error != NULL) {
93                         ERR("Fail to request status [%d: %s]", error->code, error->message);
94                         netconfig_wifi_set_scanning(FALSE);
95                         g_error_free(error);
96                 } else {
97                         ERR("Fail to request scan");
98                         netconfig_wifi_set_scanning(FALSE);
99                 }
100         } else {
101                 DBG("Successfully requested");
102                 g_variant_unref(reply);
103         }
104
105         netconfig_gdbus_pending_call_unref();
106 }
107
108 static gboolean __netconfig_wifi_bgscan_request_connman_scan(int retries)
109 {
110         gboolean reply = FALSE;
111
112         netconfig_wifi_set_scanning(TRUE);
113
114         reply = netconfig_invoke_dbus_method_nonblock(CONNMAN_SERVICE,
115                         CONNMAN_WIFI_TECHNOLOGY_PREFIX,
116                         CONNMAN_TECHNOLOGY_INTERFACE, "Scan", NULL, __netconfig_wifi_scan_request_reply);
117         if (reply != TRUE) {
118                 ERR("Failed to send Scan request to connman");
119                 netconfig_wifi_set_scanning(FALSE);
120         }
121
122         return reply;
123 }
124
125 static gboolean __netconfig_wifi_bgscan_immediate_scan(gpointer data)
126 {
127         static int retries = 0;
128         guint state = wifi_state_get_service_state();
129
130 #if !defined TIZEN_WEARABLE
131         if (netconfig_wifi_is_bgscan_paused()) {
132                 return FALSE;
133         }
134 #endif
135         if (netconfig_wifi_block_bgscan(-1))
136                 return FALSE;
137
138         if (state == NETCONFIG_WIFI_CONNECTED) {
139                 if (netconfig_wifi_bgscan_get_mode() == WIFI_BGSCAN_MODE_EXPONENTIAL) {
140                         DBG("Wi-Fi state is connected, scan is paused");
141                         return FALSE;
142                 }
143         } else if (state == NETCONFIG_WIFI_ASSOCIATION || state == NETCONFIG_WIFI_CONFIGURATION) {
144                 /* During WIFI connecting, WIFI can be disappeared.
145                  * After 1 sec, try scan even if connecting state */
146                 if (retries < 2) {
147                         retries++;
148                         return TRUE;
149                 }
150         }
151
152         if (__netconfig_wifi_bgscan_request_connman_scan(retries) == TRUE) {
153                 retries = 0;
154                 return FALSE;
155         } else if (retries > 2) {
156                 retries = 0;
157                 return FALSE;
158         }
159
160         retries++;
161
162         return TRUE;
163 }
164
165 static void __netconfig_wifi_bgscan_start_timer(gboolean immediate_scan)
166 {
167         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
168
169         netconfig_stop_timer(&(data->timer_id));
170
171         switch (data->mode) {
172         case WIFI_BGSCAN_MODE_EXPONENTIAL:
173                 if (data->time < SCAN_EXPONENTIAL_MIN ||
174                         data->time == SCAN_PERIODIC_DELAY)
175                         data->time = SCAN_EXPONENTIAL_MIN;
176                 break;
177         case WIFI_BGSCAN_MODE_PERIODIC:
178                 data->time = SCAN_PERIODIC_DELAY;
179                 break;
180         default:
181                 DBG("Invalid Wi-Fi background scan mode[%d]", data->mode);
182                 return;
183         }
184
185         if (immediate_scan)
186                 DBG("Scan immediately[%d], mode[%d](0 exponential / 1 periodic)", immediate_scan, data->mode);
187         else
188                 DBG("Scan immediately[%d], mode[%d](0 exponential / 1 periodic), next[%d]", immediate_scan, data->mode, data->time);
189
190         if (immediate_scan)
191                 g_timeout_add(500, __netconfig_wifi_bgscan_immediate_scan, NULL);
192
193         netconfig_start_timer_seconds(data->time,
194                                 __netconfig_wifi_bgscan_next_scan, data, &(data->timer_id));
195         if (data->mode == WIFI_BGSCAN_MODE_EXPONENTIAL && immediate_scan) {
196                 data->time = data->time * 2;
197                 if (data->time > SCAN_EXPONENTIAL_MAX)
198                         data->time = SCAN_EXPONENTIAL_MAX;
199         }
200
201         //DBG("Scan immediately[%d], mode[%d](0 exponential / 1 periodic), next[%d]", immediate_scan, data->mode, data->time);
202 }
203
204 static void __netconfig_wifi_bgscan_stop_timer(struct bgscan_data *data)
205 {
206         if (data == NULL)
207                 return;
208
209         netconfig_stop_timer(&(data->timer_id));
210 }
211
212 static gboolean __netconfig_wifi_bgscan_next_scan(gpointer data)
213 {
214         int pm_state = VCONFKEY_PM_STATE_NORMAL;
215
216         /* In case of LCD off, we don't need Wi-Fi scan */
217         netconfig_vconf_get_int(VCONFKEY_PM_STATE, &pm_state);
218         if (pm_state >= VCONFKEY_PM_STATE_LCDOFF)
219                 return TRUE;
220
221         __netconfig_wifi_bgscan_start_timer(TRUE);
222
223         return FALSE;
224 }
225
226 gboolean netconfig_wifi_block_bgscan(int status)
227 {
228         static gboolean blocked = FALSE;
229
230         if (status == 1)
231                 blocked = TRUE;
232         else if (status == 0)
233                 blocked = FALSE;
234
235         return blocked;
236 }
237
238 void netconfig_wifi_set_bgscan_pause(gboolean pause)
239 {
240         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
241         DBG("[%s] Wi-Fi background scan", pause ? "Pause" : "Resume");
242         data->paused = pause;
243 }
244
245 gboolean netconfig_wifi_is_bgscan_paused(void)
246 {
247         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
248         DBG("Wi-Fi background scan is [%s]", data->paused ? "Paused" : "Runable");
249         return data->paused;
250 }
251
252 void netconfig_wifi_bgscan_start(gboolean immediate_scan)
253 {
254         wifi_tech_state_e wifi_tech_state;
255         wifi_service_state_e wifi_service_state;
256         struct bgscan_data *data =
257                         __netconfig_wifi_bgscan_get_data();
258
259         if (data == NULL)
260                 return;
261
262         if (data->timer_id > 0)
263                 __netconfig_wifi_bgscan_stop_timer(data);
264
265         wifi_tech_state = wifi_state_get_technology_state(FALSE);
266         wifi_service_state = wifi_state_get_service_state();
267         DBG("wifi tech state [%s] service state [%s]",
268                 _convert_wifi_technology_state_to_string(wifi_tech_state),
269                 _convert_wifi_service_state_to_string(wifi_service_state));
270
271         if (wifi_tech_state < NETCONFIG_WIFI_TECH_POWERED)
272                 return;
273
274         if (data->mode == WIFI_BGSCAN_MODE_EXPONENTIAL &&
275                 wifi_service_state == NETCONFIG_WIFI_CONNECTED)
276                 return;
277
278         DBG("Wi-Fi background scan started or re-started(%d)", immediate_scan);
279         __netconfig_wifi_bgscan_start_timer(immediate_scan);
280 }
281
282 void netconfig_wifi_bgscan_stop(void)
283 {
284         struct bgscan_data *data =
285                         __netconfig_wifi_bgscan_get_data();
286
287         if (data == NULL)
288                 return;
289
290         DBG("Wi-Fi background scan stop [mode:%d]", data->mode);
291
292         data->time = 0;
293
294         __netconfig_wifi_bgscan_stop_timer(data);
295 }
296
297 gboolean netconfig_wifi_get_bgscan_state(void)
298 {
299         struct bgscan_data *data =
300                         __netconfig_wifi_bgscan_get_data();
301         return ((data->timer_id > (guint)0) ? TRUE : FALSE);
302 }
303
304 guint netconfig_wifi_bgscan_get_mode(void)
305 {
306         struct bgscan_data *data = __netconfig_wifi_bgscan_get_data();
307
308         return data->mode;
309 }
310
311 gboolean netconfig_wifi_bgscan_set_interval(guint interval)
312 {
313         gboolean ret = FALSE;
314
315         ret = _set_scan_reset_interval(interval);
316
317         return ret;
318 }
319
320 void netconfig_wifi_bgscan_get_interval(guint *interval)
321 {
322         *interval = _get_scan_reset_interval();
323 }
324
325 gboolean netconfig_wifi_get_scanning(void)
326 {
327         return netconfig_wifi_scanning;
328 }
329
330 void netconfig_wifi_set_scanning(gboolean scanning)
331 {
332         if (netconfig_wifi_scanning != scanning)
333                 netconfig_wifi_scanning = scanning;
334 }
335
336 gboolean handle_set_bgscan(Wifi *wifi, GDBusMethodInvocation *context, guint scan_mode)
337 {
338         gint old_mode = 0;
339         int pm_state = VCONFKEY_PM_STATE_NORMAL;
340
341         old_mode = netconfig_wifi_bgscan_get_mode();
342         if (old_mode == scan_mode) {
343                 wifi_complete_set_bgscan(wifi, context);
344                 return TRUE;
345         }
346
347         if (__netconfig_wifi_bgscan_set_mode(scan_mode) != TRUE) {
348                 ERR("Invalid mode [%d]", scan_mode);
349                 netconfig_error_invalid_parameter(context);
350                 return TRUE;
351         }
352
353         INFO("scan mode is changed [%d]", scan_mode);
354
355         /* In case of LCD off, we don't need Wi-Fi scan right now */
356         netconfig_vconf_get_int(VCONFKEY_PM_STATE, &pm_state);
357         if (pm_state >= VCONFKEY_PM_STATE_LCDOFF)
358                 netconfig_wifi_bgscan_start(FALSE);
359         else
360                 netconfig_wifi_bgscan_start(TRUE);
361
362         wifi_complete_set_bgscan(wifi, context);
363         return TRUE;
364 }
365
366 gboolean handle_resume_bgscan(Wifi *wifi, GDBusMethodInvocation *context)
367 {
368         netconfig_wifi_set_bgscan_pause(FALSE);
369
370         wifi_complete_resume_bgscan(wifi, context);
371         return TRUE;
372 }
373
374 gboolean handle_pause_bgscan(Wifi *wifi, GDBusMethodInvocation *context)
375 {
376         netconfig_wifi_set_bgscan_pause(TRUE);
377
378         wifi_complete_pause_bgscan(wifi, context);
379         return TRUE;
380 }
381
382 gboolean handle_reset_bgscan_interval(Wifi *wifi, GDBusMethodInvocation *context)
383 {
384
385         _set_scan_reset_interval(SCAN_EXPONENTIAL_MIN);
386
387         wifi_complete_reset_bgscan_interval(wifi, context);
388         return TRUE;
389 }
390
391 gboolean handle_get_autoscan(Wifi *wifi, GDBusMethodInvocation *context)
392 {
393
394         gboolean autoscan = 0;
395
396         autoscan = netconfig_wifi_is_bgscan_paused();
397
398         wifi_complete_get_autoscan(wifi, context, autoscan);
399         return TRUE;
400 }
401
402 gboolean handle_get_autoscanmode(Wifi *wifi, GDBusMethodInvocation *context)
403 {
404         guint autoscanmode = 0;
405
406         autoscanmode = netconfig_wifi_bgscan_get_mode();
407
408         wifi_complete_get_autoscanmode(wifi, context, autoscanmode);
409         return TRUE;
410 }
411