diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c index 050c939895..139242094c 100644 --- a/src/supplicant/nm-supplicant-interface.c +++ b/src/supplicant/nm-supplicant-interface.c @@ -37,6 +37,7 @@ #define WPAS_DBUS_IFACE_INTERFACE_P2P_DEVICE WPAS_DBUS_INTERFACE ".Interface.P2PDevice" #define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS" #define WPAS_DBUS_IFACE_PEER WPAS_DBUS_INTERFACE ".Peer" +#define WPAS_DBUS_IFACE_GROUP WPAS_DBUS_INTERFACE ".Group" #define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network" #define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface" #define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists" @@ -82,15 +83,18 @@ typedef struct _AddNetworkData { } AddNetworkData; enum { - STATE, /* change in the interface's state */ - REMOVED, /* interface was removed by the supplicant */ - BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ - BSS_REMOVED, /* supplicant removed BSS from its scan list */ - PEER_UPDATED, /* a new Peer appeared or an existing had properties changed */ - PEER_REMOVED, /* supplicant removed Peer from its scan list */ - SCAN_DONE, /* wifi scan is complete */ - CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ - WPS_CREDENTIALS, /* WPS credentials received */ + STATE, /* change in the interface's state */ + REMOVED, /* interface was removed by the supplicant */ + BSS_UPDATED, /* a new BSS appeared or an existing had properties changed */ + BSS_REMOVED, /* supplicant removed BSS from its scan list */ + PEER_UPDATED, /* a new Peer appeared or an existing had properties changed */ + PEER_REMOVED, /* supplicant removed Peer from its scan list */ + SCAN_DONE, /* wifi scan is complete */ + CREDENTIALS_REQUEST, /* 802.1x identity or password requested */ + WPS_CREDENTIALS, /* WPS credentials received */ + GROUP_STARTED, /* a new Group (interface) was created */ + GROUP_FINISHED, /* a Group (interface) has been finished */ + GROUP_FORMATION_FAILURE, /* P2P Group formation failed */ LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -98,6 +102,8 @@ static guint signals[LAST_SIGNAL] = { 0 }; NM_GOBJECT_PROPERTIES_DEFINE (NMSupplicantInterface, PROP_IFACE, PROP_OBJECT_PATH, + PROP_P2P_GROUP_JOINED, + PROP_P2P_GROUP_OWNER, PROP_SCANNING, PROP_CURRENT_BSS, PROP_DRIVER, @@ -137,10 +143,14 @@ typedef struct { GDBusProxy * iface_proxy; GCancellable * other_cancellable; GDBusProxy * p2p_proxy; + GDBusProxy * group_proxy; gboolean p2p_proxy_acquired; + gboolean group_proxy_acquired; gboolean p2p_capable; + gboolean p2p_group_owner; + WpsData *wps_data; AssocData * assoc_data; @@ -705,6 +715,18 @@ iface_check_netreply_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ iface_check_ready (self); } +gboolean +nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->group_proxy_acquired; +} + +gboolean +nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self) +{ + return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->p2p_group_owner; +} + NMSupplicantFeature nm_supplicant_interface_get_ap_support (NMSupplicantInterface *self) { @@ -1291,6 +1313,83 @@ props_changed_cb (GDBusProxy *proxy, g_object_thaw_notify (G_OBJECT (self)); } +static void +group_props_changed_cb (GDBusProxy *proxy, + GVariant *changed_properties, + char **invalidated_properties, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + char *s; + + g_object_freeze_notify (G_OBJECT (self)); + +#if 0 + v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + if ( len == ETH_ALEN + && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) + nm_wifi_p2p_group_set_bssid_bin (group, bytes); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + len = MIN (32, len); + + /* Stupid ieee80211 layer uses */ + if ( bytes && len + && !(((len == 8) || (len == 9)) && !memcmp (bytes, "", 8)) + && !nm_utils_is_empty_ssid (bytes, len)) + nm_wifi_p2p_group_set_ssid (group, bytes, len); + + g_variant_unref (v); + } +#endif + + if (g_variant_lookup (changed_properties, "Role", "s", &s)) { + priv->p2p_group_owner = g_strcmp0 (s, "GO") == 0; + _notify (self, PROP_P2P_GROUP_OWNER); + g_free (s); + } + + /* TODO: Members! */ + + g_object_thaw_notify (G_OBJECT (self)); +} + +static void +group_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) +{ + NMSupplicantInterface *self; + NMSupplicantInterfacePrivate *priv; + gs_free_error GError *error = NULL; + gboolean success; + + success = g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error); + if ( !success + && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = NM_SUPPLICANT_INTERFACE (user_data); + priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + + if (!success) { + _LOGD ("failed to acquire Group proxy: (%s)", error->message); + g_clear_object (&priv->group_proxy); + return; + } + + priv->group_proxy_acquired = TRUE; + _notify (self, PROP_P2P_GROUP_JOINED); + + iface_check_ready (self); +} + static void p2p_props_changed_cb (GDBusProxy *proxy, GVariant *changed_properties, @@ -1298,7 +1397,9 @@ p2p_props_changed_cb (GDBusProxy *proxy, gpointer user_data) { NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); const char **array, **iter; + const char *path = NULL; g_object_freeze_notify (G_OBJECT (self)); @@ -1309,6 +1410,41 @@ p2p_props_changed_cb (GDBusProxy *proxy, g_free (array); } + if (g_variant_lookup (changed_properties, "Group", "&o", &path)) { + if (priv->group_proxy && g_strcmp0 (path, g_dbus_proxy_get_object_path (priv->group_proxy)) == 0) { + /* We already have the proxy, nothing to do. */ + } else if (path && g_strcmp0 (path, "/") != 0) { + if (priv->group_proxy != NULL) { + _LOGW ("P2P: Unexpected udpate of the group object path"); + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + g_clear_object (&priv->group_proxy); + } + + /* Delay ready state if we have not reached it yet. */ + if (priv->ready_count) + priv->ready_count++; + + priv->group_proxy = g_object_new (G_TYPE_DBUS_PROXY, + "g-bus-type", G_BUS_TYPE_SYSTEM, + "g-flags", G_DBUS_PROXY_FLAGS_NONE, + "g-name", WPAS_DBUS_SERVICE, + "g-object-path", path, + "g-interface-name", WPAS_DBUS_IFACE_GROUP, + NULL); + g_signal_connect (priv->group_proxy, "g-properties-changed", G_CALLBACK (group_props_changed_cb), self); + g_async_initable_init_async (G_ASYNC_INITABLE (priv->group_proxy), + G_PRIORITY_DEFAULT, + priv->other_cancellable, + (GAsyncReadyCallback) group_proxy_acquired_cb, + self); + } else { + priv->group_proxy_acquired = FALSE; + _notify (self, PROP_P2P_GROUP_JOINED); + g_clear_object (&priv->group_proxy); + } + } + g_object_thaw_notify (G_OBJECT (self)); } @@ -1339,6 +1475,79 @@ p2p_device_lost (GDBusProxy *proxy, peer_data_destroy (peer_data); } +static void +p2p_group_started (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + NMSupplicantInterface *iface = NULL; + char *group_path = NULL; + char *iface_path = NULL; + + /* There is one more parameter: the role, but we don't really care about that here. */ + if (!g_variant_lookup (params, "group_object", "&o", &group_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"group_object\" parameter"); + return; + } + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupStarted signal is missing the \"interface\" parameter"); + return; + } + + if (g_strcmp0 (iface_path, priv->object_path) == 0) { + _LOGW ("P2P: GroupStarted on existing interface"); + iface = g_object_ref (self); + } else { + iface = nm_supplicant_manager_create_interface_from_path (nm_supplicant_manager_get (), + iface_path); + if (iface == NULL) { + _LOGW ("P2P: Group interface already exists in GroupStarted handler, aborting further processing."); + return; + } + } + + /* Signal existance of the (new) interface. */ + g_signal_emit (self, signals[GROUP_STARTED], 0, iface); + g_object_unref (iface); +} + +static void +p2p_group_formation_failure (GDBusProxy *proxy, + const char *group, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + + g_signal_emit (self, signals[GROUP_FORMATION_FAILURE], 0, group); +} + +static void +p2p_group_finished (GDBusProxy *proxy, + GVariant *params, + gpointer user_data) +{ + NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data); + NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self); + const char *iface_path = NULL; + /* TODO: Group finished is called on the management interface! + * This means the signal consumer will currently need to assume which + * interface is finishing or it needs to match the object paths. + */ + + if (!g_variant_lookup (params, "interface_object", "&o", &iface_path)) { + _LOGW ("P2P: GroupFinished signal is missing the \"interface\" parameter"); + return; + } + + _LOGD ("P2P: GroupFinished signal on interface %s for interface %s", priv->object_path, iface_path); + + /* Signal group finish interface (on management interface). */ + g_signal_emit (self, signals[GROUP_FINISHED], 0, iface_path); +} + static void on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data) { @@ -1449,10 +1658,13 @@ on_p2p_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_da G_CALLBACK (p2p_device_found), self); _nm_dbus_signal_connect (priv->p2p_proxy, "DeviceLost", G_VARIANT_TYPE ("(o)"), G_CALLBACK (p2p_device_lost), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupStarted", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_started), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFormationFailure", G_VARIANT_TYPE ("(s)"), + G_CALLBACK (p2p_group_formation_failure), self); + _nm_dbus_signal_connect (priv->p2p_proxy, "GroupFinished", G_VARIANT_TYPE ("(a{sv})"), + G_CALLBACK (p2p_group_finished), self); /* TODO: - * * GroupStarted - * * GroupFinished - * * GroupFormationFailure * * WpsFailed * * FindStopped * * GONegotationFailure @@ -2220,6 +2432,12 @@ get_property (GObject *object, case PROP_CURRENT_BSS: g_value_set_string (value, priv->current_bss); break; + case PROP_P2P_GROUP_JOINED: + g_value_set_boolean (value, priv->p2p_capable && priv->group_proxy_acquired); + break; + case PROP_P2P_GROUP_OWNER: + g_value_set_boolean (value, priv->p2p_group_owner); + break; case PROP_P2P_AVAILABLE: g_value_set_boolean (value, priv->p2p_capable && priv->p2p_proxy_acquired); break; @@ -2347,6 +2565,9 @@ dispose (GObject *object) if (priv->p2p_proxy) g_signal_handlers_disconnect_by_data (priv->p2p_proxy, object); g_clear_object (&priv->p2p_proxy); + if (priv->group_proxy) + g_signal_handlers_disconnect_by_data (priv->group_proxy, object); + g_clear_object (&priv->group_proxy); nm_clear_g_cancellable (&priv->init_cancellable); nm_clear_g_cancellable (&priv->other_cancellable); @@ -2396,6 +2617,16 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_JOINED] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_P2P_GROUP_OWNER] = + g_param_spec_boolean (NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); obj_properties[PROP_DRIVER] = g_param_spec_uint (NM_SUPPLICANT_INTERFACE_DRIVER, "", "", 0, G_MAXUINT, NM_SUPPLICANT_DRIVER_WIRELESS, @@ -2529,4 +2760,28 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_VARIANT); + + signals[GROUP_STARTED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, NM_TYPE_SUPPLICANT_INTERFACE); + + signals[GROUP_FINISHED] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FINISHED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[GROUP_FORMATION_FAILURE] = + g_signal_new (NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_VARIANT); } diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h index aed72af8bf..6af9798ea7 100644 --- a/src/supplicant/nm-supplicant-interface.h +++ b/src/supplicant/nm-supplicant-interface.h @@ -58,6 +58,8 @@ typedef enum { #define NM_SUPPLICANT_INTERFACE_OBJECT_PATH "object-path" #define NM_SUPPLICANT_INTERFACE_SCANNING "scanning" #define NM_SUPPLICANT_INTERFACE_CURRENT_BSS "current-bss" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED "p2p-group-joined" +#define NM_SUPPLICANT_INTERFACE_P2P_GROUP_OWNER "p2p-group-owner" #define NM_SUPPLICANT_INTERFACE_DRIVER "driver" #define NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE "p2p-available" #define NM_SUPPLICANT_INTERFACE_FAST_SUPPORT "fast-support" @@ -77,6 +79,9 @@ typedef enum { #define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done" #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request" #define NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS "wps-credentials" +#define NM_SUPPLICANT_INTERFACE_GROUP_STARTED "group-started" +#define NM_SUPPLICANT_INTERFACE_GROUP_FINISHED "group-finished" +#define NM_SUPPLICANT_INTERFACE_GROUP_FORMATION_FAILURE "group-formation-failure" typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass; @@ -129,6 +134,10 @@ guint nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self); gboolean nm_supplicant_interface_get_has_credentials_request (NMSupplicantInterface *self); +gboolean nm_supplicant_interface_get_p2p_group_joined (NMSupplicantInterface *self); + +gboolean nm_supplicant_interface_get_p2p_group_owner (NMSupplicantInterface *self); + gboolean nm_supplicant_interface_credentials_reply (NMSupplicantInterface *self, const char *field, const char *value,