I am developing an application to connect to secure WiFi networks using the Connman DBus API. I have read that I need to register an object that implements the net.connman.Agent interface so that DBus can call my methods. So far I've been able to do something very close to what I need, however when I run the program, if I've never connected to that network before, the "Connect" function times out. If I put a breakpoint on the Agent event handler, it will never get called. I have this code for agent registration:
WiFiAgent::WiFiAgent(GDBusConnection *inputConnection, Poco::JSON::Object::Ptr credentials)
: connection(inputConnection), parameters(std::move(credentials)) {
static const GDBusInterfaceVTable vtable = {
.method_call = handleMethodCall,
.get_property = nullptr,
.set_property = nullptr,
};
static GError* error = nullptr;
objectId = g_dbus_connection_register_object(
connection,
WiFiAgent::ourAgentPath,
WiFiAgent::introspectionWrapper->interfaces[0],
&vtable,
parameters.get(),
nullptr,
&error
);
if(objectId == 0 || error != nullptr)
throw GlibException("Register WiFi agent", error);
GVariant* agentPathVariant = g_variant_new("(o)", WiFiAgent::ourAgentPath);
if(agentPathVariant == nullptr)
throw std::runtime_error("Register WiFi agent: g_variant_new failed.");
GVariant* result = g_dbus_connection_call_sync(connection, "net.connman", "/", "net.connman.Manager",
"RegisterAgent", agentPathVariant, nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
if(result == nullptr || error != nullptr)
throw GlibException("Register WiFi agent", error);
}
this code for event handler:
void WiFiAgent::handleMethodCall(GDBusConnection *, const gchar *,
const gchar *, const gchar *, const gchar *method,
GVariant *methodParameters, GDBusMethodInvocation *invocation, gpointer userdata) {
std::cout << "Method got called." << std::endl;
}
and this code for the introspection:
static inline class IntrospectionWrapper final {
GDBusNodeInfo* introspection = nullptr;
static constexpr auto introspectionXML =
"<node>"
" <interface name='net.connman.Agent'>"
" <method name='RequestInput'>"
" <arg type='o' name='service' direction='in'/>"
" <arg type='a{sv}' name='fields' direction='in'/>"
" <arg type='a{sv}' name='fields' direction='out'/>"
" </method>"
" <method name='ReportError'>"
" <arg type='o' name='service' direction='in'/>"
" <arg type='s' name='error' direction='in'/>"
" </method>"
" </interface>"
"</node>";
public:
IntrospectionWrapper() {
GError* error = nullptr;
introspection = g_dbus_node_info_new_for_xml(introspectionXML, &error);
if(introspection == nullptr || error != nullptr)
std::cerr << GlibException("Agent introspection construction", error) << std::endl;
}
GDBusNodeInfo* operator->() { return introspection; };
GDBusNodeInfo* get() { return introspection; }
} introspectionWrapper;
Everything works fine and no errors occur, but the "Connect" function called on the specified service interface fails with a timeout error:
Glib error with code 24 - g-io-error-quark - Timeout was reached
I call this function this way:
void DBusManipulator::connectToTheNetwork(GDBusProxy *network) {
GError* error = nullptr;
g_dbus_proxy_call_sync(network, "Connect", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
if(error != nullptr)
throw GlibException("Connect to the network", error);
const auto state = variantGetValue(getNetworkProperty(network, "State"));
if(state != "online" && state != "ready")
throw std::runtime_error("Connect to the WiFi network: connection failed");
std::cout << "Connected to the network successfully." << std::endl;
}
Where the network get's received this way:
GDBusProxy* network = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, nullptr, "net.connman", servicePath, "net.connman.Service", nullptr, &error);
if(network == nullptr || error != nullptr)
throw GlibException("Get network by name", error);
Managed to solve the problem by changing Connect
method call to asynchronous and running main loop:
g_dbus_proxy_call(G_DBUS_PROXY(network), "Connect", g_variant_new("()"),
G_DBUS_CALL_FLAGS_NONE, -1, nullptr, finisher, data);
g_main_loop_run(mainloop);
if(data ... connectionFlag == false) /* Checking finisher result. */
throw std::runtime_error("Connection failed");
Finisher function:
static constexpr auto finisher = [](GObject*, GAsyncResult* result, gpointer userdata) {
auto* parameters = static_cast<...*>(userdata);
GDBusProxy* network = parameters->...;
GError* error = nullptr;
GVariant* tResult = g_dbus_proxy_call_finish(G_DBUS_PROXY(network), result, &error);
if (tResult != nullptr && error == nullptr)
...; /* Success, setting up the result flag. */
g_main_loop_quit(mainloop);
/* Quits the main loop and unlocks the main thread */
};
Now agent methods are called right after the Connect
method.