+{
+     gpointer *list;
+     gsize i, j;
+
+     list = (gpointer *) ap_list_get_sorted (self,
include_without_ssid);
+     for (i = 0, j = 0; list[i]; i++) {
+             NMWifiAP *ap = list[i];
+             const char *path;
+
+             /* update @list inplace to hold instead the export-
path. */
+             path = nm_exported_object_get_path
(NM_EXPORTED_OBJECT (ap));
+             nm_assert (path);
+             list[j++] = (gpointer) path;
+     }
+     return (const char **) list;
+}
+
+static void
+impl_device_iwd_get_access_points (NMDeviceIwd *self,
+                                    GDBusMethodInvocation *context)
+{
+     gs_free const char **list = NULL;
+     GVariant *v;
+
+     list = ap_list_get_sorted_paths (self, FALSE);
+     v = g_variant_new_objv (list, -1);
+     g_dbus_method_invocation_return_value (context,
g_variant_new_tuple (&v, 1));
+}
+
+static void
+impl_device_iwd_get_all_access_points (NMDeviceIwd *self,
+                                        GDBusMethodInvocation
*context)
+{
+     gs_free const char **list = NULL;
+     GVariant *v;
+
+     list = ap_list_get_sorted_paths (self, TRUE);
+     v = g_variant_new_objv (list, -1);
+     g_dbus_method_invocation_return_value (context,
g_variant_new_tuple (&v, 1));
+}
+
+static gboolean
+check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+{
+     gboolean prohibited = FALSE;
+
+     g_signal_emit (self, signals[SCANNING_PROHIBITED], 0,
periodic, &prohibited);
+     return prohibited;
+}
+
+static void
+dbus_request_scan_cb (NMDevice *device,
+                      GDBusMethodInvocation *context,
+                      NMAuthSubject *subject,
+                      GError *error,
+                      gpointer user_data)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (device);
+     NMDeviceIwdPrivate *priv;
+     gs_unref_variant GVariant *scan_options = user_data;
+     gs_unref_ptrarray GPtrArray *ssids = NULL;
+
+     if (error) {
+             g_dbus_method_invocation_return_gerror (context,
error);
+             return;
+     }
+
+     if (check_scanning_prohibited (self, FALSE)) {
+             g_dbus_method_invocation_return_error_literal
(context,
+                                                            NM_DE
VICE_ERROR,
+                                                            NM_DE
VICE_ERROR_NOT_ALLOWED,
+                                                            "Scan
ning not allowed at this time");
+             return;
+     }
+
+     priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     if (   !priv->enabled
+         || !priv->dbus_obj
+         || nm_device_get_state (device) <
NM_DEVICE_STATE_DISCONNECTED
+         || nm_device_is_activating (device)) {
+             g_dbus_method_invocation_return_error_literal
(context,
+                                                            NM_DE
VICE_ERROR,
+                                                            NM_DE
VICE_ERROR_NOT_ALLOWED,
+                                                            "Scan
ning not allowed while unavailable");
+             return;
+     }
+
+     if (scan_options) {
+             gs_unref_variant GVariant *val =
g_variant_lookup_value (scan_options, "ssids", NULL);
+
+             if (val) {
+                     g_dbus_method_invocation_return_error_litera
l (context,
+
   NM_DEVICE_ERROR,
+
   NM_DEVICE_ERROR_NOT_ALLOWED,
+
   "'ssid' scan option not supported");
+                     return;
+             }
+     }
+
+     g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new
("()"),
+                        G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL,
NULL);
+     g_dbus_method_invocation_return_value (context, NULL);
+}
+
+static void
+impl_device_iwd_request_scan (NMDeviceIwd *self,
+                               GDBusMethodInvocation *context,
+                               GVariant *options)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     NMDevice *device = NM_DEVICE (self);
+
+     if (   !priv->enabled
+         || !priv->dbus_obj
+         || nm_device_get_state (device) <
NM_DEVICE_STATE_DISCONNECTED
+         || nm_device_is_activating (device)) {
+             g_dbus_method_invocation_return_error_literal
(context,
+                                                            NM_DE
VICE_ERROR,
+                                                            NM_DE
VICE_ERROR_NOT_ALLOWED,
+                                                            "Scan
ning not allowed while unavailable");
+             return;
+     }
+
+     /* Ask the manager to authenticate this request for us */
+     g_signal_emit_by_name (device,
+                            NM_DEVICE_AUTH_REQUEST,
+                            context,
+                            NULL,
+                            NM_AUTH_PERMISSION_NETWORK_CONTROL,
+                            TRUE,
+                            dbus_request_scan_cb,
+                            options ? g_variant_ref (options) :
NULL);
+}
+
+static gboolean
+scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     g_return_val_if_fail (priv->dbus_obj != NULL, TRUE);
+
+     switch (nm_device_get_state (NM_DEVICE (self))) {
+     case NM_DEVICE_STATE_UNKNOWN:
+     case NM_DEVICE_STATE_UNMANAGED:
+     case NM_DEVICE_STATE_UNAVAILABLE:
+     case NM_DEVICE_STATE_PREPARE:
+     case NM_DEVICE_STATE_CONFIG:
+     case NM_DEVICE_STATE_NEED_AUTH:
+     case NM_DEVICE_STATE_IP_CONFIG:
+     case NM_DEVICE_STATE_IP_CHECK:
+     case NM_DEVICE_STATE_SECONDARIES:
+     case NM_DEVICE_STATE_DEACTIVATING:
+             /* Prohibit scans when unusable or activating */
+             return TRUE;
+     case NM_DEVICE_STATE_DISCONNECTED:
+     case NM_DEVICE_STATE_FAILED:
+             /* Can always scan when disconnected */
+             return FALSE;
+     case NM_DEVICE_STATE_ACTIVATED:
+             break;
+     }
+
+     /* Prohibit scans if IWD is busy */
+     return !priv->can_scan;
+}
+
+static void
+wifi_secrets_cb (NMActRequest *req,
+                 NMActRequestGetSecretsCallId call_id,
+                 NMSettingsConnection *connection,
+                 GError *error,
+                 gpointer user_data)
+{
+     NMDevice *device = user_data;
+     NMDeviceIwd *self = user_data;
+     NMDeviceIwdPrivate *priv;
+
+     g_return_if_fail (NM_IS_DEVICE_IWD (self));
+     g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+     priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     g_return_if_fail (priv->wifi_secrets_id == call_id);
+
+     priv->wifi_secrets_id = NULL;
+
+     if (g_error_matches (error, G_IO_ERROR,
G_IO_ERROR_CANCELLED))
+             return;
+
+     g_return_if_fail (req == nm_device_get_act_request
(device));
+     g_return_if_fail (nm_device_get_state (device) ==
NM_DEVICE_STATE_NEED_AUTH);
+     g_return_if_fail (nm_act_request_get_settings_connection
(req) == connection);
+
+     if (error) {
+             _LOGW (LOGD_WIFI, "%s", error->message);
+
+             nm_device_state_changed (device,
+                                      NM_DEVICE_STATE_FAILED,
+                                      NM_DEVICE_STATE_REASON_NO_S
ECRETS);
+     } else
+             nm_device_activate_schedule_stage1_device_prepare
(device);
+}
+
+static void
+wifi_secrets_cancel (NMDeviceIwd *self)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     if (priv->wifi_secrets_id)
+             nm_act_request_cancel_secrets (NULL, priv-
wifi_secrets_id);
+     nm_assert (!priv->wifi_secrets_id);
+}
+
+static void
+wifi_secrets_get_secrets (NMDeviceIwd *self,
+                          const char *setting_name,
+                          NMSecretAgentGetSecretsFlags flags)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     NMActRequest *req;
+
+     wifi_secrets_cancel (self);
+
+     req = nm_device_get_act_request (NM_DEVICE (self));
+     g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+     priv->wifi_secrets_id = nm_act_request_get_secrets (req,
+                                                         TRUE,
+                                                         setting_
name,
+                                                         flags,
+                                                         NULL,
+                                                         wifi_sec
rets_cb,
+                                                         self);
+     g_return_if_fail (priv->wifi_secrets_id);
+}
+
+static gboolean
+need_new_8021x_secrets (NMDeviceIwd *self,
+                        const char **setting_name)
+{
+     NMSetting8021x *s_8021x;
+     NMSettingWirelessSecurity *s_wsec;
+     NMSettingSecretFlags secret_flags =
NM_SETTING_SECRET_FLAG_NONE;
+     NMConnection *connection;
+
+     g_assert (setting_name != NULL);
+
+     connection = nm_device_get_applied_connection (NM_DEVICE
(self));
+     g_return_val_if_fail (connection != NULL, FALSE);
+
+     /* If it's an 802.1x or LEAP connection with "always
ask"/unsaved secrets
+      * then we need to ask again because it might be an OTP
token and the PIN
+      * may have changed.
+      */
+
+     s_8021x = nm_connection_get_setting_802_1x (connection);
+     if (s_8021x) {
+             if (!nm_setting_get_secret_flags (NM_SETTING
(s_8021x),
+                                               NM_SETTING_802_1X_
PASSWORD,
+                                               &secret_flags,
+                                               NULL))
+                     g_assert_not_reached ();
+             if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+                     *setting_name =
NM_SETTING_802_1X_SETTING_NAME;
+             return *setting_name ? TRUE : FALSE;
+     }
+
+     s_wsec = nm_connection_get_setting_wireless_security
(connection);
+     if (s_wsec) {
+             if (!nm_setting_get_secret_flags (NM_SETTING
(s_wsec),
+                                               NM_SETTING_WIRELES
S_SECURITY_LEAP_PASSWORD,
+                                               &secret_flags,
+                                               NULL))
+                     g_assert_not_reached ();
+             if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+                     *setting_name =
NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+             return *setting_name ? TRUE : FALSE;
+     }
+
+     /* Not a LEAP or 802.1x connection */
+     return FALSE;
+}
+
+static gboolean
+need_new_wpa_psk (NMDeviceIwd *self,
+                  const char **setting_name)
+{
+     NMSettingWirelessSecurity *s_wsec;
+     NMConnection *connection;
+     const char *key_mgmt = NULL;
+
+     g_assert (setting_name != NULL);
+
+     connection = nm_device_get_applied_connection (NM_DEVICE
(self));
+     g_return_val_if_fail (connection != NULL, FALSE);
+
+     s_wsec = nm_connection_get_setting_wireless_security
(connection);
+     if (s_wsec)
+             key_mgmt = nm_setting_wireless_security_get_key_mgmt
(s_wsec);
+
+     if (g_strcmp0 (key_mgmt, "wpa-psk") == 0) {
+             /* We don't have any data from IWD about the
disconnect
+              * reason or association state when the disconnect
happened
+              * so just assume it was a bad password.
+              */
+             *setting_name =
NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+             return TRUE;
+     }
+
+     /* Not a WPA-PSK connection */
+     return FALSE;
+}
+
+static gboolean
+handle_8021x_or_psk_auth_fail (NMDeviceIwd *self)
+{
+     NMDevice *device = NM_DEVICE (self);
+     NMActRequest *req;
+     const char *setting_name = NULL;
+     gboolean handled = FALSE;
+
+     req = nm_device_get_act_request (NM_DEVICE (self));
+     g_return_val_if_fail (req != NULL, FALSE);
+
+     if (   need_new_8021x_secrets (self, &setting_name)
+         || need_new_wpa_psk (self, &setting_name)) {
+             nm_act_request_clear_secrets (req);
+
+             _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                    "Activation: (wifi) disconnected during
association, asking for new key");
+
+             cleanup_association_attempt (self, FALSE);
+             nm_device_state_changed (device,
NM_DEVICE_STATE_NEED_AUTH,
+                                      NM_DEVICE_STATE_REASON_SUPP
LICANT_DISCONNECT);
+             wifi_secrets_get_secrets (self,
+                                       setting_name,
+                                       NM_SECRET_AGENT_GET_SECRET
S_FLAG_ALLOW_INTERACTION
+                                         |
NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
+             handled = TRUE;
+     }
+
+     return handled;
+}
+
+static void
+network_connect_cb (GObject *source, GAsyncResult *res, gpointer
user_data)
+{
+     NMDeviceIwd *self = user_data;
+     NMDevice *device = NM_DEVICE (self);
+     gs_free_error GError *error = NULL;
+     gs_unref_variant GVariant *variant = NULL;
+     NMConnection *connection;
+     NMSettingWireless *s_wifi;
+     GBytes *ssid;
+
+     if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
+                                      G_VARIANT_TYPE ("()"),
+                                      &error)) {
+             gs_free gchar *dbus_error = NULL;
+
+             /* Connection failed; radio problems or if the
network wasn't
+              * open, the passwords or certificates may be wrong.
+              */
+
+             _LOGE (LOGD_DEVICE | LOGD_WIFI,
+                    "Activation: (wifi) Network.Connect failed:
%s",
+                    error->message);
+
+             connection = nm_device_get_applied_connection
(NM_DEVICE (self));
+             if (!connection ||
nm_connection_get_setting_wireless_security (connection))
+                     goto failed;
+
+             if (g_error_matches (error, G_IO_ERROR,
G_IO_ERROR_DBUS_ERROR))
+                     dbus_error = g_dbus_error_get_remote_error
(error);
+
+             /* If secrets were wrong, we'd be getting a
net.connman.iwd.Failed */
+             if (nm_streq0 (dbus_error,
"net.connman.iwd.Failed")) {
+                     if (handle_8021x_or_psk_auth_fail (self)) {
+                             _LOGW (LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) asking for new secrets");
+                     } else {
+                             cleanup_association_attempt (self,
FALSE);
+                             nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
+                                                      NM_DEVICE_S
TATE_REASON_NO_SECRETS);
+                     }
+             } else if (   !nm_utils_error_is_cancelled (error,
TRUE)
+                        && nm_device_is_activating (device))
+                     goto failed;
+
+             /* Call Disconnect to make sure IWD's autoconnect is
disabled */
+             cleanup_association_attempt (self, TRUE);
+
+             return;
+     }
+
+     nm_assert (nm_device_get_state (device) ==
NM_DEVICE_STATE_CONFIG);
+
+     connection = nm_device_get_applied_connection (device);
+     if (!connection)
+             goto failed;
+
+     s_wifi = nm_connection_get_setting_wireless (connection);
+     if (!s_wifi)
+             goto failed;
+
+     ssid = nm_setting_wireless_get_ssid (s_wifi);
+     if (!ssid)
+             goto failed;
+
+     _LOGI (LOGD_DEVICE | LOGD_WIFI,
+            "Activation: (wifi) Stage 2 of 5 (Device Configure)
successful.  Connected to '%s'.",
+            ssid ? nm_utils_escape_ssid (g_bytes_get_data (ssid,
NULL),
+                                         g_bytes_get_size (ssid))
: "(none)");
+     nm_device_activate_schedule_stage3_ip_config_start (device);
+     return;
+
+failed:
+     cleanup_association_attempt (self, FALSE);
+     nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
+                            NM_DEVICE_STATE_REASON_SUPPLICANT_FAI
LED);
+}
+
+static gboolean
+handle_auth_or_fail (NMDeviceIwd *self,
+                     NMActRequest *req,
+                     gboolean new_secrets)
+{
+     const char *setting_name;
+     guint32 tries;
+     NMConnection *applied_connection;
+     NMSecretAgentGetSecretsFlags get_secret_flags =
NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+
+     g_return_val_if_fail (NM_IS_DEVICE_IWD (self), FALSE);
+
+     applied_connection = nm_act_request_get_applied_connection
(req);
+
+     tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT
(applied_connection), wireless_secrets_tries_quark ()));
+     if (tries > 3)
+             return FALSE;
+
+     nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+
+     nm_act_request_clear_secrets (req);
+     setting_name = nm_connection_need_secrets
(applied_connection, NULL);
+     if (!setting_name) {
+             _LOGW (LOGD_DEVICE, "Cleared secrets, but setting
didn't need any secrets.");
+             return FALSE;
+     }
+
+     if (new_secrets)
+             get_secret_flags |=
NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
+     wifi_secrets_get_secrets (self, setting_name,
get_secret_flags);
+     g_object_set_qdata (G_OBJECT (applied_connection),
wireless_secrets_tries_quark (), GUINT_TO_POINTER (++tries));
+     return TRUE;
+}
+
+/*******************************************************************
**********/
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason
*out_failure_reason)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (device);
+     NMActStageReturn ret;
+     NMWifiAP *ap = NULL;
+     NMActRequest *req;
+     NMConnection *connection;
+     NMSettingWireless *s_wireless;
+     const char *ap_path;
+
+     ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)-
act_stage1_prepare (device, out_failure_reason);
+     if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+             return ret;
+
+     req = nm_device_get_act_request (NM_DEVICE (self));
+     g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
+     connection = nm_act_request_get_applied_connection (req);
+     g_return_val_if_fail (connection,
NM_ACT_STAGE_RETURN_FAILURE);
+
+     s_wireless = nm_connection_get_setting_wireless
(connection);
+     g_return_val_if_fail (s_wireless,
NM_ACT_STAGE_RETURN_FAILURE);
+
+     ap_path = nm_active_connection_get_specific_object
(NM_ACTIVE_CONNECTION (req));
+     ap = ap_path ? get_ap_by_path (self, ap_path) : NULL;
+     if (!ap) {
+             ap = find_first_compatible_ap (self, connection,
FALSE);
+
+             /* TODO: assuming hidden networks aren't supported
do we need
+              * to consider the case of APs that are not in the
scan list
+              * yet, for which nm-device-wifi.c creates the
temporary fake
+              * AP object?
+              */
+
+             nm_active_connection_set_specific_object
(NM_ACTIVE_CONNECTION (req),
+                                                       nm_exporte
d_object_get_path (NM_EXPORTED_OBJECT (ap)));
+     }
+
+     set_current_ap (self, ap, FALSE);
+     return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *device, NMDeviceStateReason
*out_failure_reason)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (device);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+     NMActRequest *req;
+     NMWifiAP *ap;
+     NMConnection *connection;
+     const char *setting_name;
+     NMSettingWireless *s_wireless;
+     GError *error = NULL;
+     GDBusProxy *network_proxy;
+
+     req = nm_device_get_act_request (device);
+     g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
+     ap = priv->current_ap;
+     if (!ap) {
+             NM_SET_OUT (out_failure_reason,
NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+             goto out;
+     }
+
+     connection = nm_act_request_get_applied_connection (req);
+     g_assert (connection);
+
+     s_wireless = nm_connection_get_setting_wireless
(connection);
+     g_assert (s_wireless);
+
+     /* If we need secrets, get them */
+     setting_name = nm_connection_need_secrets (connection,
NULL);
+     if (setting_name) {
+             _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                    "Activation: (wifi) access point '%s' has
security, but secrets are required.",
+                    nm_connection_get_id (connection));
+
+             if (handle_auth_or_fail (self, req, FALSE))
+                     ret = NM_ACT_STAGE_RETURN_POSTPONE;
+             else {
+                     NM_SET_OUT (out_failure_reason,
NM_DEVICE_STATE_REASON_NO_SECRETS);
+                     ret = NM_ACT_STAGE_RETURN_FAILURE;
+             }
+             goto out;
+     }
+
+     /* Have secrets or no secrets required */
+     if (nm_connection_get_setting_wireless_security
(connection)) {
+             _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                    "Activation: (wifi) connection '%s' has
security, and secrets exist.  No new secrets needed.",
+                    nm_connection_get_id (connection));
+     } else {
+             _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                    "Activation: (wifi) connection '%s' requires
no security.  No secrets needed.",
+                    nm_connection_get_id (connection));
+     }
+
+     /* Locate the IWD Network object */
+     network_proxy = g_dbus_proxy_new_for_bus_sync (IWD_BUS_TYPE,
+                                                    G_DBUS_PROXY_
FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                                    G_DBUS_PROXY_
FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                                    NULL,
+                                                    IWD_SERVICE,
+                                                    nm_wifi_ap_ge
t_supplicant_path (ap),
+                                                    IWD_NETWORK_I
NTERFACE,
+                                                    NULL,
&error);
+     if (!network_proxy) {
+             return FALSE;
+
+             _LOGE (LOGD_DEVICE | LOGD_WIFI,
+                    "Activation: (wifi) could not get Network
interface proxy for %s: %s",
+                    nm_wifi_ap_get_supplicant_path (ap),
+                    error->message);
+             g_clear_error (&error);
+             NM_SET_OUT (out_failure_reason,
NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+             goto out;
+     }
+
+     if (!priv->cancellable)
+             priv->cancellable = g_cancellable_new ();
+
+     /* Call Network.Connect.  No timeout because IWD already
handles
+      * timeouts.
+      */
+     g_dbus_proxy_call (network_proxy, "Connect",
+                        g_variant_new ("()"),
+                        G_DBUS_CALL_FLAGS_NONE, -1,
+                        priv->cancellable, network_connect_cb,
self);
+
+     g_object_unref (network_proxy);
+
+     /* We'll get stage3 started when the supplicant connects */
+     ret = NM_ACT_STAGE_RETURN_POSTPONE;
+
+out:
+     if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+             cleanup_association_attempt (self, FALSE);
+
+     return ret;
+}
+
+static guint32
+get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+{
+     NMSettingWireless *setting;
+     gint64 mtu_default;
+     guint32 mtu;
+
+     nm_assert (NM_IS_DEVICE (device));
+     nm_assert (out_is_user_config);
+
+     setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting
(device, NM_TYPE_SETTING_WIRELESS));
+     if (!setting)
+             g_return_val_if_reached (0);
+
+     mtu = nm_setting_wireless_get_mtu (setting);
+     if (mtu == 0) {
+             mtu_default =
nm_device_get_configured_mtu_from_connection_default (device,
"wifi.mtu");
+             if (mtu_default >= 0) {
+                     *out_is_user_config = TRUE;
+                     return (guint32) mtu_default;
+             }
+     }
+     *out_is_user_config = (mtu != 0);
+     return mtu;
+}
+
+static void
+activation_success_handler (NMDevice *device)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (device);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     NMActRequest *req;
+     NMConnection *applied_connection;
+
+     req = nm_device_get_act_request (device);
+     g_assert (req);
+
+     applied_connection = nm_act_request_get_applied_connection
(req);
+
+     /* Clear wireless secrets tries on success */
+     g_object_set_qdata (G_OBJECT (applied_connection),
wireless_secrets_tries_quark (), NULL);
+
+     /* There should always be a current AP */
+     g_warn_if_fail (priv->current_ap);
+}
+
+static void
+activation_failure_handler (NMDevice *device)
+{
+     NMConnection *applied_connection;
+
+     applied_connection = nm_device_get_applied_connection
(device);
+     g_assert (applied_connection);
+
+     /* Clear wireless secrets tries on failure */
+     g_object_set_qdata (G_OBJECT (applied_connection),
wireless_secrets_tries_quark (), NULL);
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (device);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     if (new_state <= NM_DEVICE_STATE_UNAVAILABLE)
+             remove_all_aps (self);
+     else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE)
+             update_aps (self);
+
+     switch (new_state) {
+     case NM_DEVICE_STATE_UNMANAGED:
+             break;
+     case NM_DEVICE_STATE_UNAVAILABLE:
+             /*
+              * If the device is enabled and the IWD manager is
ready,
+              * transition to DISCONNECTED because the device is
now
+              * ready to use.
+              */
+             if (priv->enabled && priv->dbus_obj) {
+                     nm_device_queue_recheck_available (device,
+                                                        NM_DEVICE
_STATE_REASON_SUPPLICANT_AVAILABLE,
+                                                        NM_DEVICE
_STATE_REASON_SUPPLICANT_FAILED);
+             }
+             break;
+     case NM_DEVICE_STATE_NEED_AUTH:
+             send_disconnect (self);
+             break;
+     case NM_DEVICE_STATE_IP_CHECK:
+             break;
+     case NM_DEVICE_STATE_ACTIVATED:
+             activation_success_handler (device);
+             break;
+     case NM_DEVICE_STATE_FAILED:
+             activation_failure_handler (device);
+             break;
+     case NM_DEVICE_STATE_DISCONNECTED:
+             break;
+     default:
+             break;
+     }
+}
+
+static gboolean
+get_enabled (NMDevice *device)
+{
+     return NM_DEVICE_IWD_GET_PRIVATE ((NMDeviceIwd *) device)-
enabled;
+}
+
+static void
+set_enabled (NMDevice *device, gboolean enabled)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (device);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     NMDeviceState state;
+
+     enabled = !!enabled;
+
+     if (priv->enabled == enabled)
+             return;
+
+     priv->enabled = enabled;
+
+     _LOGD (LOGD_WIFI, "device now %s", enabled ? "enabled" :
"disabled");
+
+     state = nm_device_get_state (NM_DEVICE (self));
+     if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+             _LOGD (LOGD_WIFI, "(%s): device blocked by UNMANAGED
state",
+                    enabled ? "enable" : "disable");
+             return;
+     }
+
+     if (enabled) {
+             if (state != NM_DEVICE_STATE_UNAVAILABLE)
+                     _LOGW (LOGD_CORE, "not in expected
unavailable state!");
+
+             if (priv->dbus_obj)
+                     nm_device_queue_recheck_available (NM_DEVICE
(self),
+                                                        NM_DEVICE
_STATE_REASON_SUPPLICANT_AVAILABLE,
+                                                        NM_DEVICE
_STATE_REASON_SUPPLICANT_FAILED);
+     } else {
+             nm_device_state_changed (NM_DEVICE (self),
+                                      NM_DEVICE_STATE_UNAVAILABLE
,
+                                      NM_DEVICE_STATE_REASON_NONE
);
+     }
+}
+
+static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    GError **error)
+{
+     NMDeviceClass *device_class;
+
+     /* Only handle wireless setting here, delegate other
settings to parent class */
+     if (nm_streq (setting_name,
NM_SETTING_WIRELESS_SETTING_NAME)) {
+             return nm_device_hash_check_invalid_keys (diffs,
+                                                       NM_SETTING
_WIRELESS_SETTING_NAME,
+                                                       error,
+                                                       NM_SETTING
_WIRELESS_MTU); /* reapplied with IP config */
+     }
+
+     device_class = NM_DEVICE_CLASS (nm_device_iwd_parent_class);
+     return device_class->can_reapply_change (device,
+                                              setting_name,
+                                              s_old,
+                                              s_new,
+                                              diffs,
+                                              error);
+}
+
+/*******************************************************************
**********/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (object);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     gsize i;
+     char **list;
+
+     switch (prop_id) {
+     case PROP_MODE:
+             if (priv->current_ap)
+                     g_value_set_uint (value,
NM_802_11_MODE_INFRA);
+             else
+                     g_value_set_uint (value,
NM_802_11_MODE_UNKNOWN);
+             break;
+     case PROP_BITRATE:
+             g_value_set_uint (value, 65000);
+             break;
+     case PROP_CAPABILITIES:
+             g_value_set_uint (value, priv->capabilities);
+             break;
+     case PROP_ACCESS_POINTS:
+             list = (char **) ap_list_get_sorted_paths (self,
TRUE);
+             for (i = 0; list[i]; i++)
+                     list[i] = g_strdup (list[i]);
+             g_value_take_boxed (value, list);
+             break;
+     case PROP_ACTIVE_ACCESS_POINT:
+             nm_utils_g_value_set_object_path (value, priv-
current_ap);
+             break;
+     case PROP_SCANNING:
+             g_value_set_boolean (value, priv->scanning);
+             break;
+     default:
+             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
pspec);
+             break;
+     }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+     NMDeviceIwd *device = NM_DEVICE_IWD (object);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE
(device);
+
+     switch (prop_id) {
+     case PROP_CAPABILITIES:
+             /* construct-only */
+             priv->capabilities = g_value_get_uint (value);
+             break;
+     default:
+             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
pspec);
+             break;
+     }
+}
+
+/*******************************************************************
**********/
+
+static void
+state_changed (NMDeviceIwd *self, const gchar *new_state)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+     NMDevice *device = NM_DEVICE (self);
+
+     _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is
%s", new_state);
+
+     /* Don't allow scanning while connecting, disconnecting or
roaming */
+     priv->can_scan = NM_IN_STRSET (new_state, "connected",
"disconnected");
+
+     if (NM_IN_STRSET (new_state, "connecting", "connected",
"roaming")) {
+             /* If we're activating, do nothing, the confirmation
of
+              * a connection success is handled in the
Device.Connect
+              * method return callback.  Otherwise IWD must have
connected
+              * without Network Manager's will so for simplicity
force a
+              * disconnect.
+              */
+             if (   nm_device_is_activating (device)
+                 || nm_device_get_state (device) ==
NM_DEVICE_STATE_ACTIVATED)
+                     return;
+
+             _LOGW (LOGD_DEVICE | LOGD_WIFI,
+                    "Unsolicited connection success, asking IWD
to disconnect");
+             send_disconnect (self);
+
+             return;
+     } else if (NM_IN_STRSET (new_state, "disconnecting",
"disconnected")) {
+             if (   !nm_device_is_activating (device)
+                 && nm_device_get_state (device) !=
NM_DEVICE_STATE_ACTIVATED)
+                     return;
+
+             /* Call Disconnect on the IWD device object to make
sure it
+              * disables its own autoconnect.
+              *
+              * Note we could instead call
net.connman.iwd.KnownNetworks.ForgetNetwork
+              * and leave the device in autoconnect.  This way if
NetworkManager
+              * changes any settings for this connection, they'd
be taken into
+              * account on the next connection attempt.  But both
methods are
+              * a hack, we'll perhaps need an IWD API to "connect
once" without
+              * storing anything.
+              */
+             send_disconnect (self);
+
+             nm_device_state_changed (device,
+                                      NM_DEVICE_STATE_FAILED,
+                                      NM_DEVICE_STATE_REASON_SUPP
LICANT_DISCONNECT);
+
+             return;
+     }
+
+     _LOGE (LOGD_WIFI, "State %s unknown", new_state);
+}
+
+static void
+scanning_changed (NMDeviceIwd *self, gboolean new_scanning)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     if (new_scanning == priv->scanning)
+             return;
+
+     priv->scanning = new_scanning;
+
+     _notify (self, PROP_SCANNING);
+
+     if (!priv->scanning)
+             update_aps (self);
+}
+
+static void
+properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
+                    GStrv invalidate_properties, gpointer user_data)
+{
+     NMDeviceIwd *self = user_data;
+     GVariantIter *iter;
+     const gchar *key;
+     GVariant *value;
+
+     g_variant_get (changed_properties, "a{sv}", &iter);
+     while (g_variant_iter_next (iter, "{&sv}", &key, &value)) {
+             if (!strcmp (key, "State"))
+                     state_changed (self, g_variant_get_string
(value, NULL));
+
+             if (!strcmp (key, "Scanning"))
+                     scanning_changed (self,
g_variant_get_boolean (value));
+
+             g_variant_unref (value);
+     }
+
+     g_variant_iter_free (iter);
+}
+
+/*******************************************************************
**********/
+
+static void
+nm_device_iwd_init (NMDeviceIwd *self)
+{
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     priv->aps = g_hash_table_new (g_str_hash, g_str_equal);
+
+     /* Make sure the manager is running */
+     (void) nm_iwd_manager_get ();
+}
+
+NMDevice *
+nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities
capabilities)
+{
+     return g_object_new (NM_TYPE_DEVICE_IWD,
+                          NM_DEVICE_IFACE, iface,
+                          NM_DEVICE_TYPE_DESC, "802.11 WiFi",
+                          NM_DEVICE_DEVICE_TYPE,
NM_DEVICE_TYPE_WIFI,
+                          NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI,
+                          NM_DEVICE_RFKILL_TYPE,
RFKILL_TYPE_WLAN,
+                          NM_DEVICE_IWD_CAPABILITIES, (guint)
capabilities,
+                          NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (object);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     nm_clear_g_cancellable (&priv->cancellable);
+
+     wifi_secrets_cancel (self);
+
+     cleanup_association_attempt (self, TRUE);
+
+     g_clear_object (&priv->dbus_proxy);
+     g_clear_object (&priv->dbus_obj);
+
+     remove_all_aps (self);
+
+     G_OBJECT_CLASS (nm_device_iwd_parent_class)->dispose
(object);
+}
+
+static void
+finalize (GObject *object)
+{
+     NMDeviceIwd *self = NM_DEVICE_IWD (object);
+     NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+     nm_assert (g_hash_table_size (priv->aps) == 0);
+
+     g_hash_table_unref (priv->aps);
+
+     G_OBJECT_CLASS (nm_device_iwd_parent_class)->finalize
(object);
+}
+
+static void
+nm_device_iwd_class_init (NMDeviceIwdClass *klass)
+{
+     GObjectClass *object_class = G_OBJECT_CLASS (klass);
+     NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+     NM_DEVICE_CLASS_DECLARE_TYPES (klass,
NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
+
+     object_class->get_property = get_property;
+     object_class->set_property = set_property;
+     object_class->dispose = dispose;
+     object_class->finalize = finalize;
+
+     parent_class->can_auto_connect = can_auto_connect;
+     parent_class->is_available = is_available;
+     parent_class->check_connection_compatible =
check_connection_compatible;
+     parent_class->check_connection_available =
check_connection_available;
+     parent_class->complete_connection = complete_connection;
+     parent_class->get_enabled = get_enabled;
+     parent_class->set_enabled = set_enabled;
+
+     parent_class->act_stage1_prepare = act_stage1_prepare;
+     parent_class->act_stage2_config = act_stage2_config;
+     parent_class->get_configured_mtu = get_configured_mtu;
+     parent_class->deactivate = deactivate;
+     parent_class->can_reapply_change = can_reapply_change;
+
+     parent_class->state_changed = device_state_changed;
+
+     klass->scanning_prohibited = scanning_prohibited;
+
+     obj_properties[PROP_MODE] =
+         g_param_spec_uint (NM_DEVICE_IWD_MODE, "", "",
+                            NM_802_11_MODE_UNKNOWN,
+                            NM_802_11_MODE_AP,
+                            NM_802_11_MODE_INFRA,
+                            G_PARAM_READABLE |
+                            G_PARAM_STATIC_STRINGS);