summaryrefslogtreecommitdiffstats
path: root/src/imports/wifi/qwifimanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports/wifi/qwifimanager.cpp')
-rw-r--r--src/imports/wifi/qwifimanager.cpp356
1 files changed, 205 insertions, 151 deletions
diff --git a/src/imports/wifi/qwifimanager.cpp b/src/imports/wifi/qwifimanager.cpp
index 1ba3d64..7d6683b 100644
--- a/src/imports/wifi/qwifimanager.cpp
+++ b/src/imports/wifi/qwifimanager.cpp
@@ -1,56 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://qt.digia.com/
+**
+** This file is part of Qt Enterprise Embedded.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** the contact form at http://qt.digia.com/
+**
+****************************************************************************/
#include "qwifimanager.h"
#include <QtCore>
#include <hardware_legacy/wifi.h>
-#include <cutils/properties.h>
+#include <cutils/sockets.h>
#include <unistd.h>
-#define WLAN_INTERFACE "wlan0"
+static const char SUPPLICANT_SVC[] = "init.svc.wpa_supplicant";
+static const char WIFI_INTERFACE[] = "wifi.interface";
+static const char QT_WIFI_BACKEND[] = "qt.wifi";
static bool QT_WIFI_DEBUG = !qgetenv("QT_WIFI_DEBUG").isEmpty();
const QEvent::Type WIFI_SCAN_RESULTS = (QEvent::Type) (QEvent::User + 2001);
const QEvent::Type WIFI_CONNECTED = (QEvent::Type) (QEvent::User + 2002);
-static int q_wifi_start_supplicant()
+/*
+ * This function is borrowed from /system/core/libnetutils/dhcp_utils.c
+ *
+ * Wait for a system property to be assigned a specified value.
+ * If desired_value is NULL, then just wait for the property to
+ * be created with any value. maxwait is the maximum amount of
+ * time in seconds to wait before giving up.
+ */
+static const int NAP_TIME = 200; // wait for 200ms at a time when polling for property values
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
{
-#if Q_ANDROID_VERSION_MAJOR > 4 || (Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR >= 1)
- return wifi_start_supplicant(0);
-#else
- return wifi_start_supplicant();
-#endif
-}
-
-static int q_wifi_connect_to_supplicant()
-{
-#if Q_ANDROID_VERSION_MAJOR > 4 || (Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR >= 1)
- return wifi_connect_to_supplicant(WLAN_INTERFACE);
-#else
- return wifi_connect_to_supplicant();
-#endif
-}
-
-static int q_wifi_command(const char *command, char *reply, size_t *reply_len)
-{
-#if Q_ANDROID_VERSION_MAJOR > 4 || (Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR >= 1)
- return wifi_command(WLAN_INTERFACE, command, reply, reply_len);
-#else
- return wifi_command(command, reply, reply_len);
-#endif
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ int maxnaps = (maxwait * 1000) / NAP_TIME;
-}
+ if (maxnaps < 1) {
+ maxnaps = 1;
+ }
-static int q_wifi_wait_for_event(char *buf, size_t len)
-{
-#if Q_ANDROID_VERSION_MAJOR > 4 || (Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR >= 1)
- return wifi_wait_for_event(WLAN_INTERFACE, buf, len);
-#else
- return wifi_wait_for_event(buf, len);
-#endif
+ while (maxnaps-- > 0) {
+ usleep(NAP_TIME * 1000);
+ if (property_get(name, value, NULL)) {
+ if (desired_value == NULL ||
+ strcmp(value, desired_value) == 0) {
+ return 0;
+ }
+ }
+ }
+ return -1; /* failure */
}
-
class QWifiManagerEvent : public QEvent
{
public:
@@ -66,13 +78,12 @@ private:
QByteArray m_data;
};
-
-
class QWifiManagerEventThread : public QThread
{
public:
- QWifiManagerEventThread(QWifiManager *manager)
+ QWifiManagerEventThread(QWifiManager *manager, const QByteArray &interface)
: m_manager(manager)
+ , m_if(interface)
{
}
@@ -81,7 +92,7 @@ public:
if (QT_WIFI_DEBUG) qDebug("EventReceiver thread is running");
char buffer[2048];
while (1) {
- int size = q_wifi_wait_for_event(buffer, sizeof(buffer) - 1);
+ int size = wifi_wait_for_event(m_if.constData(), buffer, sizeof(buffer) - 1);
if (size > 0) {
buffer[size] = 0;
@@ -89,39 +100,166 @@ public:
char *event = &buffer[11];
if (strstr(event, "SCAN-RESULTS")) {
+ if (m_manager->exitingEventThread())
+ return;
QWifiManagerEvent *e = new QWifiManagerEvent(WIFI_SCAN_RESULTS);
QCoreApplication::postEvent(m_manager, e);
} else if (strstr(event, "CONNECTED")) {
QWifiManagerEvent *e = new QWifiManagerEvent(WIFI_CONNECTED);
QCoreApplication::postEvent(m_manager, e);
+ } else if (strstr(event, "TERMINATING")) {
+ // stop monitoring for events when supplicant is terminating
+ return;
}
}
}
}
QWifiManager *m_manager;
+ QByteArray m_if;
};
-
-
QWifiManager::QWifiManager()
: m_networks(this)
, m_eventThread(0)
, m_scanTimer(0)
- , m_internalState(IS_Uninitialized)
, m_scanning(false)
+ , m_daemonClientSocket(0)
+ , m_exitingEventThread(false)
{
+ char interface[PROPERTY_VALUE_MAX];
+ property_get(WIFI_INTERFACE, interface, NULL);
+ m_interface = interface;
+ if (QT_WIFI_DEBUG) qDebug("QWifiManager: using wifi interface: %s", m_interface.constData());
+ m_eventThread = new QWifiManagerEventThread(this, m_interface);
+
+ m_daemonClientSocket = new QLocalSocket;
+ int qconnFd = socket_local_client("qconnectivity", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (qconnFd != -1) {
+ m_daemonClientSocket->setSocketDescriptor(qconnFd);
+ QObject::connect(m_daemonClientSocket, SIGNAL(readyRead()), this, SLOT(handleDhcpReply()));
+ QObject::connect(m_daemonClientSocket, SIGNAL(connected()), this, SLOT(connectedToDaemon()));
+ } else {
+ qWarning() << "QWifiManager: failed to connect to qconnectivity socket";
+ }
+ // check if backend has already been initialized
+ char backend_status[PROPERTY_VALUE_MAX];
+ if (property_get(QT_WIFI_BACKEND, backend_status, NULL)) {
+ if (strcmp(backend_status, "running") == 0) {
+ // let it re-connect, in most cases this will see that everything is working properly
+ // and will do nothing. Special case is when process has crashed or was killed by a system
+ // signal in previous execution, which results in broken connection to a supplicant,
+ // connectToBackend will fix it..
+ connectToBackend();
+ } else if (strcmp(backend_status, "stopped") == 0) {
+ // same here, cleans up the state
+ disconnectFromBackend();
+ }
+ }
}
+QWifiManager::~QWifiManager()
+{
+ // exit event thread if it is running
+ if (m_eventThread->isRunning()) {
+ m_exitingEventThread = true;
+ call("SCAN");
+ m_eventThread->wait();
+ }
+ delete m_eventThread;
+ delete m_daemonClientSocket;
+}
+
+void QWifiManager::handleDhcpReply()
+{
+ if (m_daemonClientSocket->canReadLine()) {
+ QByteArray receivedMessage;
+ receivedMessage = m_daemonClientSocket->readLine(m_daemonClientSocket->bytesAvailable());
+ if (QT_WIFI_DEBUG) qDebug() << "QWifiManager: reply from qconnectivity: " << receivedMessage;
+ if (receivedMessage == "success") {
+ m_state = Connected;
+ emit networkStateChanged();
+ emit connectedSSIDChanged(m_connectedSSID);
+ // Store settings of a working wifi connection
+ call("SAVE_CONFIG");
+ } else if (receivedMessage == "failed") {
+ m_state = DhcpRequestFailed;
+ emit networkStateChanged();
+ } else {
+ qWarning() << "QWifiManager: unknown message: " << receivedMessage;
+ }
+ }
+}
+void QWifiManager::sendDhcpRequest(const QByteArray &request)
+{
+ if (QT_WIFI_DEBUG) qDebug() << "QWifiManager: sending request - " << request;
+ m_request = request;
+ m_request.append("\n");
+ m_daemonClientSocket->abort();
+ // path where android stores "reserved" sockets
+ m_daemonClientSocket->connectToServer(ANDROID_SOCKET_DIR "/qconnectivity");
+}
+
+void QWifiManager::connectedToDaemon()
+{
+ m_daemonClientSocket->write(m_request.constData(), m_request.length());
+ m_daemonClientSocket->flush();
+}
void QWifiManager::start()
{
- if (QT_WIFI_DEBUG) qDebug("QWifiManager: start");
+ if (QT_WIFI_DEBUG) qDebug("QWifiManager: connecting to the backend");
connectToBackend();
}
+void QWifiManager::stop()
+{
+ if (QT_WIFI_DEBUG) qDebug("QWifiManager: shutting down");
+ disconnectFromBackend();
+}
+void QWifiManager::connectToBackend()
+{
+ if (!(is_wifi_driver_loaded() || wifi_load_driver() == 0)) {
+ qWarning("QWifiManager: failed to load a driver");
+ return;
+ }
+ if (wifi_start_supplicant(0) != 0) {
+ qWarning("QWifiManager: failed to start a supplicant");
+ return;
+ }
+ if (wait_for_property(SUPPLICANT_SVC, "running", 5) < 0) {
+ qWarning("QWifiManager: Timed out waiting for supplicant to start");
+ return;
+ }
+ if (wifi_connect_to_supplicant(m_interface.constData()) == 0) {
+ m_backendReady = true;
+ emit backendReadyChanged();
+ property_set(QT_WIFI_BACKEND, "running");
+ } else {
+ qWarning("QWifiManager: failed to connect to a supplicant");
+ return;
+ }
+ if (QT_WIFI_DEBUG) qDebug("QWifiManager: started successfully");
+ m_exitingEventThread = false;
+ m_eventThread->start();
+ handleConnected();
+}
+
+void QWifiManager::disconnectFromBackend()
+{
+ m_exitingEventThread = true;
+ call("SCAN");
+ m_eventThread->wait();
+
+ if (wifi_stop_supplicant(0) < 0)
+ qWarning("QWifiManager: failed to stop supplicant");
+ wifi_close_supplicant_connection(m_interface.constData());
+ property_set(QT_WIFI_BACKEND, "stopped");
+ m_backendReady = false;
+ emit backendReadyChanged();
+}
void QWifiManager::setScanning(bool scanning)
{
@@ -129,70 +267,23 @@ void QWifiManager::setScanning(bool scanning)
return;
m_scanning = scanning;
- emit scanningChanged(scanning);
+ emit scanningChanged(m_scanning);
if (m_scanning) {
if (QT_WIFI_DEBUG) qDebug("QWifiManager: scanning");
call("SCAN");
- m_scanTimer = startTimer(5000);
+ m_scanTimer = startTimer(5000); // ### todo - this could be a qml property
} else {
if (QT_WIFI_DEBUG) qDebug("QWifiManager: stop scanning");
killTimer(m_scanTimer);
}
}
-
-
-QByteArray int_to_ip(int i) {
- QByteArray ip;
-
- ip.append(QByteArray::number(i & 0x000000ff));
- ip.append('.');
- ip.append(QByteArray::number((i & 0x0000ff00) >> 8));
- ip.append('.');
- ip.append(QByteArray::number((i & 0x00ff0000) >> 16));
- ip.append('.');
- ip.append(QByteArray::number(i >> 24));
-
- return ip;
-}
-
-
-void QWifiManager::connectToBackend()
-{
- if (m_internalState == IS_Uninitialized)
- m_internalState = IS_LoadDriver;
-
- if (m_internalState == IS_LoadDriver && (is_wifi_driver_loaded() || wifi_load_driver() >= 0))
- m_internalState = IS_StartBackend;
-
- if (m_internalState == IS_StartBackend && q_wifi_start_supplicant() >= 0) {
- m_internalState = IS_ConnectToBackend;
- sleep(3); //###
- }
-
- if (m_internalState == IS_ConnectToBackend && q_wifi_connect_to_supplicant() >= 0)
- m_internalState = IS_UpAndRunning;
-
- if (m_internalState == IS_UpAndRunning) {
- qDebug("QWifiManager: started successfully");
-
- emit readyChanged(true);
- m_eventThread = new QWifiManagerEventThread(this);
- m_eventThread->start();
-
- handleConnected();
- } else {
- qWarning("QWifiManager: stuck at internal state level: %d", m_internalState);
- }
-}
-
-
-QByteArray QWifiManager::call(const char *command)
+QByteArray QWifiManager::call(const char *command) const
{
char data[2048];
size_t len = sizeof(data) - 1; // -1: room to add a 0-terminator
- if (q_wifi_command(command, data, &len) < 0) {
+ if (wifi_command(m_interface.constData(), command, data, &len) < 0) {
qWarning("QWifiManager: call failed: %s", command);
return QByteArray();
}
@@ -203,13 +294,11 @@ QByteArray QWifiManager::call(const char *command)
return result;
}
-
-bool QWifiManager::checkedCall(const char *command)
+bool QWifiManager::checkedCall(const char *command) const
{
return call(command).trimmed().toUpper() == "OK";
}
-
bool QWifiManager::event(QEvent *e)
{
switch ((int) e->type()) {
@@ -243,9 +332,10 @@ void QWifiManager::connect(QWifiNetwork *network, const QString &passphrase)
if (!m_connectedSSID.isEmpty()) {
m_connectedSSID.clear();
emit connectedSSIDChanged(m_connectedSSID);
- //also possibly change online state
}
+ m_state = ObtainingIPAddress;
+ emit networkStateChanged();
bool networkKnown = false;
QByteArray id;
QByteArray listResult = call("LIST_NETWORKS");
@@ -277,7 +367,7 @@ void QWifiManager::connect(QWifiNetwork *network, const QString &passphrase)
QByteArray key_mgmt;
if (network->supportsWPA() || network->supportsWPA2()) {
ok = ok && checkedCall(setNetworkCommand + QByteArray(" psk ") + '"' + passphrase.toLatin1() + '"');
- key_mgmt = "WPA_PSK";
+ key_mgmt = "WPA-PSK";
} else if (network->supportsWEP()) {
ok = ok && checkedCall(setNetworkCommand + QByteArray(" wep_key0 ") + '"' + passphrase.toLatin1() + '"');
ok = ok && checkedCall(setNetworkCommand + QByteArray(" auth_alg OPEN SHARED"));
@@ -299,19 +389,16 @@ void QWifiManager::connect(QWifiNetwork *network, const QString &passphrase)
call("RECONNECT");
}
-
-class ProcessRunner : public QThread {
-public:
- void run() {
- QStringList args;
- args << QStringLiteral("-A")
- << QStringLiteral("-h") << QStringLiteral("KAON") //### hardcoded hostname, for testing
- << QStringLiteral("wlan0");
- QProcess::execute(QStringLiteral("dhcpcd"), args);
- deleteLater();
- }
-};
-
+void QWifiManager::disconnect()
+{
+ call("DISCONNECT");
+ QByteArray req = m_interface;
+ sendDhcpRequest(req.append(" disconnect"));
+ m_state = Disconnected;
+ m_connectedSSID.clear();
+ emit networkStateChanged();
+ emit connectedSSIDChanged(m_connectedSSID);
+}
void QWifiManager::handleConnected()
{
@@ -326,49 +413,16 @@ void QWifiManager::handleConnected()
if (connectedNetwork.isEmpty()) {
if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: not connected to a network...");
- m_online = false;
- onlineChanged(m_online);
+ m_state = Disconnected;
+ emit networkStateChanged();
return;
- } else {
- if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: current is %s", connectedNetwork.constData());
}
+ if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: current is %s", connectedNetwork.constData());
+
QList<QByteArray> info = connectedNetwork.split('\t');
m_connectedSSID = info.at(1);
- emit connectedSSIDChanged(m_connectedSSID);
-
- if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: starting dhcpcd...");
- QThread *t = new ProcessRunner();
- t->start();
-
- int ipaddr, gateway, mask, dns1, dns2, server, lease;
- if (do_dhcp_request(&ipaddr, &gateway, &mask, &dns1, &dns2, &server, &lease) == 0) {
- if (QT_WIFI_DEBUG) {
- printf("ip ........: %s\n"
- "gateway ...: %s\n"
- "mask ......: %s\n"
- "dns1 ......: %s\n"
- "dns2 ......: %s\n"
- "lease .....: %d\n",
- int_to_ip(ipaddr).constData(),
- int_to_ip(gateway).constData(),
- int_to_ip(mask).constData(),
- int_to_ip(dns1).constData(),
- int_to_ip(dns2).constData(),
- lease);
- }
-
- // Updating dns values..
- property_set("net.dns1", int_to_ip(dns1).constData());
- property_set("net.dns2", int_to_ip(dns2).constData());
-
- // Store (possibly updated) settings
- call("SAVE_CONFIG");
-
- m_online = true;
- onlineChanged(m_online);
- } else {
- if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: dhcp request failed...");
- }
+ QByteArray req = m_interface;
+ sendDhcpRequest(req.append(" connect"));
}