[Fix][src/service] Add 'fall through' comment when break statement is not needed
[platform/upstream/connman.git] / src / device.c
old mode 100644 (file)
new mode 100755 (executable)
index 264c5e2..78b01f7
@@ -38,6 +38,10 @@ static GSList *device_list = NULL;
 static gchar **device_filter = NULL;
 static gchar **nodevice_filter = NULL;
 
+#if defined TIZEN_EXT
+static DBusConnection *connection;
+#endif
+
 enum connman_pending_type {
        PENDING_NONE    = 0,
        PENDING_ENABLE  = 1,
@@ -68,10 +72,24 @@ struct connman_device {
        char *last_network;
        struct connman_network *network;
        GHashTable *networks;
+#if defined TIZEN_EXT
+       time_t last_user_selection_time;
+       char *last_user_selection_ident;
+       char *last_connected_ident;
+       DBusMessage *pending_reply;
+       int max_scan_ssids;
+       bool is_5_0_ghz_supported;
+#endif
 };
 
 static void clear_pending_trigger(struct connman_device *device)
 {
+#if defined TIZEN_EXT
+       if (device->pending_reply) {
+               dbus_message_unref(device->pending_reply);
+               device->pending_reply = NULL;
+       }
+#endif
        if (device->pending_timeout > 0) {
                g_source_remove(device->pending_timeout);
                device->pending_timeout = 0;
@@ -166,6 +184,11 @@ static bool device_has_service_type(struct connman_device *device,
        if (device_service_type == CONNMAN_SERVICE_TYPE_UNKNOWN)
                return true;
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (device_service_type == CONNMAN_SERVICE_TYPE_MESH)
+               return service_type != CONNMAN_SERVICE_TYPE_MESH;
+#endif
+
        if (device_service_type == CONNMAN_SERVICE_TYPE_WIFI) {
                return service_type == CONNMAN_SERVICE_TYPE_WIFI ||
                        service_type == CONNMAN_SERVICE_TYPE_P2P;
@@ -180,6 +203,19 @@ static gboolean device_pending_reset(gpointer user_data)
 
        DBG("device %p", device);
 
+#if defined TIZEN_EXT
+       DBusMessage *reply;
+
+       /* Power request timed out, send ETIMEDOUT. */
+       if (device->pending_reply) {
+               reply = __connman_error_failed(device->pending_reply, ETIMEDOUT);
+               if (reply)
+                       g_dbus_send_message(connection, reply);
+
+               dbus_message_unref(device->pending_reply);
+               device->pending_reply = NULL;
+       }
+#endif
        /* Power request timedout, reset power pending state. */
        device->pending_timeout = 0;
        device->powered_pending = PENDING_NONE;
@@ -418,9 +454,59 @@ static void device_destruct(struct connman_device *device)
 
        g_free(device->last_network);
 
+#if defined TIZEN_EXT
+       g_free(device->last_user_selection_ident);
+       g_free(device->last_connected_ident);
+#endif
+
        g_free(device);
 }
 
+#if defined TIZEN_EXT
+static void device_send_changed(const char *ifname, enum connman_service_type type,
+                                                               const char *key, bool state)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, dict;
+       dbus_bool_t value = state;
+       const char *tech_path = connman_techonology_get_path(type);
+
+       if (!tech_path || !ifname)
+               return;
+
+       DBG("%s %s %s", ifname, key, state ? "TRUE" : "FALSE");
+
+       signal = dbus_message_new_signal(tech_path,
+                       CONNMAN_TECHNOLOGY_INTERFACE, "DeviceChanged");
+       if (!signal)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       connman_dbus_dict_open(&iter, &dict);
+       connman_dbus_dict_append_basic(&dict, "Ifname",
+                                       DBUS_TYPE_STRING,
+                                       &ifname);
+       connman_dbus_dict_append_basic(&dict, key,
+                                       DBUS_TYPE_BOOLEAN,
+                                       &value);
+       connman_dbus_dict_close(&iter, &dict);
+
+       dbus_connection_send(connection, signal, NULL);
+       dbus_message_unref(signal);
+}
+
+static void device_send_reply(struct connman_device *device)
+{
+       if (device->pending_reply) {
+               g_dbus_send_reply(connection,
+                               device->pending_reply, DBUS_TYPE_INVALID);
+               dbus_message_unref(device->pending_reply);
+               device->pending_reply = NULL;
+       }
+}
+#endif
+
 /**
  * connman_device_create:
  * @node: device node name (for example an address)
@@ -596,6 +682,10 @@ int connman_device_set_powered(struct connman_device *device,
        if (device->powered == powered)
                return -EALREADY;
 
+#if defined TIZEN_EXT
+       device_send_reply(device);
+#endif
+
        clear_pending_trigger(device);
 
        device->powered_pending = PENDING_NONE;
@@ -604,6 +694,11 @@ int connman_device_set_powered(struct connman_device *device,
 
        type = __connman_device_get_service_type(device);
 
+#if defined TIZEN_EXT
+       device_send_changed(device->interface, type, "Powered", powered);
+       technology_save_device(device);
+#endif
+
        if (!device->powered) {
                __connman_technology_disabled(type);
                return 0;
@@ -691,6 +786,194 @@ int connman_device_reconnect_service(struct connman_device *device)
        return 0;
 }
 
+#if defined TIZEN_EXT
+bool connman_device_set_last_user_selection_time(struct connman_device *device,
+                                               time_t time)
+{
+       if (device->last_user_selection_time != time) {
+               device->last_user_selection_time = time;
+               return true;
+       }
+
+       return false;
+}
+
+time_t connman_device_get_last_user_selection_time(struct connman_device *device)
+{
+       return device->last_user_selection_time;
+}
+
+bool connman_device_set_last_user_selection_ident(struct connman_device *device,
+                                               const char *ident)
+{
+       if (g_strcmp0(device->last_user_selection_ident, ident) != 0) {
+               g_free(device->last_user_selection_ident);
+               device->last_user_selection_ident = g_strdup(ident);
+
+               return true;
+       }
+
+       return false;
+}
+
+const char *connman_device_get_last_user_selection_ident(struct connman_device *device)
+{
+       return device->last_user_selection_ident;
+}
+
+bool connman_device_set_last_connected_ident(struct connman_device *device,
+                                               const char *ident)
+{
+       if (g_strcmp0(device->last_connected_ident, ident) != 0) {
+               g_free(device->last_connected_ident);
+               device->last_connected_ident = g_strdup(ident);
+
+               return true;
+       }
+
+       return false;
+}
+
+const char *connman_device_get_last_connected_ident(struct connman_device *device)
+{
+       return device->last_connected_ident;
+}
+
+void connman_device_save_last_user_selection(struct connman_device *device)
+{
+       GKeyFile *keyfile;
+       gchar *get_str;
+       gchar *selection_str;
+
+       keyfile = __connman_storage_load_ins();
+
+       selection_str = g_strdup_printf("%s:%ld",
+                       device->last_user_selection_ident, device->last_user_selection_time);
+
+       if (!keyfile) {
+               keyfile = g_key_file_new();
+
+               g_key_file_set_string(keyfile, device->interface, "LastUserSelection", selection_str);
+               DBG("%s", selection_str);
+               __connman_storage_save_ins(keyfile);
+
+       } else {
+               get_str = g_key_file_get_string(keyfile, device->interface, "LastUserSelection", NULL);
+               if (!get_str || g_strcmp0(get_str, selection_str) != 0) {
+                       g_key_file_set_string(keyfile, device->interface, "LastUserSelection", selection_str);
+                       DBG("%s -> %s", get_str, selection_str);
+                       __connman_storage_save_ins(keyfile);
+               }
+
+               g_free(get_str);
+       }
+
+       g_free(selection_str);
+       g_key_file_free(keyfile);
+}
+
+void connman_device_load_last_user_selection(struct connman_device *device)
+{
+       GKeyFile *keyfile;
+       gchar *get_str;
+       char **selection_str;
+
+       keyfile = __connman_storage_load_ins();
+       if (!keyfile)
+               return;
+
+       get_str = g_key_file_get_string(keyfile, device->interface, "LastUserSelection", NULL);
+       if (get_str) {
+               selection_str = g_strsplit(get_str, ":", 0);
+               if (selection_str) {
+                       time_t ref_time;
+                       struct tm* timeinfo;
+                       time_t last_user_selection_time;
+
+                       /* Only events that occur within 8 hours are counted. */
+                       ref_time = time(NULL);
+                       timeinfo = localtime(&ref_time);
+                       timeinfo->tm_hour -= 8;
+                       ref_time = mktime(timeinfo);
+
+                       last_user_selection_time = strtol(selection_str[1], NULL, 10);
+
+                       if (last_user_selection_time > ref_time) {
+                               if (g_strcmp0(selection_str[0], device->last_user_selection_ident) != 0) {
+                                       g_free(device->last_user_selection_ident);
+                                       device->last_user_selection_ident = g_strdup(selection_str[0]);
+                               }
+
+                               device->last_user_selection_time = last_user_selection_time;
+
+                               DBG("%s %ld", device->last_user_selection_ident, device->last_user_selection_time);
+                       }
+
+                       g_strfreev(selection_str);
+               }
+
+               g_free(get_str);
+       }
+
+       g_key_file_free(keyfile);
+}
+
+void connman_device_save_last_connected(struct connman_device *device)
+{
+       GKeyFile *keyfile;
+       gchar *get_str;
+
+       if (!device->last_connected_ident)
+               return;
+
+       keyfile = __connman_storage_load_ins();
+
+       if (!keyfile) {
+               keyfile = g_key_file_new();
+
+               g_key_file_set_string(keyfile, device->interface, "LastConnected", device->last_connected_ident);
+               DBG("%s", device->last_connected_ident);
+               __connman_storage_save_ins(keyfile);
+
+       } else {
+               get_str = g_key_file_get_string(keyfile, device->interface, "LastConnected", NULL);
+               if (!get_str || g_strcmp0(get_str, device->last_connected_ident) != 0) {
+                       g_key_file_set_string(keyfile, device->interface, "LastConnected", device->last_connected_ident);
+                       DBG("%s -> %s", get_str, device->last_connected_ident);
+                       __connman_storage_save_ins(keyfile);
+               }
+
+               g_free(get_str);
+       }
+
+       g_key_file_free(keyfile);
+}
+
+void connman_device_load_last_connected(struct connman_device *device)
+{
+       GKeyFile *keyfile;
+       gchar *get_str;
+
+       keyfile = __connman_storage_load_ins();
+       if (!keyfile)
+               return;
+
+       get_str = g_key_file_get_string(keyfile, device->interface, "LastConnected", NULL);
+       if (get_str) {
+               if (g_strcmp0(get_str, device->last_connected_ident) != 0) {
+                       g_free(device->last_connected_ident);
+                       device->last_connected_ident = g_strdup(get_str);
+               }
+
+               DBG("%s", device->last_connected_ident);
+
+               g_free(get_str);
+       }
+
+       g_key_file_free(keyfile);
+}
+#endif
+
 static void mark_network_available(gpointer key, gpointer value,
                                                        gpointer user_data)
 {
@@ -791,6 +1074,11 @@ int connman_device_set_scanning(struct connman_device *device,
 
        __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO);
 
+#if defined TIZEN_EXT_WIFI_MESH
+       if (type == CONNMAN_SERVICE_TYPE_MESH)
+               __connman_mesh_auto_connect();
+#endif
+
        return 0;
 }
 
