mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-07 18:20:22 +01:00
wifi: follow supplicant's scan list instead of managing AP lifetime internally
Instead of tricky logic to merge APs and age them, just tell the supplicant what our aging parameters are, and rely on it to handle removal from the list. Notable behavioral changes are: * APs will now be removed when they haven't been seen for two consecutive scans in which they would have been included. This means that when the scan interval is short, out-of-range APs will be removed much more quickly than the previous 360 seconds. * APs now live at most 250 seconds (twice our longest scan interval) instead of the previous 360 seconds. * The problem with wpa_supplicant < 2.3 not notifying that a BSS has been seen in the scan if none of its properties actually changed is now avoided, because an AP is only removed when the supplicant removes it In general these changes should make the scan list more responsive, at the cost of slightly more instability in the list due to the unreliability of WiFi scanning. But it also removes a layer of complexity and abstraction from NetworkManager, pushing the scan results list closer to that which the hardware reports.
This commit is contained in:
parent
be370859ef
commit
910c62d8c7
4 changed files with 99 additions and 249 deletions
|
|
@ -131,7 +131,7 @@ struct _NMDeviceWifiPrivate {
|
|||
gint32 scheduled_scan_time;
|
||||
guint8 scan_interval; /* seconds */
|
||||
guint pending_scan_id;
|
||||
guint scanlist_cull_id;
|
||||
guint ap_dump_id;
|
||||
gboolean requested_scan;
|
||||
|
||||
NMSupplicantManager *sup_mgr;
|
||||
|
|
@ -189,8 +189,6 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
|
|||
GParamSpec *pspec,
|
||||
NMDeviceWifi *self);
|
||||
|
||||
static void schedule_scanlist_cull (NMDeviceWifi *self);
|
||||
|
||||
static gboolean request_wireless_scan (gpointer user_data);
|
||||
|
||||
static void emit_ap_added_removed (NMDeviceWifi *self,
|
||||
|
|
@ -305,10 +303,7 @@ supplicant_interface_release (NMDeviceWifi *self)
|
|||
_LOGD (LOGD_WIFI_SCAN, "reset scanning interval to %d seconds",
|
||||
priv->scan_interval);
|
||||
|
||||
if (priv->scanlist_cull_id) {
|
||||
g_source_remove (priv->scanlist_cull_id);
|
||||
priv->scanlist_cull_id = 0;
|
||||
}
|
||||
nm_clear_g_source (&priv->ap_dump_id);
|
||||
|
||||
if (priv->sup_iface) {
|
||||
remove_supplicant_interface_error_handler (self);
|
||||
|
|
@ -1420,11 +1415,6 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
|
|||
priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
|
||||
schedule_scan (self, success);
|
||||
|
||||
/* Ensure that old APs get removed, which otherwise only
|
||||
* happens when there are new BSSes.
|
||||
*/
|
||||
schedule_scanlist_cull (self);
|
||||
|
||||
if (priv->requested_scan) {
|
||||
priv->requested_scan = FALSE;
|
||||
nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE);
|
||||
|
|
@ -1436,15 +1426,14 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
|
|||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
ap_list_dump (NMDeviceWifi *self)
|
||||
static gboolean
|
||||
ap_list_dump (gpointer user_data)
|
||||
{
|
||||
NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
|
||||
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
GSList *sorted, *iter;
|
||||
|
||||
if (!nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN))
|
||||
return;
|
||||
|
||||
priv->ap_dump_id = 0;
|
||||
_LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%u next:%u]",
|
||||
nm_utils_get_monotonic_timestamp_s (),
|
||||
priv->last_scan,
|
||||
|
|
@ -1453,9 +1442,19 @@ ap_list_dump (NMDeviceWifi *self)
|
|||
for (iter = sorted; iter; iter = iter->next)
|
||||
nm_ap_dump (NM_AP (iter->data), " ", nm_device_get_iface (NM_DEVICE (self)));
|
||||
g_slist_free (sorted);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
#define WPAS_REMOVED_TAG "supplicant-removed"
|
||||
static void
|
||||
schedule_ap_list_dump (NMDeviceWifi *self)
|
||||
{
|
||||
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
|
||||
if (!nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN))
|
||||
return;
|
||||
nm_clear_g_source (&priv->ap_dump_id);
|
||||
priv->ap_dump_id = g_timeout_add_seconds (1, ap_list_dump, self);
|
||||
}
|
||||
|
||||
static void
|
||||
try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
|
||||
|
|
@ -1489,137 +1488,6 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
merge_scanned_ap (NMDeviceWifi *self,
|
||||
NMAccessPoint *merge_ap,
|
||||
const char *supplicant_path,
|
||||
GVariant *properties)
|
||||
{
|
||||
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
NMAccessPoint *found_ap = NULL;
|
||||
const GByteArray *ssid;
|
||||
const char *bssid;
|
||||
|
||||
/* Let the manager try to fill in the SSID from seen-bssids lists */
|
||||
bssid = nm_ap_get_address (merge_ap);
|
||||
ssid = nm_ap_get_ssid (merge_ap);
|
||||
if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
|
||||
/* Try to fill the SSID from the AP database */
|
||||
try_fill_ssid_for_hidden_ap (merge_ap);
|
||||
|
||||
ssid = nm_ap_get_ssid (merge_ap);
|
||||
if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
|
||||
/* Yay, matched it, no longer treat as hidden */
|
||||
_LOGD (LOGD_WIFI_SCAN, "matched hidden AP %s => '%s'",
|
||||
str_if_set (bssid, "(none)"), nm_utils_escape_ssid (ssid->data, ssid->len));
|
||||
} else {
|
||||
/* Didn't have an entry for this AP in the database */
|
||||
_LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s",
|
||||
str_if_set (bssid, "(none)"));
|
||||
}
|
||||
}
|
||||
|
||||
found_ap = get_ap_by_supplicant_path (self, supplicant_path);
|
||||
if (!found_ap)
|
||||
found_ap = nm_ap_match_in_hash (merge_ap, priv->aps);
|
||||
if (found_ap) {
|
||||
_LOGD (LOGD_WIFI_SCAN, "merging AP '%s' %s (%p) with existing (%p)",
|
||||
ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
|
||||
str_if_set (bssid, "(none)"),
|
||||
merge_ap,
|
||||
found_ap);
|
||||
|
||||
nm_ap_update_from_properties (found_ap, supplicant_path, properties);
|
||||
nm_ap_set_fake (found_ap, FALSE);
|
||||
g_object_set_data (G_OBJECT (found_ap), WPAS_REMOVED_TAG, NULL);
|
||||
} else {
|
||||
/* New entry in the list */
|
||||
_LOGD (LOGD_WIFI_SCAN, "adding new AP '%s' %s (%p)",
|
||||
ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
|
||||
str_if_set (bssid, "(none)"), merge_ap);
|
||||
|
||||
g_object_ref (merge_ap);
|
||||
nm_ap_export_to_dbus (merge_ap);
|
||||
g_hash_table_insert (priv->aps, (gpointer) nm_ap_get_dbus_path (merge_ap), merge_ap);
|
||||
emit_ap_added_removed (self, ACCESS_POINT_ADDED, merge_ap, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
cull_scan_list (NMDeviceWifi *self)
|
||||
{
|
||||
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
gint32 now = nm_utils_get_monotonic_timestamp_s ();
|
||||
guint32 removed = 0, total = 0;
|
||||
GHashTableIter iter;
|
||||
NMAccessPoint *ap;
|
||||
|
||||
priv->scanlist_cull_id = 0;
|
||||
|
||||
_LOGD (LOGD_WIFI_SCAN, "checking scan list for outdated APs");
|
||||
|
||||
/* Walk the access point list and remove any access points older than
|
||||
* three times the inactive scan interval.
|
||||
*/
|
||||
g_hash_table_iter_init (&iter, priv->aps);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
|
||||
const guint prune_interval_s = SCAN_INTERVAL_MAX * 3;
|
||||
gint32 last_seen;
|
||||
|
||||
/* Don't cull the associated AP or manually created APs */
|
||||
if (ap == priv->current_ap)
|
||||
continue;
|
||||
g_assert (!nm_ap_get_fake (ap)); /* only the current_ap can be fake */
|
||||
|
||||
/* Don't cull APs still known to the supplicant. Since the supplicant
|
||||
* doesn't yet emit property updates for "last seen" we have to rely
|
||||
* on changing signal strength for updating "last seen". But if the
|
||||
* AP's strength doesn't change we won't get any updates for the AP,
|
||||
* and we'll end up here even if the AP was still found by the
|
||||
* supplicant in the last scan.
|
||||
*/
|
||||
if ( nm_ap_get_supplicant_path (ap)
|
||||
&& g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL)
|
||||
continue;
|
||||
|
||||
last_seen = nm_ap_get_last_seen (ap);
|
||||
if (!last_seen || last_seen + prune_interval_s < now) {
|
||||
const GByteArray *ssid = nm_ap_get_ssid (ap);
|
||||
|
||||
_LOGD (LOGD_WIFI_SCAN,
|
||||
" removing %s (%s%s%s)",
|
||||
str_if_set (nm_ap_get_address (ap), "(none)"),
|
||||
ssid ? "'" : "",
|
||||
ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
|
||||
ssid ? "'" : "");
|
||||
emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE);
|
||||
g_hash_table_iter_remove (&iter);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
|
||||
_LOGD (LOGD_WIFI_SCAN, "removed %d APs (of %d)",
|
||||
removed, total);
|
||||
|
||||
ap_list_dump (self);
|
||||
|
||||
if(removed > 0)
|
||||
nm_device_recheck_available_connections (NM_DEVICE (self));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
schedule_scanlist_cull (NMDeviceWifi *self)
|
||||
{
|
||||
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
|
||||
/* Cull the scan list after the last request for it has come in */
|
||||
if (priv->scanlist_cull_id)
|
||||
g_source_remove (priv->scanlist_cull_id);
|
||||
priv->scanlist_cull_id = g_timeout_add_seconds (4, (GSourceFunc) cull_scan_list, self);
|
||||
}
|
||||
|
||||
static void
|
||||
supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
|
||||
const char *object_path,
|
||||
|
|
@ -1629,6 +1497,9 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
|
|||
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
NMDeviceState state;
|
||||
NMAccessPoint *ap;
|
||||
NMAccessPoint *found_ap = NULL;
|
||||
const GByteArray *ssid;
|
||||
const char *bssid;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (properties != NULL);
|
||||
|
|
@ -1642,23 +1513,52 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
|
|||
return;
|
||||
|
||||
ap = nm_ap_new_from_properties (object_path, properties);
|
||||
if (ap) {
|
||||
nm_ap_dump (ap, "New AP: ", nm_device_get_iface (NM_DEVICE (self)));
|
||||
|
||||
/* Add the AP to the device's AP list */
|
||||
merge_scanned_ap (self, ap, object_path, properties);
|
||||
g_object_unref (ap);
|
||||
|
||||
/* Update the current AP if the supplicant notified a current BSS change
|
||||
* before it sent the current BSS's scan result.
|
||||
*/
|
||||
if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0)
|
||||
supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self);
|
||||
} else
|
||||
if (!ap) {
|
||||
_LOGW (LOGD_WIFI_SCAN, "invalid AP properties received");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove outdated access points */
|
||||
schedule_scanlist_cull (self);
|
||||
/* Let the manager try to fill in the SSID from seen-bssids lists */
|
||||
bssid = nm_ap_get_address (ap);
|
||||
ssid = nm_ap_get_ssid (ap);
|
||||
if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
|
||||
/* Try to fill the SSID from the AP database */
|
||||
try_fill_ssid_for_hidden_ap (ap);
|
||||
|
||||
ssid = nm_ap_get_ssid (ap);
|
||||
if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
|
||||
/* Yay, matched it, no longer treat as hidden */
|
||||
_LOGD (LOGD_WIFI_SCAN, "matched hidden AP %s => '%s'",
|
||||
str_if_set (bssid, "(none)"), nm_utils_escape_ssid (ssid->data, ssid->len));
|
||||
} else {
|
||||
/* Didn't have an entry for this AP in the database */
|
||||
_LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s",
|
||||
str_if_set (bssid, "(none)"));
|
||||
}
|
||||
}
|
||||
|
||||
found_ap = get_ap_by_supplicant_path (self, object_path);
|
||||
if (found_ap) {
|
||||
nm_ap_dump (ap, "updated ", nm_device_get_iface (NM_DEVICE (self)));
|
||||
nm_ap_update_from_properties (found_ap, object_path, properties);
|
||||
} else {
|
||||
nm_ap_dump (ap, "added ", nm_device_get_iface (NM_DEVICE (self)));
|
||||
nm_ap_export_to_dbus (ap);
|
||||
g_hash_table_insert (priv->aps,
|
||||
(gpointer) nm_ap_get_dbus_path (ap),
|
||||
g_object_ref (ap));
|
||||
emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE);
|
||||
}
|
||||
|
||||
g_object_unref (ap);
|
||||
|
||||
/* Update the current AP if the supplicant notified a current BSS change
|
||||
* before it sent the current BSS's scan result.
|
||||
*/
|
||||
if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0)
|
||||
supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self);
|
||||
|
||||
schedule_ap_list_dump (self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1679,13 +1579,12 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
|
|||
if (state <= NM_DEVICE_STATE_UNAVAILABLE)
|
||||
return;
|
||||
|
||||
/* Update the AP's last-seen property */
|
||||
ap = get_ap_by_supplicant_path (self, object_path);
|
||||
if (ap)
|
||||
if (ap) {
|
||||
nm_ap_dump (ap, "updated ", nm_device_get_iface (NM_DEVICE (self)));
|
||||
nm_ap_update_from_properties (ap, object_path, properties);
|
||||
|
||||
/* Remove outdated access points */
|
||||
schedule_scanlist_cull (self);
|
||||
schedule_ap_list_dump (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1693,27 +1592,19 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
|
|||
const char *object_path,
|
||||
NMDeviceWifi *self)
|
||||
{
|
||||
NMDeviceWifiPrivate *priv;
|
||||
NMAccessPoint *ap;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (object_path != NULL);
|
||||
|
||||
priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||
ap = get_ap_by_supplicant_path (self, object_path);
|
||||
if (ap) {
|
||||
gint32 now = nm_utils_get_monotonic_timestamp_s ();
|
||||
gint32 last_seen = nm_ap_get_last_seen (ap);
|
||||
|
||||
/* We don't know when the supplicant last saw the AP's beacons,
|
||||
* it could be two minutes or it could be 2 seconds. Because the
|
||||
* supplicant doesn't send property change notifications if the
|
||||
* AP's other properties don't change, our last-seen time may be
|
||||
* much older the supplicant's, and the AP would be immediately
|
||||
* removed from the list on the next cleanup. So update the
|
||||
* last-seen time to ensure the AP sticks around for at least
|
||||
* one more periodic scan.
|
||||
*/
|
||||
nm_ap_set_last_seen (ap, MAX (last_seen, now - SCAN_INTERVAL_MAX));
|
||||
g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE));
|
||||
if (ap && ap != priv->current_ap) {
|
||||
nm_ap_dump (ap, "removed ", nm_device_get_iface (NM_DEVICE (self)));
|
||||
emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, TRUE);
|
||||
g_hash_table_remove (priv->aps, nm_ap_get_dbus_path (ap));
|
||||
schedule_ap_list_dump (self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1219,66 +1219,3 @@ nm_ap_complete_connection (NMAccessPoint *self,
|
|||
error);
|
||||
}
|
||||
|
||||
NMAccessPoint *
|
||||
nm_ap_match_in_hash (NMAccessPoint *find_ap, GHashTable *hash)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
NMAccessPoint *list_ap, *band_match = NULL;
|
||||
|
||||
g_return_val_if_fail (find_ap != NULL, NULL);
|
||||
|
||||
g_hash_table_iter_init (&iter, hash);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &list_ap)) {
|
||||
const GByteArray * list_ssid = nm_ap_get_ssid (list_ap);
|
||||
const char * list_addr = nm_ap_get_address (list_ap);
|
||||
const guint32 list_freq = nm_ap_get_freq (list_ap);
|
||||
|
||||
const GByteArray * find_ssid = nm_ap_get_ssid (find_ap);
|
||||
const char * find_addr = nm_ap_get_address (find_ap);
|
||||
const guint32 find_freq = nm_ap_get_freq (find_ap);
|
||||
|
||||
/* SSID match; if both APs are hiding their SSIDs,
|
||||
* let matching continue on BSSID and other properties
|
||||
*/
|
||||
if ( (!list_ssid && find_ssid)
|
||||
|| (list_ssid && !find_ssid))
|
||||
continue;
|
||||
if ( list_ssid
|
||||
&& find_ssid
|
||||
&& !nm_utils_same_ssid (list_ssid->data, list_ssid->len,
|
||||
find_ssid->data, find_ssid->len,
|
||||
TRUE))
|
||||
continue;
|
||||
|
||||
/* BSSID match */
|
||||
if ( nm_ethernet_address_is_valid (list_addr, -1)
|
||||
&& !nm_utils_hwaddr_matches (list_addr, -1, find_addr, -1))
|
||||
continue;
|
||||
|
||||
/* mode match */
|
||||
if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap))
|
||||
continue;
|
||||
|
||||
/* AP flags */
|
||||
if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap))
|
||||
continue;
|
||||
|
||||
if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap))
|
||||
continue;
|
||||
|
||||
if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap))
|
||||
continue;
|
||||
|
||||
if (list_freq != find_freq) {
|
||||
/* Must be last check to ensure all other properties match */
|
||||
if (freq_to_band (list_freq) == freq_to_band (find_freq))
|
||||
band_match = list_ap;
|
||||
continue;
|
||||
}
|
||||
|
||||
return list_ap;
|
||||
}
|
||||
|
||||
return band_match;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,8 +116,6 @@ gboolean nm_ap_complete_connection (NMAccessPoint *self,
|
|||
gboolean lock_bssid,
|
||||
GError **error);
|
||||
|
||||
NMAccessPoint * nm_ap_match_in_hash (NMAccessPoint *find_ap, GHashTable *hash);
|
||||
|
||||
void nm_ap_dump (NMAccessPoint *self,
|
||||
const char *prefix,
|
||||
const char *ifname);
|
||||
|
|
|
|||
|
|
@ -645,6 +645,30 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_
|
|||
_nm_dbus_signal_connect (priv->iface_proxy, "NetworkRequest", G_VARIANT_TYPE ("(oss)"),
|
||||
G_CALLBACK (wpas_iface_network_request), self);
|
||||
|
||||
/* Scan result aging parameters */
|
||||
g_dbus_proxy_call (priv->iface_proxy,
|
||||
"org.freedesktop.DBus.Properties.Set",
|
||||
g_variant_new ("(ssv)",
|
||||
WPAS_DBUS_IFACE_INTERFACE,
|
||||
"BSSExpireAge",
|
||||
g_variant_new_uint32 (250)),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
priv->init_cancellable,
|
||||
NULL,
|
||||
NULL);
|
||||
g_dbus_proxy_call (priv->iface_proxy,
|
||||
"org.freedesktop.DBus.Properties.Set",
|
||||
g_variant_new ("(ssv)",
|
||||
WPAS_DBUS_IFACE_INTERFACE,
|
||||
"BSSExpireCount",
|
||||
g_variant_new_uint32 (2)),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
priv->init_cancellable,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
/* Check whether NetworkReply and AP mode are supported */
|
||||
priv->ready_count = 1;
|
||||
g_dbus_proxy_call (priv->iface_proxy,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue