+/*
+ * Network Configuration Module
+ *
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <glib.h>
+#include <vconf.h>
+#include <vconf-keys.h>
+
+#include "log.h"
+#include "util.h"
+#include "netdbus.h"
+#include "wifi-state.h"
+#include "wifi-scan.h"
+
+typedef struct {
+ const char *interface_name;
+
+ /* background scan */
+ guint bg_interval;
+ guint bg_mode;
+ guint bg_timer_id;
+ gboolean bg_paused;
+
+ /* bssid scan*/
+ gboolean bssid_enabled;
+ gboolean bssid_aborted;
+ gboolean bssid_scanning;
+
+ /* scan */
+ gboolean scanning;
+} scan_data_s;
+
+static GSList *g_scan_list = NULL;
+
+static scan_data_s *__wifi_scan_create_data(const char *interface_name)
+{
+ scan_data_s *scan_data = g_try_malloc0(sizeof(scan_data_s));
+ if (!scan_data) {
+ ERR("Memory allocation failed");
+ return NULL;
+ }
+
+ scan_data->interface_name = g_strdup(interface_name);
+
+ scan_data->scanning = FALSE;
+
+ scan_data->bg_interval = SCAN_EXPONENTIAL_MIN;
+ scan_data->bg_mode = WIFI_BGSCAN_MODE_EXPONENTIAL;
+ scan_data->bg_timer_id = 0;
+ scan_data->bg_paused = FALSE;
+
+ scan_data->bssid_enabled = FALSE;
+ scan_data->bssid_aborted = FALSE;
+ scan_data->bssid_scanning = FALSE;
+
+ g_scan_list = g_slist_append(g_scan_list, scan_data);
+
+ return scan_data;
+}
+
+static scan_data_s *__wifi_scan_get_data(const char *interface_name)
+{
+ GSList *list;
+ scan_data_s *scan_data;
+
+ for (list = g_scan_list; list; list = list->next) {
+ scan_data = list->data;
+ if (g_strcmp0(scan_data->interface_name, interface_name) == 0)
+ return scan_data;
+ }
+
+ scan_data = __wifi_scan_create_data(interface_name);
+ return scan_data;
+}
+
+void netconfig_wifi_scan_set_scanning(const char *interface_name, gboolean scanning)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return;
+
+ scan_data->scanning = scanning;
+}
+
+guint netconfig_wifi_scan_get_scanning(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return FALSE;
+
+ return scan_data->scanning;;
+}
+
+gboolean netconfig_wifi_bgscan_set_mode(const char *interface_name, guint mode)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return FALSE;
+
+ if (mode != WIFI_BGSCAN_MODE_EXPONENTIAL && mode != WIFI_BGSCAN_MODE_PERIODIC) {
+ ERR("Invalid scan mode [%d]", mode);
+ return FALSE;
+ }
+
+ scan_data->bg_mode = mode;
+
+ return TRUE;
+}
+
+guint netconfig_wifi_bgscan_get_mode(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return 0;
+
+ return scan_data->bg_mode;
+}
+
+void netconfig_wifi_bgscan_set_timer_id(const char *interface_name, guint timer_id)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return;
+
+ scan_data->bg_timer_id = timer_id;
+}
+
+guint netconfig_wifi_bgscan_get_timer_id(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return 0;
+
+ return scan_data->bg_timer_id;
+}
+
+gboolean netconfig_wifi_bgscan_set_interval(const char *interface_name, guint interval)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return FALSE;
+
+ if ((interval < SCAN_EXPONENTIAL_MIN) || (interval > SCAN_EXPONENTIAL_MAX)) {
+ ERR("Invalid interval [%d]", interval);
+ return FALSE;
+ }
+
+ scan_data->bg_interval = interval;
+
+ return TRUE;
+}
+
+guint netconfig_wifi_bgscan_get_interval(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return SCAN_EXPONENTIAL_MIN;
+
+ return scan_data->bg_interval;
+}
+
+void netconfig_wifi_bgscan_set_pause(const char *interface_name, gboolean pause)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+ if (!scan_data)
+ return;
+
+ scan_data->bg_paused = pause;
+
+ DBG("[%s] Wi-Fi background scan [%s]", interface_name,
+ pause ? "Pause" : "Resume");
+}
+
+gboolean netconfig_wifi_bgscan_is_paused(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+ if (!scan_data)
+ return FALSE;
+
+ DBG("[%s] Wi-Fi background scan is [%s]", interface_name,
+ scan_data->bg_paused ? "Paused" : "Runnable");
+
+ return scan_data->bg_paused;
+}
+
+static void __wifi_scan_request_reply(GObject *source_object,
+ GAsyncResult *res, gpointer user_data)
+{
+ GVariant *reply;
+ GDBusConnection *conn = NULL;
+ GError *error = NULL;
+ char *interface_name = user_data;
+
+ conn = G_DBUS_CONNECTION(source_object);
+ reply = g_dbus_connection_call_finish(conn, res, &error);
+
+ if (reply == NULL) {
+ if (error != NULL) {
+ ERR("Fail to request status [%d: %s]", error->code, error->message);
+ netconfig_wifi_scan_set_scanning(interface_name, FALSE);
+ g_error_free(error);
+ } else {
+ ERR("Fail to request scan");
+ netconfig_wifi_scan_set_scanning(interface_name, FALSE);
+ }
+ } else {
+ DBG("Successfully requested");
+ g_variant_unref(reply);
+ }
+
+ netconfig_gdbus_pending_call_unref();
+ g_free(interface_name);
+}
+
+static gboolean __wifi_scan_request_connman_scan(const char *interface_name)
+{
+ GVariant *params = NULL;
+ gboolean reply = FALSE;
+
+ netconfig_wifi_scan_set_scanning(interface_name, TRUE);
+
+ params = g_variant_new("(s)", interface_name);
+
+ reply = netconfig_invoke_dbus_method_nonblock(CONNMAN_SERVICE,
+ CONNMAN_WIFI_TECHNOLOGY_PREFIX,
+ CONNMAN_TECHNOLOGY_INTERFACE, "ScanDevice", params,
+ __wifi_scan_request_reply, g_strdup(interface_name));
+ if (reply != TRUE) {
+ ERR("Failed to send Scan request to connman");
+ netconfig_wifi_scan_set_scanning(interface_name, FALSE);
+ }
+
+ return reply;
+}
+
+static gboolean __wifi_bgscan_immediate_scan(gpointer data)
+{
+ static int retries = 0;
+ scan_data_s *scan_data = data;
+ guint state = 0;
+
+ if (!scan_data)
+ return FALSE;
+
+ state = wifi_state_get_service_state(scan_data->interface_name);
+
+#if !defined TIZEN_WEARABLE
+ if (netconfig_wifi_bgscan_is_paused(scan_data->interface_name)) {
+ return FALSE;
+ }
+#endif
+
+ if (state == NETCONFIG_WIFI_CONNECTED) {
+ if (netconfig_wifi_bgscan_get_mode(scan_data->interface_name) == WIFI_BGSCAN_MODE_EXPONENTIAL) {
+ DBG("[%s] Wi-Fi state is connected, scan is paused", scan_data->interface_name);
+ return FALSE;
+ }
+ } else if (state == NETCONFIG_WIFI_ASSOCIATION || state == NETCONFIG_WIFI_CONFIGURATION) {
+ /* During WIFI connecting, WIFI can be disappeared.
+ * After 1 sec, try scan even if connecting state */
+ if (retries < 2) {
+ retries++;
+ return TRUE;
+ }
+ }
+
+ if (__wifi_scan_request_connman_scan(scan_data->interface_name) == TRUE) {
+ retries = 0;
+ return FALSE;
+ } else if (retries > 2) {
+ retries = 0;
+ return FALSE;
+ }
+
+ retries++;
+
+ return TRUE;
+}
+
+static gboolean __wifi_bgscan_next_scan(gpointer data)
+{
+ int pm_state = VCONFKEY_PM_STATE_NORMAL;
+ scan_data_s *scan_data = data;
+
+ if (!scan_data)
+ return FALSE;
+
+ /* In case of LCD off, we don't need Wi-Fi scan */
+ netconfig_vconf_get_int(VCONFKEY_PM_STATE, &pm_state);
+ if (pm_state >= VCONFKEY_PM_STATE_LCDOFF)
+ return TRUE;
+
+ netconfig_wifi_bgscan_start_timer(scan_data->interface_name, TRUE);
+
+ return FALSE;
+}
+
+void netconfig_wifi_bgscan_start_timer(const char *interface_name, gboolean immediate_scan)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return;
+
+ netconfig_stop_timer(&(scan_data->bg_timer_id));
+
+ switch (scan_data->bg_mode) {
+ case WIFI_BGSCAN_MODE_EXPONENTIAL:
+ if (scan_data->bg_interval < SCAN_EXPONENTIAL_MIN ||
+ scan_data->bg_interval == SCAN_PERIODIC_DELAY)
+ scan_data->bg_interval = SCAN_EXPONENTIAL_MIN;
+ break;
+ case WIFI_BGSCAN_MODE_PERIODIC:
+ scan_data->bg_interval = SCAN_PERIODIC_DELAY;
+ break;
+ default:
+ DBG("Invalid Wi-Fi background scan mode[%d]", scan_data->bg_mode);
+ return;
+ }
+
+ if (immediate_scan)
+ DBG("[%s] Scan immediately[%d], mode[%d](0 exponential / 1 periodic)",
+ interface_name, immediate_scan, scan_data->bg_mode);
+ else
+ DBG("[%s] Scan immediately[%d], mode[%d](0 exponential / 1 periodic), next[%d]",
+ interface_name, immediate_scan, scan_data->bg_mode, scan_data->bg_interval);
+
+ if (immediate_scan)
+ g_timeout_add(500, __wifi_bgscan_immediate_scan, scan_data);
+
+ netconfig_start_timer_seconds(scan_data->bg_interval,
+ __wifi_bgscan_next_scan, scan_data, &(scan_data->bg_timer_id));
+
+ if (scan_data->bg_mode == WIFI_BGSCAN_MODE_EXPONENTIAL && immediate_scan) {
+ scan_data->bg_interval = scan_data->bg_interval * 2;
+ if (scan_data->bg_interval > SCAN_EXPONENTIAL_MAX)
+ scan_data->bg_interval = SCAN_EXPONENTIAL_MAX;
+ }
+}
+
+void netconfig_wifi_bgscan_stop_timer(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (scan_data)
+ return;
+
+ netconfig_stop_timer(&(scan_data->bg_timer_id));
+}
+
+void netconfig_wifi_bssidscan_set_mode(const char *interface_name, gboolean enable)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+ if (!scan_data)
+ return;
+
+ scan_data->bssid_enabled = enable;
+}
+
+gboolean netconfig_wifi_bssidscan_get_mode(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+ if (!scan_data)
+ return FALSE;
+
+ return scan_data->bssid_enabled;
+}
+
+void netconfig_wifi_bssidscan_set_aborted(const char *interface_name, gboolean abort)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+ if (!scan_data)
+ return;
+
+ scan_data->bssid_aborted = abort;
+}
+
+gboolean netconfig_wifi_bssidscan_get_aborted(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+ if (!scan_data)
+ return FALSE;
+
+ return scan_data->bssid_aborted;
+}
+
+void netconfig_wifi_bssidscan_set_scanning(const char *interface_name, gboolean scanning)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return;
+
+ scan_data->bssid_scanning = scanning;
+}
+
+guint netconfig_wifi_bssidscan_get_scanning(const char *interface_name)
+{
+ scan_data_s *scan_data = __wifi_scan_get_data(interface_name);
+
+ if (!scan_data)
+ return FALSE;
+
+ return scan_data->bssid_scanning;;
+}