@@ -894,6 +1182,54 @@ struct connman_network *connman_device_get_network(struct connman_device *device
        return g_hash_table_lookup(device->networks, identifier);
 }
 
+#if defined TIZEN_EXT
+struct connman_network *connman_device_get_default_network(
+                                                       struct connman_device *device)
+{
+       return device->network;
+}
+
+void connman_device_set_pending_reply(struct connman_device *device,
+                                                       DBusMessage *msg)
+{
+       device->pending_reply = dbus_message_ref(msg);
+}
+
+void connman_device_send_connected_signal(struct connman_device *device,
+                                                       bool connected)
+{
+       enum connman_service_type type;
+
+       if (!device)
+               return;
+
+       type = __connman_device_get_service_type(device);
+       device_send_changed(device->interface, type, "Connected", connected);
+}
+
+void connman_device_set_max_scan_ssids(struct connman_device *device,
+                                                       int max_scan_ssids)
+{
+       device->max_scan_ssids = max_scan_ssids;
+}
+
+int connman_device_get_max_scan_ssids(struct connman_device *device)
+{
+       return device->max_scan_ssids;
+}
+
+void connman_device_set_wifi_5ghz_supported(struct connman_device *device,
+                                                       bool is_5_0_ghz_supported)
+{
+       device->is_5_0_ghz_supported = is_5_0_ghz_supported;
+}
+
+bool connman_device_get_wifi_5ghz_supported(struct connman_device *device)
+{
+       return device->is_5_0_ghz_supported;
+}
+#endif
+
 /**
  * connman_device_remove_network:
  * @device: device structure
@@ -1090,6 +1426,225 @@ void connman_device_regdom_notify(struct connman_device *device,
        __connman_technology_notify_regdom_by_device(device, result, alpha2);
 }
 
+#if defined TIZEN_EXT
+static int device_specific_scan(enum connman_service_type type,
+                               struct connman_device *device,
+                               int scan_type, GSList *specific_scan_list)
+{
+       if (!device->driver || !device->driver->specific_scan)
+               return -EOPNOTSUPP;
+
+       if (!device->powered)
+               return -ENOLINK;
+
+       return device->driver->specific_scan(type, device, scan_type,
+                       specific_scan_list, NULL);
+}
+
+int __connman_device_request_specific_scan(enum connman_service_type type,
+                               const char *ifname, int scan_type, GSList *specific_scan_list)
+{
+       bool success = false;
+       int last_err = -ENOSYS;
+       GSList *list;
+       int err;
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EOPNOTSUPP;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
+               break;
+       }
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       if (type == CONNMAN_SERVICE_TYPE_P2P) {
+                               if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+                                       continue;
+                       } else if (service_type != type)
+                               continue;
+               }
+
+               if (ifname && g_strcmp0(device->interface, ifname) != 0)
+                       continue;
+
+               err = device_specific_scan(type, device, scan_type, specific_scan_list);
+               if (err == 0 || err == -EINPROGRESS) {
+                       success = true;
+               } else {
+                       last_err = err;
+                       DBG("device %p err %d", device, err);
+               }
+       }
+
+       if (success)
+               return 0;
+
+       return last_err;
+}
+
+int connman_device_request_device_scan(enum connman_service_type type,
+                               const char * ifname, bool force_full_scan)
+{
+       bool success = false;
+       int last_err = -ENOSYS;
+       GSList *list;
+       int err;
+
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_SYSTEM:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+       case CONNMAN_SERVICE_TYPE_BLUETOOTH:
+       case CONNMAN_SERVICE_TYPE_CELLULAR:
+       case CONNMAN_SERVICE_TYPE_GPS:
+       case CONNMAN_SERVICE_TYPE_VPN:
+       case CONNMAN_SERVICE_TYPE_GADGET:
+               return -EOPNOTSUPP;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
+               break;
+       }
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               if (!device_has_service_type(device, type))
+                       continue;
+
+               if (g_strcmp0(device->interface, ifname) != 0)
+                       continue;
+
+               err = device_scan(type, device, force_full_scan);
+
+               if (err == 0 || err == -EINPROGRESS) {
+                       success = true;
+               } else {
+                       last_err = err;
+                       DBG("device %p err %d", device, err);
+               }
+               break;
+       }
+
+       if (success)
+               return 0;
+
+       return last_err;
+}
+
+#if defined TIZEN_EXT_WIFI_MESH
+static int device_abort_scan(enum connman_service_type type,
+                               struct connman_device *device)
+{
+       if (!device->driver || !device->driver->scan)
+               return -EOPNOTSUPP;
+
+       if (!device->powered)
+               return -ENOLINK;
+
+       return device->driver->abort_scan(type, device);
+}
+
+int __connman_device_abort_scan(enum connman_service_type type)
+{
+       GSList *list;
+       int err = -EINVAL;
+
+       if (type != CONNMAN_SERVICE_TYPE_MESH)
+               return -EINVAL;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       if (type == CONNMAN_SERVICE_TYPE_MESH)
+                               if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+                                       continue;
+
+                       if (!device->scanning) {
+                               err = -EEXIST;
+                               continue;
+                       }
+
+                       err = device_abort_scan(type, device);
+               }
+       }
+       return err;
+}
+
+static int device_mesh_specific_scan(enum connman_service_type type,
+                               struct connman_device *device, const char *name,
+                               unsigned int freq)
+{
+       if (!device->driver || !device->driver->mesh_specific_scan)
+               return -EOPNOTSUPP;
+
+       if (!device->powered)
+               return -ENOLINK;
+
+       return device->driver->mesh_specific_scan(type, device, name, freq, NULL);
+}
+
+int __connman_device_request_mesh_specific_scan(enum connman_service_type type,
+                                               const char *name,
+                                               unsigned int freq)
+{
+       bool success = false;
+       int last_err = -ENOSYS;
+       GSList *list;
+       int err;
+
+       if (type != CONNMAN_SERVICE_TYPE_MESH)
+               return -EINVAL;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+               enum connman_service_type service_type =
+                       __connman_device_get_service_type(device);
+
+               if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) {
+                       if (type == CONNMAN_SERVICE_TYPE_MESH)
+                               if (service_type != CONNMAN_SERVICE_TYPE_WIFI)
+                                       continue;
+               }
+
+               err = device_mesh_specific_scan(type, device, name, freq);
+               if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
+                       success = true;
+               } else {
+                       last_err = err;
+                       DBG("device %p err %d", device, err);
+               }
+       }
+
+       if (success)
+               return 0;
+
+       return last_err;
+}
+#endif /* TIZEN_EXT_WIFI_MESH */
+#endif
+
 static int connman_device_request_scan(enum connman_service_type type,
                                        bool force_full_scan)
 {
@@ -1110,6 +1665,9 @@ static int connman_device_request_scan(enum connman_service_type type,
                return -EOPNOTSUPP;
        case CONNMAN_SERVICE_TYPE_WIFI:
        case CONNMAN_SERVICE_TYPE_P2P:
+#if defined TIZEN_EXT_WIFI_MESH
+       case CONNMAN_SERVICE_TYPE_MESH:
+#endif
                break;
        }
 
@@ -1120,7 +1678,15 @@ static int connman_device_request_scan(enum connman_service_type type,
                        continue;
 
                err = device_scan(type, device, force_full_scan);
+#if defined TIZEN_EXT
+               /* When Scan is already in progress then return Error so that
+                * wifi-manager can block the scan-done signal to be sent to
+                * application and start requested scan after scan already in progress
+                * is completed then notify to application about the scan event */
+               if (err == 0 || err == -EINPROGRESS) {
+#else
                if (err == 0 || err == -EALREADY || err == -EINPROGRESS) {
+#endif
                        success = true;
                } else {
                        last_err = err;
@@ -1183,7 +1749,11 @@ void __connman_device_stop_scan(enum connman_service_type type)
        }
 }
 
+#if defined TIZEN_EXT
+char *index2ident(int index, const char *prefix)
+#else
 static char *index2ident(int index, const char *prefix)
+#endif
 {
        struct ifreq ifr;
        struct ether_addr eth;
@@ -1229,7 +1799,11 @@ static char *index2ident(int index, const char *prefix)
        return str;
 }
 
+#if defined TIZEN_EXT
+char *index2addr(int index)
+#else
 static char *index2addr(int index)
+#endif
 {
        struct ifreq ifr;
        struct ether_addr eth;
@@ -1338,6 +1912,10 @@ struct connman_device *connman_device_create_from_index(int index)
 
        connman_device_set_index(device, index);
        connman_device_set_interface(device, devname);
+#if defined TIZEN_EXT
+       connman_device_load_last_connected(device);
+       connman_device_load_last_user_selection(device);
+#endif
 
        if (ident) {
                connman_device_set_ident(device, ident);
@@ -1469,6 +2047,9 @@ static void cleanup_devices(void)
 
                DBG("cleaning up %s index %d", interfaces[i], index);
 
+#if defined TIZEN_EXT
+               if (strcmp(interfaces[i], "wlan0") != 0)
+#endif
                connman_inet_ifdown(index);
 
                /*
@@ -1484,6 +2065,10 @@ int __connman_device_init(const char *device, const char *nodevice)
 {
        DBG("");
 
+#if defined TIZEN_EXT
+       connection = connman_dbus_get_connection();
+#endif
+
        if (device)
                device_filter = g_strsplit(device, ",", -1);
 
@@ -1501,4 +2086,8 @@ void __connman_device_cleanup(void)
 
        g_strfreev(nodevice_filter);
        g_strfreev(device_filter);
+
+#if defined TIZEN_EXT
+       dbus_connection_unref(connection);
+#endif
 }