summaryrefslogtreecommitdiffstats
path: root/src/wifi
diff options
context:
space:
mode:
Diffstat (limited to 'src/wifi')
-rw-r--r--src/wifi/qwificonfiguration.cpp89
-rw-r--r--src/wifi/qwificonfiguration.h54
-rw-r--r--src/wifi/qwificontroller.cpp461
-rw-r--r--src/wifi/qwificontroller_p.h120
-rw-r--r--src/wifi/qwifidevice.cpp152
-rw-r--r--src/wifi/qwifidevice.h44
-rw-r--r--src/wifi/qwifielinux.cpp317
-rw-r--r--src/wifi/qwifielinux_p.h42
-rw-r--r--src/wifi/qwifimanager.cpp600
-rw-r--r--src/wifi/qwifimanager.h118
-rw-r--r--src/wifi/qwifimanager_p.h68
-rw-r--r--src/wifi/qwifinetwork.cpp121
-rw-r--r--src/wifi/qwifinetwork_p.h66
-rw-r--r--src/wifi/qwifinetworklistmodel.cpp184
-rw-r--r--src/wifi/qwifinetworklistmodel_p.h57
-rw-r--r--src/wifi/wifi.pro43
16 files changed, 2536 insertions, 0 deletions
diff --git a/src/wifi/qwificonfiguration.cpp b/src/wifi/qwificonfiguration.cpp
new file mode 100644
index 0000000..f2c859a
--- /dev/null
+++ b/src/wifi/qwificonfiguration.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwificonfiguration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWifiConfigurationPrivate
+{
+ Q_DECLARE_PUBLIC(QWifiConfiguration)
+public:
+ QWifiConfigurationPrivate(QWifiConfiguration *config);
+
+ // member variables
+ QWifiConfiguration *const q_ptr;
+ QString m_ssid;
+ QString m_psk;
+ QString m_protocol;
+};
+
+QWifiConfigurationPrivate::QWifiConfigurationPrivate(QWifiConfiguration *config)
+ : q_ptr(config)
+{
+}
+
+
+QWifiConfiguration::QWifiConfiguration(QObject *parent)
+ : QObject(parent)
+ , d_ptr(new QWifiConfigurationPrivate(this))
+{
+}
+
+QWifiConfiguration::~QWifiConfiguration()
+{
+ delete d_ptr;
+}
+
+void QWifiConfiguration::setSsid(const QString &ssid)
+{
+ Q_D(QWifiConfiguration);
+ d->m_ssid = ssid;
+}
+
+QString QWifiConfiguration::ssid() const
+{
+ Q_D(const QWifiConfiguration);
+ return d->m_ssid;
+}
+
+void QWifiConfiguration::setPassphrase(const QString &psk)
+{
+ Q_D(QWifiConfiguration);
+ d->m_psk = psk;
+}
+
+QString QWifiConfiguration::passphrase() const
+{
+ Q_D(const QWifiConfiguration);
+ return d->m_psk;
+}
+
+void QWifiConfiguration::setProtocol(const QString &protocol)
+{
+ Q_D(QWifiConfiguration);
+ d->m_protocol = protocol;
+}
+
+QString QWifiConfiguration::protocol() const
+{
+ Q_D(const QWifiConfiguration);
+ return d->m_protocol;
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwificonfiguration.h b/src/wifi/qwificonfiguration.h
new file mode 100644
index 0000000..2253fbb
--- /dev/null
+++ b/src/wifi/qwificonfiguration.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFICONFIGURATION_H
+#define QWIFICONFIGURATION_H
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+class QWifiConfigurationPrivate;
+
+QT_BEGIN_NAMESPACE
+
+class Q_DECL_EXPORT QWifiConfiguration : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString ssid READ ssid WRITE setSsid)
+ Q_PROPERTY(QString passphrase READ passphrase WRITE setPassphrase)
+ Q_PROPERTY(QString protocol READ protocol WRITE setProtocol)
+public:
+ explicit QWifiConfiguration(QObject *parent = 0);
+ virtual ~QWifiConfiguration();
+
+ void setSsid(const QString &ssid);
+ QString ssid() const;
+ void setPassphrase(const QString &psk);
+ QString passphrase() const;
+ void setProtocol(const QString &protocol);
+ QString protocol() const;
+
+private:
+ Q_DISABLE_COPY(QWifiConfiguration)
+ Q_DECLARE_PRIVATE(QWifiConfiguration)
+ QWifiConfigurationPrivate *const d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFICONFIGURATION_H
diff --git a/src/wifi/qwificontroller.cpp b/src/wifi/qwificontroller.cpp
new file mode 100644
index 0000000..ea49764
--- /dev/null
+++ b/src/wifi/qwificontroller.cpp
@@ -0,0 +1,461 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwificontroller_p.h"
+#include "qwifimanager_p.h"
+#include "qwifidevice.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QProcess>
+#include <QtCore/QByteArray>
+#include <QtCore/QFile>
+
+#ifdef Q_OS_ANDROID_NO_SDK
+#include <QtNetwork/QLocalSocket>
+#include <cutils/sockets.h>
+#include <unistd.h>
+#endif
+
+#ifdef Q_OS_ANDROID_NO_SDK
+/*
+ * Work API differences between Android versions
+ */
+int q_wifi_start_supplicant()
+{
+#if Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 1
+ return wifi_start_supplicant();
+#else
+ return wifi_start_supplicant(0);
+#endif
+}
+
+int q_wifi_stop_supplicant()
+{
+#if Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 1
+ return wifi_stop_supplicant();
+#else
+ return wifi_stop_supplicant(0);
+#endif
+}
+
+int q_wifi_connect_to_supplicant(const char *ifname)
+{
+#if Q_ANDROID_VERSION_MAJOR == 4 && (Q_ANDROID_VERSION_MINOR < 4 && Q_ANDROID_VERSION_MINOR >= 1)
+ return wifi_connect_to_supplicant(ifname);
+#else
+ Q_UNUSED(ifname);
+ return wifi_connect_to_supplicant();
+#endif
+}
+
+void q_wifi_close_supplicant_connection(const char *ifname)
+{
+#if Q_ANDROID_VERSION_MAJOR == 4 && (Q_ANDROID_VERSION_MINOR < 4 && Q_ANDROID_VERSION_MINOR >= 1)
+ wifi_close_supplicant_connection(ifname);
+#else
+ Q_UNUSED(ifname);
+ wifi_close_supplicant_connection();
+#endif
+}
+
+int q_wifi_wait_for_event(const char *ifname, char *buf, size_t len)
+{
+#if Q_ANDROID_VERSION_MAJOR == 4 && (Q_ANDROID_VERSION_MINOR < 4 && Q_ANDROID_VERSION_MINOR >= 1)
+ return wifi_wait_for_event(ifname, buf, len);
+#else
+ Q_UNUSED(ifname);
+ return wifi_wait_for_event(buf, len);
+#endif
+}
+
+int q_wifi_command(const char *ifname, const char *command, char *reply, size_t *reply_len)
+{
+#if Q_ANDROID_VERSION_MAJOR == 4 && (Q_ANDROID_VERSION_MINOR < 4 && Q_ANDROID_VERSION_MINOR >= 1)
+ return wifi_command(ifname, command, reply, reply_len);
+#else
+ Q_UNUSED(ifname);
+ return wifi_command(command, reply, reply_len);
+#endif
+}
+
+/*
+ * 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.
+ */
+const int NAP_TIME = 200; // wait for 200ms at a time when polling for property values
+int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+ if (maxnaps < 1) {
+ maxnaps = 1;
+ }
+
+ 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 */
+}
+
+#endif // Q_OS_ANDROID_NO_SDK
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(B2QT_WIFI, "qt.b2qt.wifi")
+
+class QWifiManagerEvent : public QEvent
+{
+public:
+ QWifiManagerEvent(QEvent::Type type, const QByteArray &data = QByteArray())
+ : QEvent(type)
+ , m_data(data)
+ {
+ }
+
+ QByteArray data() const { return m_data; }
+
+private:
+ QByteArray m_data;
+};
+
+class QWifiEventThread : public QThread
+{
+public:
+ QWifiEventThread(QWifiController *controller)
+ : m_controller(controller)
+ {
+ m_interface = QWifiDevice::wifiInterfaceName();
+ }
+
+ void run() {
+ qCDebug(B2QT_WIFI) << "running wifi event thread";
+ QWifiManagerEvent *event = 0;
+ char buffer[2048];
+ forever {
+ int size = q_wifi_wait_for_event(m_interface, buffer, sizeof(buffer) - 1);
+ if (size > 0) {
+ buffer[size] = 0;
+ event = 0;
+ qCDebug(B2QT_WIFI) << "[event]: " << buffer;
+ if (m_controller->isWifiThreadExitRequested()) {
+ qCDebug(B2QT_WIFI) << "exit wifi event thread";
+ return;
+ }
+ if (strstr(buffer, "SCAN-RESULTS")) {
+ event = new QWifiManagerEvent(WIFI_SCAN_RESULTS);
+ } else if (strstr(buffer, "CTRL-EVENT-CONNECTED")) {
+ event = new QWifiManagerEvent(WIFI_CONNECTED);
+ } else if (strstr(buffer, "CTRL-EVENT-DISCONNECTED")) {
+ event = new QWifiManagerEvent(WIFI_DISCONNECTED);
+ } else if (strstr(buffer, "Trying to associate")) {
+ event = new QWifiManagerEvent(WIFI_AUTHENTICATING);
+ } else if (strstr(buffer, "Handshake failed")) {
+ event = new QWifiManagerEvent(WIFI_HANDSHAKE_FAILED);
+ }
+ if (event)
+ QCoreApplication::postEvent(m_controller->wifiManager(), event);
+ }
+ }
+ }
+
+private:
+ QWifiController *m_controller;
+ QByteArray m_interface;
+};
+
+
+QWifiController::QWifiController(QWifiManager *manager, QWifiManagerPrivate *managerPrivate) :
+ m_manager(manager),
+ m_managerPrivate(managerPrivate),
+ m_exitEventThread(false),
+#ifdef Q_OS_ANDROID_NO_SDK
+ m_qcSocket(0),
+#endif
+ m_eventThread(0)
+{
+ m_interface = QWifiDevice::wifiInterfaceName();
+ m_eventThread = new QWifiEventThread(this);
+
+ qRegisterMetaType<QWifiManager::BackendState>("QWifiManager::BackendState");
+#ifdef Q_OS_ANDROID_NO_SDK
+ qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
+#endif
+}
+
+QWifiController::~QWifiController()
+{
+ exitWifiEventThread();
+ delete m_eventThread;
+#ifdef Q_OS_ANDROID_NO_SDK
+ delete m_qcSocket;
+#endif
+}
+
+void QWifiController::allocateOnThisThread()
+{
+#ifdef Q_OS_ANDROID_NO_SDK
+ m_qcSocket = new QLocalSocket;
+ int qcFd = socket_local_client("qconnectivity", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+ if (qcFd != -1) {
+ m_qcSocket->setSocketDescriptor(qcFd);
+ } else {
+ qCWarning(B2QT_WIFI) << "failed to get file descriptor of a qconnectivity socket!";
+ }
+#endif
+}
+
+void QWifiController::run()
+{
+ qCDebug(B2QT_WIFI) << "running wifi backend controller thread";
+ allocateOnThisThread();
+ Method method;
+ forever {
+ m_methodsMutex.lock();
+ if (m_methods.isEmpty())
+ methodCallRequested.wait(&m_methodsMutex);
+ method = m_methods.takeFirst();
+ m_methodsMutex.unlock();
+ switch (method) {
+ case InitializeBackend:
+ initializeBackend();
+ break;
+ case TerminateBackend:
+ terminateBackend();
+ break;
+ case AcquireIPAddress:
+ acquireIPAddress();
+ break;
+ case StopDhcp:
+ stopDhcp();
+ break;
+ case ExitEventLoop:
+ qCDebug(B2QT_WIFI) << "exit wifi backend controller thread";
+ return;
+ }
+ }
+}
+
+void QWifiController::call(Method method)
+{
+ QMutexLocker locker(&m_methodsMutex);
+ m_methods.append(method);
+ methodCallRequested.wakeOne();
+}
+
+void QWifiController::initializeBackend()
+{
+ qCDebug(B2QT_WIFI) << "initializing wifi backend";
+ emit backendStateChanged(QWifiManager::Initializing);
+
+#ifdef Q_OS_ANDROID_NO_SDK
+ qCDebug(B2QT_WIFI) << "initialize driver";
+ if (!(is_wifi_driver_loaded() || wifi_load_driver() == 0)) {
+ qCWarning(B2QT_WIFI) << "failed to load a driver";
+ return;
+ }
+#else
+ qCDebug(B2QT_WIFI) << "run ifconfig (up)";
+ QProcess ifconfig;
+ ifconfig.start(QStringLiteral("ifconfig"), QStringList() << QLatin1String(m_interface) << QStringLiteral("up"));
+ ifconfig.waitForFinished();
+ if (ifconfig.exitStatus() != QProcess::NormalExit && ifconfig.exitCode() != 0) {
+ qCWarning(B2QT_WIFI) << "failed to bring up wifi interface!";
+ return;
+ }
+#endif
+ resetSupplicantSocket();
+ startWifiEventThread();
+ qCDebug(B2QT_WIFI) << "wifi backend started successfully";
+ emit backendStateChanged(QWifiManager::Running);
+}
+
+void QWifiController::resetSupplicantSocket() const
+{
+ qCDebug(B2QT_WIFI) << "reset supplicant socket";
+ // close down the previous connection to supplicant if
+ // one exists before re-connecting.
+ if (q_wifi_stop_supplicant() < 0)
+ qCWarning(B2QT_WIFI) << "failed to stop supplicant!";
+ q_wifi_close_supplicant_connection(m_interface);
+ qCDebug(B2QT_WIFI) << "start supplicant";
+ if (q_wifi_start_supplicant() != 0) {
+ qCWarning(B2QT_WIFI) << "failed to start a supplicant!";
+ return;
+ }
+#ifdef Q_OS_ANDROID_NO_SDK
+ if (wait_for_property("init.svc.wpa_supplicant", "running", 5) < 0) {
+ qCWarning(B2QT_WIFI) << "timed out waiting for supplicant to start!";
+ return;
+ }
+#endif
+ qCDebug(B2QT_WIFI) << "connect to supplicant";
+ if (q_wifi_connect_to_supplicant(m_interface) != 0) {
+ qCWarning(B2QT_WIFI) << "failed to connect to a supplicant!";
+ return;
+ }
+}
+
+void QWifiController::terminateBackend()
+{
+ qCDebug(B2QT_WIFI) << "terminating wifi backend";
+ emit backendStateChanged(QWifiManager::Terminating);
+ exitWifiEventThread();
+ if (q_wifi_stop_supplicant() < 0)
+ qCWarning(B2QT_WIFI) << "failed to stop supplicant!";
+ q_wifi_close_supplicant_connection(m_interface);
+#ifndef Q_OS_ANDROID_NO_SDK
+ qCDebug(B2QT_WIFI) << "run ifconfig (down)";
+ QProcess ifconfig;
+ ifconfig.start(QStringLiteral("ifconfig"), QStringList() << QLatin1String(m_interface) << QStringLiteral("down"));
+ ifconfig.waitForFinished();
+ if (ifconfig.exitStatus() != QProcess::NormalExit && ifconfig.exitCode() != 0) {
+ qCWarning(B2QT_WIFI) << "failed to bring down wifi interface!";
+ return;
+ }
+#endif
+ stopDhcp();
+ qCDebug(B2QT_WIFI) << "wifi backend stopped successfully";
+ emit backendStateChanged(QWifiManager::NotRunning);
+}
+
+void QWifiController::startWifiEventThread()
+{
+ m_exitEventThread = false;
+ m_eventThread->start();
+}
+
+void QWifiController::exitWifiEventThread()
+{
+ if (m_eventThread->isRunning()) {
+ m_exitEventThread = true;
+ m_managerPrivate->call(QStringLiteral("SCAN"));
+ m_eventThread->wait();
+ }
+}
+
+#ifdef Q_OS_ANDROID_NO_SDK
+bool QWifiController::getQConnectivityReply()
+{
+ bool arrived = false;
+ if (m_qcSocket->canReadLine()) {
+ arrived = true;
+ QByteArray received = m_qcSocket->readLine(m_qcSocket->bytesAvailable());
+ if (received != "success" && received != "failed") {
+ qCWarning(B2QT_WIFI) << "unknown message: " << received;
+ received = "failed";
+ }
+ emit dhcpRequestFinished(QLatin1String(received));
+ }
+ return arrived;
+}
+#else
+void QWifiController::killDhcpProcess(const QString &path) const
+{
+ QFile pidFile(path);
+ if (pidFile.exists()) {
+ pidFile.open(QIODevice::ReadOnly);
+ QByteArray pid = pidFile.readAll();
+ QProcess kill;
+ kill.start(QStringLiteral("kill"), QStringList() << QLatin1String(pid.trimmed()));
+ kill.waitForFinished();
+ if (kill.exitStatus() != QProcess::NormalExit && kill.exitCode() != 0)
+ qCWarning(B2QT_WIFI) << "killing dhcp process failed!";
+ }
+ pidFile.close();
+}
+#endif
+
+void QWifiController::acquireIPAddress()
+{
+ qCDebug(B2QT_WIFI, "acquireIPAddress");
+#ifdef Q_OS_ANDROID_NO_SDK
+ QByteArray request = m_interface + " connect\n";
+ m_qcSocket->abort();
+ m_qcSocket->connectToServer(QStringLiteral(ANDROID_SOCKET_DIR "/qconnectivity"));
+ bool timeout = false;
+ if (m_qcSocket->waitForConnected()) {
+ m_qcSocket->write(request, request.length());
+ m_qcSocket->flush();
+ do {
+ if (m_qcSocket->waitForReadyRead()) {
+ if (getQConnectivityReply())
+ break;
+ } else {
+ timeout = true;
+ qCWarning(B2QT_WIFI) << "waiting a message from qconnectivity timed out!";
+ break;
+ }
+ } while (true);
+ } else {
+ timeout = true;
+ qCWarning(B2QT_WIFI) << "connecting to qconnectivity server socket timed out!";
+ }
+ if (timeout)
+ emit dhcpRequestFinished(QStringLiteral("failed"));
+#else
+ QString filePath = QLatin1String("/var/run/udhcpc." + m_interface + ".pid");
+ killDhcpProcess(filePath);
+ QStringList args;
+ args << QStringLiteral("-R") << QStringLiteral("-n") << QStringLiteral("-p")
+ << filePath << QStringLiteral("-i") << QLatin1String(m_interface);
+
+ QProcess udhcpc;
+ udhcpc.start(QStringLiteral("udhcpc"), args);
+ udhcpc.waitForFinished();
+ if (udhcpc.exitStatus() != QProcess::NormalExit && udhcpc.exitCode() != 0)
+ qCWarning(B2QT_WIFI) << "udhcpc process failed!";
+
+ QString status = QLatin1String("success");
+ if (udhcpc.readAll().contains("No lease"))
+ status = QLatin1String("failed");
+
+ emit dhcpRequestFinished(status);
+#endif
+}
+
+void QWifiController::stopDhcp() const
+{
+ qCDebug(B2QT_WIFI, "stopDhcp");
+#ifdef Q_OS_ANDROID_NO_SDK
+ QByteArray request = m_interface + " disconnect\n";
+ m_qcSocket->abort();
+ m_qcSocket->connectToServer(QStringLiteral(ANDROID_SOCKET_DIR "/qconnectivity"));
+ if (m_qcSocket->waitForConnected()) {
+ m_qcSocket->write(request, request.length());
+ m_qcSocket->flush();
+ } else {
+ qCWarning(B2QT_WIFI) << "connecting to qconnectivity server socket timed out!";
+ }
+#else
+ QString filePath = QLatin1String("/var/run/udhcpc." + m_interface + ".pid");
+ killDhcpProcess(filePath);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwificontroller_p.h b/src/wifi/qwificontroller_p.h
new file mode 100644
index 0000000..230b7c8
--- /dev/null
+++ b/src/wifi/qwificontroller_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFICONTROLLER_H
+#define QWIFICONTROLLER_H
+
+#include "qwifimanager.h"
+
+#include <QtCore/QEvent>
+#include <QtCore/QVector>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QLoggingCategory>
+
+#ifdef Q_OS_ANDROID_NO_SDK
+#include <hardware_legacy/wifi.h>
+#include <cutils/properties.h>
+#else
+#include "qwifielinux_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI)
+
+#ifdef Q_OS_ANDROID_NO_SDK
+int q_wifi_start_supplicant();
+int q_wifi_stop_supplicant();
+int q_wifi_connect_to_supplicant(const char *ifname);
+void q_wifi_close_supplicant_connection(const char *ifname);
+int q_wifi_wait_for_event(const char *ifname, char *buf, size_t len);
+int q_wifi_command(const char *ifname, const char *command, char *reply, size_t *reply_len);
+int wait_for_property(const char *name, const char *desired_value, int maxwait);
+#endif
+
+const QEvent::Type WIFI_SCAN_RESULTS = (QEvent::Type) (QEvent::User + 2001);
+const QEvent::Type WIFI_CONNECTED = (QEvent::Type) (QEvent::User + 2002);
+const QEvent::Type WIFI_HANDSHAKE_FAILED = (QEvent::Type) (QEvent::User + 2003);
+const QEvent::Type WIFI_AUTHENTICATING = (QEvent::Type) (QEvent::User + 2004);
+const QEvent::Type WIFI_DISCONNECTED = (QEvent::Type) (QEvent::User + 2005);
+
+class QWifiManager;
+class QWifiManagerPrivate;
+class QWifiEventThread;
+#ifdef Q_OS_ANDROID_NO_SDK
+class QLocalSocket;
+#endif
+
+class QWifiController : public QThread
+{
+ Q_OBJECT
+public:
+ enum Method {
+ InitializeBackend,
+ TerminateBackend,
+ AcquireIPAddress,
+ StopDhcp,
+ ExitEventLoop
+ };
+
+ explicit QWifiController(QWifiManager *manager, QWifiManagerPrivate *managerPrivate);
+ ~QWifiController();
+
+ void call(Method method);
+ QWifiManager *wifiManager() const { return m_manager; }
+ bool isWifiThreadExitRequested() const { return m_exitEventThread; }
+ void startWifiEventThread();
+ void acquireIPAddress();
+ void stopDhcp() const;
+ void resetSupplicantSocket() const;
+
+signals:
+ void backendStateChanged(QWifiManager::BackendState backendState);
+ void dhcpRequestFinished(const QString &status);
+
+protected:
+ void run();
+ void initializeBackend();
+ void terminateBackend();
+ void exitWifiEventThread();
+ void allocateOnThisThread();
+#ifdef Q_OS_ANDROID_NO_SDK
+ bool getQConnectivityReply();
+#else
+ void killDhcpProcess(const QString &path) const;
+#endif
+
+private:
+ QWifiManager *m_manager;
+ QWifiManagerPrivate *const m_managerPrivate;
+ bool m_exitEventThread;
+ QByteArray m_interface;
+#ifdef Q_OS_ANDROID_NO_SDK
+ QLocalSocket *m_qcSocket;
+#endif
+ QVector<Method> m_methods;
+ QWifiEventThread *m_eventThread;
+ QMutex m_methodsMutex;
+ QWaitCondition methodCallRequested;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFICONTROLLER_H
diff --git a/src/wifi/qwifidevice.cpp b/src/wifi/qwifidevice.cpp
new file mode 100644
index 0000000..4f100c4
--- /dev/null
+++ b/src/wifi/qwifidevice.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwifidevice.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDir>
+#ifdef Q_OS_ANDROID_NO_SDK
+#include <hardware_legacy/wifi.h>
+#include <cutils/properties.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype WifiDevice
+ \inqmlmodule QtWifi
+ \ingroup wifi-qmltypes
+ \brief Represents a physical device
+
+ Use this element to query if a device is WiFi capable before attempting
+ to use functionality of WifiManager.
+
+ \qml
+ import QtWifi 1.0
+
+ GroupBox {
+ id: wifiOptions
+ title: "Wifi"
+ visible: false
+
+ Component.onCompleted: {
+ if (WifiDevice.wifiSupported()) {
+ var component = Qt.createComponent("WifiGroupBox.qml")
+ var wifi = component.createObject(wifiOptions.contentItem)
+ if (wifi)
+ wifiOptions.visible = true
+ } else {
+ print("WiFi functionality not available on this device.")
+ }
+ }
+ }
+ \endqml
+*/
+
+/*!
+ \qmlmethod bool QWifiDevice::wifiSupported()
+
+ Returns true if the device is WiFi capable - WiFi driver and firmware has been
+ successfully loaded by the system, otherwise returns false.
+
+ \sa wifiInterfaceName
+*/
+
+QWifiDevice::QWifiDevice()
+{
+}
+
+QWifiDevice::~QWifiDevice()
+{
+}
+
+bool QWifiDevice::wifiSupported()
+{
+#ifdef Q_OS_ANDROID_NO_SDK
+ const char *fwpath = 0;
+ // reload wifi firmware
+ fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_STA);
+ if (!fwpath) {
+ qCWarning(B2QT_WIFI) << "failed to get firmware path";
+ return false;
+ }
+ if (wifi_change_fw_path((const char *)fwpath)) {
+ qCWarning(B2QT_WIFI) << "failed to change firmware path";
+ return false;
+ }
+#endif
+ QByteArray ifc = wifiInterfaceName();
+ bool hasInterface = QDir().exists(QString::fromLatin1("/sys/class/net/" + ifc));
+ if (!hasInterface)
+ qCWarning(B2QT_WIFI) << "could not find wifi interface in \"/sys/class/net/\", "
+ "looking for interface named: " << ifc;
+#ifdef Q_OS_ANDROID_NO_SDK
+ if (hasInterface && wifi_load_driver() == 0) {
+ return true;
+ } else {
+ qCWarning(B2QT_WIFI) << "wifi driver is not available";
+ return false;
+ }
+#else
+ return hasInterface;
+#endif
+}
+
+/*!
+ \fn QByteArray QWifiDevice::wifiInterfaceName()
+
+ Returns WiFi interface name.
+
+ \note On Android WiFi interface name is read from "wifi.interface" system property.
+ On Linux WiFi interface name is read from B2QT_WIFI_INTERFACE environmental variable if
+ it is set. The default interface name is "wlan0" if reading the designated places does not
+ provide an interface name.
+
+ /sa setWifiInterfaceName
+*/
+
+QByteArray QWifiDevice::wifiInterfaceName()
+{
+ QByteArray ifc;
+#ifdef Q_OS_ANDROID_NO_SDK
+ char interface[PROPERTY_VALUE_MAX];
+ property_get("wifi.interface", interface, NULL);
+ ifc = interface[0] == '\0' ? "wlan0" : interface;
+#else
+ ifc = qEnvironmentVariableIsSet("B2QT_WIFI_INTERFACE")
+ ? qgetenv("B2QT_WIFI_INTERFACE") : "wlan0";
+#endif
+ return ifc;
+}
+
+/*!
+ \fn void QWifiDevice::setWifiInterfaceName(const QByteArray &name)
+
+ A conveniece method for settings WiFi interface name.
+*/
+
+void QWifiDevice::setWifiInterfaceName(const QByteArray &name)
+{
+#ifdef Q_OS_ANDROID_NO_SDK
+ property_set("wifi.interface", name);
+#else
+ qputenv("B2QT_WIFI_INTERFACE", name);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwifidevice.h b/src/wifi/qwifidevice.h
new file mode 100644
index 0000000..80b4891
--- /dev/null
+++ b/src/wifi/qwifidevice.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFIDEVICE_H
+#define QWIFIDEVICE_H
+
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI)
+
+class Q_DECL_EXPORT QWifiDevice : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QWifiDevice();
+ virtual ~QWifiDevice();
+
+ Q_INVOKABLE static bool wifiSupported();
+ static QByteArray wifiInterfaceName();
+ static void setWifiInterfaceName(const QByteArray &name);
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFIDEVICE_H
diff --git a/src/wifi/qwifielinux.cpp b/src/wifi/qwifielinux.cpp
new file mode 100644
index 0000000..0dc3a39
--- /dev/null
+++ b/src/wifi/qwifielinux.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwifielinux_p.h"
+#include "qwifidevice.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QProcess>
+
+#include "wpa-supplicant/wpa_ctrl.h"
+
+#include <poll.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+QT_BEGIN_NAMESPACE
+
+const char SUPP_CONFIG_FILE[] = "/etc/wpa_supplicant.conf";
+const char IFACE_DIR[] = "/var/run/wpa_supplicant/";
+const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE ";
+
+static struct wpa_ctrl *ctrl_conn;
+static struct wpa_ctrl *monitor_conn;
+// socket pair used to exit from a blocking read
+static int exit_sockets[2];
+QByteArray ctrlInterface;
+
+int wifi_connect_on_socket_path(const char *path);
+int wifi_ctrl_recv(char *reply, size_t *reply_len);
+int wifi_wait_on_socket(char *buf, size_t buflen);
+int wifi_send_command(const char *cmd, char *reply, size_t *reply_len);
+void wifi_close_sockets();
+
+int q_wifi_start_supplicant()
+{
+ // #### TODO - if "/etc/wpa_supplicant/driver.$IFACE" exists, read driver name from there
+ QByteArray ifc = QWifiDevice::wifiInterfaceName();
+ QString driver(QStringLiteral("wext"));
+ QString pidFile = QLatin1String("/var/run/wpa_supplicant." + ifc + ".pid");
+
+ QStringList arg;
+ arg << QStringLiteral("--start") << QStringLiteral("--quiet") << QStringLiteral("--name");
+ arg << QStringLiteral("wpa_supplicant") << QStringLiteral("--startas");
+ arg << QStringLiteral("/usr/sbin/wpa_supplicant") << QStringLiteral("--pidfile") << pidFile;
+ arg << QStringLiteral("--") << QStringLiteral("-B") << QStringLiteral("-P") << pidFile;
+ arg << QStringLiteral("-i") << QLatin1String(ifc) << QStringLiteral("-c");
+ arg << QLatin1String(SUPP_CONFIG_FILE) << QStringLiteral("-D") << driver;
+
+ QProcess ssDaemon;
+ ssDaemon.start(QStringLiteral("start-stop-daemon"), arg);
+ ssDaemon.waitForFinished();
+ if (ssDaemon.exitStatus() != QProcess::NormalExit && ssDaemon.exitCode() != 0) {
+ qCWarning(B2QT_WIFI) << "failed to start a supplicant process!";
+ return -1;
+ }
+
+ QFile configFile;
+ configFile.setFileName(QLatin1String(SUPP_CONFIG_FILE));
+ if (configFile.exists() && configFile.open(QFile::ReadOnly)) {
+ ctrlInterface.clear();
+ while (!configFile.atEnd()) {
+ QByteArray line = configFile.readLine().trimmed();
+ if (line.startsWith("ctrl_interface")) {
+ ctrlInterface = line.mid(16);
+ break;
+ }
+ }
+ configFile.close();
+ if (!ctrlInterface.isEmpty()) {
+ // if the interface socket exists, then wpa_supplicant was invoked successfully
+ if (!QFile(QLatin1String(ctrlInterface + "/" + ifc)).exists())
+ return -1;
+ } else {
+ qCWarning(B2QT_WIFI) << "ctrl_interface is not set in " << SUPP_CONFIG_FILE;
+ return -1;
+ }
+ } else {
+ qCWarning(B2QT_WIFI) << "could not find/read wpa_supplicant configuration file in" << SUPP_CONFIG_FILE;
+ return -1;
+ }
+ // reset sockets used for exiting from hung state
+ exit_sockets[0] = exit_sockets[1] = -1;
+ return 0;
+}
+
+int q_wifi_stop_supplicant()
+{
+ QByteArray ifc = QWifiDevice::wifiInterfaceName();
+ QString pidFile = QLatin1String("/var/run/wpa_supplicant." + ifc + ".pid");
+ QStringList arg;
+ arg << QStringLiteral("--stop") << QStringLiteral("--quiet") << QStringLiteral("--name");
+ arg << QStringLiteral("wpa_supplicant") << QStringLiteral("--pidfile") << pidFile;
+ QProcess ssDaemon;
+ ssDaemon.start(QStringLiteral("start-stop-daemon"), arg);
+ ssDaemon.waitForFinished();
+ if (ssDaemon.exitStatus() != QProcess::NormalExit && ssDaemon.exitCode() != 0) {
+ qCWarning(B2QT_WIFI) << "failed to stop a supplicant process!";
+ return -1;
+ }
+
+ QFile::remove(QLatin1String(ctrlInterface + "/" + ifc));
+ QFile::remove(pidFile);
+ return 0;
+}
+
+int q_wifi_connect_to_supplicant(const char *ifname)
+{
+ static char path[4096];
+ snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, ifname);
+ return wifi_connect_on_socket_path(path);
+}
+
+int wifi_connect_on_socket_path(const char *path)
+{
+ // establishes the control and monitor socket connections on the interface
+ ctrl_conn = wpa_ctrl_open(path);
+ if (ctrl_conn == NULL) {
+ qCWarning(B2QT_WIFI, "Unable to open connection to supplicant on \"%s\": %s",
+ path, strerror(errno));
+ return -1;
+ }
+ monitor_conn = wpa_ctrl_open(path);
+ if (monitor_conn == NULL) {
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = NULL;
+ return -1;
+ }
+ if (wpa_ctrl_attach(monitor_conn) != 0) {
+ wpa_ctrl_close(monitor_conn);
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = monitor_conn = NULL;
+ return -1;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) {
+ wpa_ctrl_close(monitor_conn);
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = monitor_conn = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int q_wifi_wait_for_event(const char *ifname, char *buf, size_t buflen)
+{
+ Q_UNUSED(ifname);
+ return wifi_wait_on_socket(buf, buflen);
+}
+
+int wifi_wait_on_socket(char *buf, size_t buflen)
+{
+ size_t nread = buflen - 1;
+ int result;
+ char *match, *match2;
+
+ if (monitor_conn == NULL) {
+ return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - connection closed");
+ }
+
+ result = wifi_ctrl_recv(buf, &nread);
+
+ /* Terminate reception on exit socket */
+ if (result == -2) {
+ return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - connection closed");
+ }
+
+ if (result < 0) {
+ qCWarning(B2QT_WIFI, "wifi_ctrl_recv failed: %s", strerror(errno));
+ return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - recv error");
+ }
+ buf[nread] = '\0';
+ /* Check for EOF on the socket */
+ if (result == 0 && nread == 0) {
+ /* Fabricate an event to pass up */
+ qCWarning(B2QT_WIFI, "Received EOF on supplicant socket");
+ return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - signal 0 received");
+ }
+ /*
+ * Events strings are in the format
+ *
+ * IFNAME=iface <N>CTRL-EVENT-XXX
+ * or
+ * <N>CTRL-EVENT-XXX
+ *
+ * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG,
+ * etc.) and XXX is the event name. The level information is not useful
+ * to us, so strip it off.
+ */
+
+ if (strncmp(buf, "IFNAME=", (sizeof("IFNAME=") - 1)) == 0) {
+ match = strchr(buf, ' ');
+ if (match != NULL) {
+ if (match[1] == '<') {
+ match2 = strchr(match + 2, '>');
+ if (match2 != NULL) {
+ nread -= (match2 - match);
+ memmove(match + 1, match2 + 1, nread - (match - buf) + 1);
+ }
+ }
+ } else {
+ return snprintf(buf, buflen, "%s", WPA_EVENT_IGNORE);
+ }
+ } else if (buf[0] == '<') {
+ match = strchr(buf, '>');
+ if (match != NULL) {
+ nread -= (match + 1 - buf);
+ memmove(buf, match + 1, nread + 1);
+ //qCWarning(B2QT_WIFI, "supplicant generated event without interface - %s", buf);
+ }
+ } else {
+ /* let the event go as is! */
+ qCWarning(B2QT_WIFI, "supplicant generated event without interface and without message level - %s", buf);
+ }
+
+ return nread;
+}
+
+int wifi_ctrl_recv(char *reply, size_t *reply_len)
+{
+ int res = 0;
+ int ctrlfd = wpa_ctrl_get_fd(monitor_conn);
+ struct pollfd rfds[2];
+
+ memset(rfds, 0, 2 * sizeof(struct pollfd));
+ rfds[0].fd = ctrlfd;
+ rfds[0].events |= POLLIN;
+ rfds[1].fd = exit_sockets[1];
+ rfds[1].events |= POLLIN;
+ res = TEMP_FAILURE_RETRY(poll(rfds, 2, -1));
+ if (res < 0) {
+ qCWarning(B2QT_WIFI, "Error poll = %d", res);
+ return res;
+ }
+ if (rfds[0].revents & POLLIN) {
+ return wpa_ctrl_recv(monitor_conn, reply, reply_len);
+ }
+
+ /* it is not rfds[0], then it must be rfts[1] (i.e. the exit socket)
+ * or we timed out. In either case, this call has failed ..
+ */
+ return -2;
+}
+
+int wifi_send_command(const char *cmd, char *reply, size_t *reply_len)
+{
+ int ret;
+ if (ctrl_conn == NULL) {
+ qCWarning(B2QT_WIFI, "Not connected to wpa_supplicant - \"%s\" command dropped.", cmd);
+ return -1;
+ }
+ ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), reply, reply_len, NULL);
+ if (ret == -2) {
+ qCWarning(B2QT_WIFI, "'%s' command timed out.", cmd);
+ /* unblocks the monitor receive socket for termination */
+ TEMP_FAILURE_RETRY(write(exit_sockets[0], "T", 1));
+ return -2;
+ } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) {
+ return -1;
+ }
+ if (strncmp(cmd, "PING", 4) == 0) {
+ reply[*reply_len] = '\0';
+ }
+ return 0;
+}
+
+int q_wifi_command(const char *ifname, const char *command, char *reply, size_t *reply_len)
+{
+ Q_UNUSED(ifname);
+ return wifi_send_command(command, reply, reply_len);
+}
+
+void q_wifi_close_supplicant_connection(const char *ifname)
+{
+ Q_UNUSED(ifname)
+ wifi_close_sockets();
+}
+
+void wifi_close_sockets()
+{
+ if (ctrl_conn != NULL) {
+ wpa_ctrl_close(ctrl_conn);
+ ctrl_conn = NULL;
+ }
+
+ if (monitor_conn != NULL) {
+ wpa_ctrl_close(monitor_conn);
+ monitor_conn = NULL;
+ }
+
+ if (exit_sockets[0] >= 0) {
+ close(exit_sockets[0]);
+ exit_sockets[0] = -1;
+ }
+
+ if (exit_sockets[1] >= 0) {
+ close(exit_sockets[1]);
+ exit_sockets[1] = -1;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwifielinux_p.h b/src/wifi/qwifielinux_p.h
new file mode 100644
index 0000000..bef90c3
--- /dev/null
+++ b/src/wifi/qwifielinux_p.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef LOCAL_WIFI_H
+#define LOCAL_WIFI_H
+
+#include <QtCore/QLoggingCategory>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI)
+
+// This API mirrors Android's Wi-Fi libraries interface [1] and parts of implementation.
+// [1] http://androidxref.com/4.4.2_r2/xref/hardware/libhardware_legacy/include/hardware_legacy/wifi.h
+
+int q_wifi_command(const char *ifname, const char *command, char *reply, size_t *reply_len);
+int q_wifi_wait_for_event(const char *ifname, char *buf, size_t buflen);
+int q_wifi_connect_to_supplicant(const char *ifname);
+void q_wifi_close_supplicant_connection(const char *ifname);
+int q_wifi_start_supplicant();
+int q_wifi_stop_supplicant();
+
+QT_END_NAMESPACE
+
+#endif // LOCAL_WIFI_H
diff --git a/src/wifi/qwifimanager.cpp b/src/wifi/qwifimanager.cpp
new file mode 100644
index 0000000..de35224
--- /dev/null
+++ b/src/wifi/qwifimanager.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwificontroller_p.h"
+#include "qwifinetworklistmodel_p.h"
+#include "qwifinetwork_p.h"
+#include "qwifimanager_p.h"
+#include "qwifidevice.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QProcess>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtWifi 1.0
+ \title QtWifi Module
+ \ingroup qtee-qmlmodules
+ \brief A module for managing wireless network connectivity.
+
+ Provides QML types for controlling WiFi networks - handling WiFi backend initialization,
+ retrieving information from nearby WiFi access points, setting up and bringing down WiFi
+ connections, querying DHCP server for IP address.
+
+ The import command for adding these QML types is:
+
+ \code
+ import QtWifi 1.0
+ \endcode
+
+ \section1 API Reference
+*/
+
+/*!
+
+ \qmltype WifiManager
+ \inqmlmodule QtWifi
+ \ingroup wifi-qmltypes
+ \brief Main interface to the WiFi functionality.
+
+ WifiManager is a singleton type that provides information about the WiFi backend and
+ available networks, use it to control the WiFi backend, scan for wireless networks
+ and connect to selected network. WifiManager provides events for backend and network
+ state changes.
+
+ */
+
+/*!
+ \qmlsignal void WifiManager::networkStateChanged(NetworkState networkState)
+
+ This signal is emitted whenever changes in a network state occur. The network name for
+ which the state changes events are send can be obtained from currentSSID.
+
+ \sa networkState
+*/
+
+/*!
+ \qmlsignal void WifiManager::backendStateChanged(BackendState backendState)
+
+ This signal is emitted whenever changes in a backend state occur.
+
+ \sa start, stop
+*/
+
+/*!
+ \qmlsignal void WifiManager::currentSSIDChanged(string currentSSID)
+
+ This signal is emitted when switching between different WiFi networks.
+
+ \sa start, stop
+*/
+
+/*!
+ \qmlsignal void WifiManager::scanningChanged(bool scanning)
+
+ This signal is emitted when device starts or stops to scan for available wifi networks.
+
+ \sa scanning
+
+*/
+
+// must be in the same order as in enum {} definiton
+const char *nsText[] = { "Disconnected", "Authenticating", "HandshakeFailed",
+ "ObtainingIPAddress", "DhcpRequestFailed", "Connected" };
+const char *bsText[] = { "Initializing", "Running", "Terminating", "NotRunning" };
+
+QWifiManagerPrivate::QWifiManagerPrivate(QWifiManager *manager)
+ : q_ptr(manager)
+ , m_networkListModel(new QWifiNetworkListModel())
+ , m_scanTimer(0)
+ , m_scanning(false)
+ , m_interface(QWifiDevice::wifiInterfaceName())
+ , m_backendState(QWifiManager::NotRunning)
+ , m_networkState(QWifiManager::Disconnected)
+ , m_setCurrentSSID(true)
+{
+ qCDebug(B2QT_WIFI) << "using wifi interface: " << m_interface;
+}
+
+QWifiManagerPrivate::~QWifiManagerPrivate()
+{
+ delete m_wifiController;
+ delete m_networkListModel;
+}
+
+QString QWifiManagerPrivate::getConnectedNetwork()
+{
+ QStringList lists = call(QStringLiteral("LIST_NETWORKS")).split('\n');
+ QString connectedNetwork;
+ for (int i = 1; i < lists.size(); ++i) {
+ if (lists.at(i).toUpper().contains(QStringLiteral("[CURRENT]"))) {
+ connectedNetwork = lists.at(i);
+ break;
+ }
+ }
+ return connectedNetwork;
+}
+
+void QWifiManagerPrivate::emitCurrentSSIDChanged()
+{
+ Q_Q(QWifiManager);
+ if (m_previousSSID != m_currentSSID) {
+ qCDebug(B2QT_WIFI) << "current SSID: " << m_previousSSID << " -> " << m_currentSSID;
+ m_previousSSID = m_currentSSID;
+ emit q->currentSSIDChanged(m_currentSSID);
+ }
+}
+
+void QWifiManagerPrivate::setCurrentSSID()
+{
+ qCDebug(B2QT_WIFI, "setCurrentSSID");
+ m_setCurrentSSID = false;
+ QString connectedNetwork = getConnectedNetwork();
+ if (!connectedNetwork.isEmpty()) {
+ QString ssid = connectedNetwork.split('\t').at(1);
+ QWifiNetwork *network = m_networkListModel->networkForSSID(ssid);
+ if (network) {
+ m_currentSSID = network->ssid();
+ emitCurrentSSIDChanged();
+ if (call(QStringLiteral("STATUS")).contains(QStringLiteral("wpa_state=COMPLETED")))
+ updateNetworkState(QWifiManager::Connected);
+ }
+ }
+}
+
+void QWifiManagerPrivate::handleConnected()
+{
+ qCDebug(B2QT_WIFI, "handleConnected");
+ QString connectedNetwork = getConnectedNetwork();
+ if (connectedNetwork.isEmpty())
+ return;
+
+ m_currentSSID = connectedNetwork.split('\t').at(1);
+ qCDebug(B2QT_WIFI) << "connected network: " << m_currentSSID;
+ updateNetworkState(QWifiManager::ObtainingIPAddress);
+ m_wifiController->call(QWifiController::AcquireIPAddress);
+}
+
+void QWifiManagerPrivate::handleDisconneced()
+{
+ updateNetworkState(QWifiManager::Disconnected);
+}
+
+void QWifiManagerPrivate::updateNetworkState(QWifiManager::NetworkState networkState)
+{
+ Q_Q(QWifiManager);
+ qCDebug(B2QT_WIFI, "network state: %s -> %s", nsText[m_networkState], nsText[networkState]);
+ if (m_networkState == networkState)
+ return;
+
+ m_networkState = networkState;
+ emit q->networkStateChanged(m_networkState);
+}
+
+void QWifiManagerPrivate::updateBackendState(QWifiManager::BackendState backendState)
+{
+ Q_Q(QWifiManager);
+ qCDebug(B2QT_WIFI, "backend state: %s -> %s", bsText[m_backendState], bsText[backendState]);
+ if (m_backendState == backendState)
+ return;
+
+ m_backendState = backendState;
+ emit q->backendStateChanged(m_backendState);
+}
+
+void QWifiManagerPrivate::updateWifiState()
+{
+ bool supplicantRunning = false;
+#ifdef Q_OS_ANDROID_NO_SDK
+ char supplicantState[PROPERTY_VALUE_MAX];
+ if (property_get("init.svc.wpa_supplicant", supplicantState, 0)) {
+ if (strcmp(supplicantState, "running") == 0)
+ supplicantRunning = true;
+ }
+#else
+ QProcess ps;
+ ps.start(QStringLiteral("ps"));
+ ps.waitForFinished();
+ if (ps.readAll().contains("wpa_supplicant"))
+ supplicantRunning = true;
+#endif
+ if (supplicantRunning) {
+ m_wifiController->resetSupplicantSocket();
+ m_wifiController->startWifiEventThread();
+ m_backendState = QWifiManager::Running;
+ }
+}
+
+QString QWifiManagerPrivate::call(const QString &command)
+{
+ if (m_backendState != QWifiManager::Running)
+ return QString();
+
+ char data[2048];
+ size_t len = sizeof(data) - 1; // -1: room to add a 0-terminator
+ QString actualCommand = command;
+#ifdef Q_OS_ANDROID_NO_SDK
+#if !(Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 4)
+ QString prefix = QLatin1String("IFNAME=" + m_interface + " ");
+ actualCommand.prepend(prefix);
+#endif
+#endif
+ if (q_wifi_command(m_interface, actualCommand.toLatin1(), data, &len) < 0) {
+ qCDebug(B2QT_WIFI) << "call to supplicant failed: " << actualCommand;
+ return QString();
+ }
+ if (len < sizeof(data))
+ data[len] = 0;
+
+ QString result = QLatin1String(data);
+ return result;
+}
+
+bool QWifiManagerPrivate::checkedCall(const QString &command)
+{
+ return call(command).trimmed().toUpper() == QLatin1String("OK");
+}
+
+void QWifiManagerPrivate::updateLastError(const QString &error)
+{
+ Q_Q(QWifiManager);
+ qCWarning(B2QT_WIFI) << error;
+ m_lastError = error;
+ emit q->lastErrorChanged(m_lastError);
+}
+
+
+QWifiManager* QWifiManager::m_instance = 0;
+QWifiManager* QWifiManager::instance()
+{
+ if (!m_instance)
+ m_instance = new QWifiManager();
+ return m_instance;
+}
+
+QWifiManager::QWifiManager()
+ : d_ptr(new QWifiManagerPrivate(this))
+{
+ Q_D(QWifiManager);
+
+ if (!QWifiDevice::wifiSupported())
+ qCWarning(B2QT_WIFI) << "WifiManager may not work as expected on this device. Use the API provided by QtWifi "
+ "library to verify if device has support for Wi-Fi before creating an instance of wifi manager!";
+
+ d->m_wifiController = new QWifiController(this, d_ptr);
+ QObject::connect(d->m_wifiController, &QWifiController::backendStateChanged,
+ this, &QWifiManager::handleBackendStateChanged);
+ QObject::connect(d->m_wifiController, &QWifiController::dhcpRequestFinished,
+ this, &QWifiManager::handleDhcpRequestFinished);
+ d->m_wifiController->start();
+
+ d->updateWifiState();
+}
+
+QWifiManager::~QWifiManager()
+{
+ Q_D(QWifiManager);
+ d->m_wifiController->call(QWifiController::ExitEventLoop);
+ d->m_wifiController->wait();
+ delete d_ptr;
+}
+
+/*!
+ \qmlproperty WifiNetworkListModel WifiManager::networks
+ \readonly
+
+ This property holds a list of networks that can be sensed by a device. Use the returned
+ model as data model in ListView, model is updated every 5 seconds.
+
+ WifiNetworkListModel is a simple data model consisting of WifiNetwork objects, accessed with
+ the "network" data role name. Instances of WifiNetwork cannot be created directly from the QML system.
+
+*/
+
+QAbstractListModel *QWifiManager::networks() const
+{
+ Q_D(const QWifiManager);
+ return d->m_networkListModel;
+}
+
+/*!
+ \qmlproperty string WifiManager::currentSSID
+ \readonly
+
+ This property holds the network name for which the networkState changes events are sent or
+ or an empty string when there is no active network.
+*/
+
+QString QWifiManager::currentSSID() const
+{
+ Q_D(const QWifiManager);
+ return d->m_currentSSID;
+}
+
+/*!
+ \qmlproperty enumeration WifiManager::networkState
+ \readonly
+
+ This property holds the current state of the network connection.
+
+ \list
+ \li \e WifiManager.Disconnected - Not connected to any network
+ \li \e WifiManager.Authenticating - Verifying password with the network provider
+ \li \e WifiManager.HandshakeFailed - Incorrect password provided
+ \li \e WifiManager.ObtainingIPAddress - Requesting IP address from DHCP server
+ \li \e WifiManager.DhcpRequestFailed - Could not retrieve IP address
+ \li \e WifiManager.Connected - Ready to process network requests
+ \endlist
+*/
+
+QWifiManager::NetworkState QWifiManager::networkState() const
+{
+ Q_D(const QWifiManager);
+ return d->m_networkState;
+}
+
+/*!
+ \qmlproperty enumeration WifiManager::backendState
+ \readonly
+
+ This property holds the current state of the WiFi backend.
+
+ \list
+ \li \e WifiManager.Initializing - Wireless supplicant is starting up
+ \li \e WifiManager.Running - Supplicant is initialized and ready to process commands
+ \li \e WifiManager.Terminating - Shutting down wireless supplicant
+ \li \e WifiManager.NotRunning - Wireless supplicant process is not running
+ \endlist
+*/
+
+QWifiManager::BackendState QWifiManager::backendState() const
+{
+ Q_D(const QWifiManager);
+ return d->m_backendState;
+}
+
+/*!
+ \qmlmethod void WifiManager::start()
+
+ Start the WiFi backend. This function returns immediately, the backendState
+ change events are delivered asynchronously.
+
+ \sa stop, backendState
+ */
+
+void QWifiManager::start()
+{
+ Q_D(QWifiManager);
+ d->m_wifiController->call(QWifiController::InitializeBackend);
+}
+
+/*!
+ \qmlmethod void WifiManager::stop()
+
+ Stop the WiFi backend and if connected to any network shut down network connection.
+ This function returns immediately, the backendState change events are delivered asynchronously.
+
+ \sa start, backendState
+ */
+
+void QWifiManager::stop()
+{
+ Q_D(QWifiManager);
+ d->m_wifiController->call(QWifiController::TerminateBackend);
+}
+
+void QWifiManager::handleBackendStateChanged(BackendState backendState)
+{
+ Q_D(QWifiManager);
+ switch (backendState) {
+ case Running:
+ d->m_setCurrentSSID = true;
+ break;
+ case NotRunning:
+ d->updateNetworkState(Disconnected);
+ break;
+ default:
+ break;
+ }
+ d->updateBackendState(backendState);
+}
+
+void QWifiManager::handleDhcpRequestFinished(const QString &status)
+{
+ Q_D(QWifiManager);
+ qCDebug(B2QT_WIFI) << "handleDhcpRequestFinished: " << status << " for " << d->m_currentSSID;
+ if (status == QLatin1String("success")) {
+ d->emitCurrentSSIDChanged();
+ d->updateNetworkState(Connected);
+ d->call(QStringLiteral("SAVE_CONFIG"));
+ } else {
+ d->updateNetworkState(DhcpRequestFailed);
+ }
+}
+
+void QWifiManager::setScanning(bool scanning)
+{
+ Q_D(QWifiManager);
+ if (d->m_scanning == scanning)
+ return;
+
+ d->m_scanning = scanning;
+ emit scanningChanged(d->m_scanning);
+ if (d->m_scanning) {
+ d->call(QStringLiteral("SCAN"));
+ // ### TODO android has property for this - wifi.supplicant_scan_interval
+ d->m_scanTimer = startTimer(5000);
+ } else {
+ killTimer(d->m_scanTimer);
+ }
+}
+
+/*!
+ \qmlproperty bool WifiManager::scanning
+
+ This property holds whether or not the backend is scanning for WiFi networks. To
+ preserve battery energy, set this property to false when scanning is not required.
+
+ Before starting to scan for networks, you need to initialize the WiFi backend.
+
+ \sa start
+*/
+
+bool QWifiManager::scanning() const
+{
+ Q_D(const QWifiManager);
+ return d->m_scanning;
+}
+
+QString QWifiManager::lastError() const
+{
+ Q_D(const QWifiManager);
+ return d->m_lastError;
+}
+
+bool QWifiManager::event(QEvent *event)
+{
+ Q_D(QWifiManager);
+ switch ((int) event->type()) {
+ case WIFI_SCAN_RESULTS:
+ d->m_networkListModel->parseScanResults(d->call(QStringLiteral("SCAN_RESULTS")));
+ if (d->m_setCurrentSSID || d->m_currentSSID.isEmpty())
+ d->setCurrentSSID();
+ return true;
+ case WIFI_CONNECTED:
+ d->handleConnected();
+ return true;
+ case WIFI_DISCONNECTED:
+ d->handleDisconneced();
+ return true;
+ case WIFI_AUTHENTICATING:
+ d->updateNetworkState(Authenticating);
+ d->emitCurrentSSIDChanged();
+ return true;
+ case WIFI_HANDSHAKE_FAILED:
+ d->updateNetworkState(HandshakeFailed);
+ return true;
+ case QEvent::Timer: {
+ int tid = static_cast<QTimerEvent *>(event)->timerId();
+ if (tid == d->m_scanTimer) {
+ d->call(QStringLiteral("SCAN"));
+ return true;
+ }
+ break;
+ }
+ }
+ return QObject::event(event);
+}
+
+
+/*!
+ \qmlmethod void WifiManager::connect(WifiNetwork network, string passphrase)
+
+ Connect to network \a network and use passphrase \a passphrase for authentication.
+
+ \sa disconnect, networkState
+ */
+
+bool QWifiManager::connect(QWifiConfiguration *config)
+{
+ Q_D(QWifiManager);
+ if (d->m_backendState != Running) {
+ qCWarning(B2QT_WIFI) << "start wifi backend before calling connect() !";
+ return false;
+ }
+
+ d->call(QStringLiteral("DISABLE_NETWORK all"));
+
+ d->m_currentSSID = config->ssid();
+ bool networkKnown = false;
+ QString id;
+ QString listResult = d->call(QStringLiteral("LIST_NETWORKS"));
+ QStringList lines = listResult.split('\n');
+ foreach (const QString &line, lines) {
+ if (line.contains(d->m_currentSSID)) {
+ id = line.split('\t').at(0);
+ networkKnown = true;
+ break;
+ }
+ }
+
+ if (!networkKnown) {
+ bool ok;
+ id = d->call(QStringLiteral("ADD_NETWORK")).trimmed();
+ id.toInt(&ok);
+ if (!ok) {
+ d->updateLastError(QStringLiteral("failed to add network!"));
+ return false;
+ }
+ }
+
+ bool ok = true;
+ QChar q = QLatin1Char('"');
+ QString setNetworkCommand = QLatin1String("SET_NETWORK ") + id;
+ if (!networkKnown) {
+ ok = ok && d->checkedCall(setNetworkCommand + QLatin1String(" ssid ") + q + d->m_currentSSID + q);
+ }
+
+ QString key_mgmt;
+ QString protocol = config->protocol().toUpper();
+ QString psk = config->passphrase();
+ // ref: http://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf
+ if (protocol.isEmpty() || protocol.contains(QStringLiteral("WPA"))) {
+ // ### todo - password length has limits (see IEEE 802.11), we need to check
+ // for those limits here. Supplicant gives only a meaningless "fail" message.
+ ok = ok && d->checkedCall(setNetworkCommand + QLatin1String(" psk ") + q + psk + q);
+ key_mgmt = QLatin1String("WPA-PSK");
+ } else if (protocol.contains(QStringLiteral("WEP"))) {
+ ok = ok && d->checkedCall(setNetworkCommand + QLatin1String(" wep_key0 ") + q + psk + q);
+ ok = ok && d->checkedCall(setNetworkCommand + QLatin1String(" auth_alg OPEN SHARED"));
+ key_mgmt = QLatin1String("NONE");
+ } else if (protocol.contains(QStringLiteral("WPS")) && psk.length() == 0) {
+ // open network
+ key_mgmt = QLatin1String("NONE");
+ }
+ ok = ok && d->checkedCall(setNetworkCommand + QLatin1String(" key_mgmt ") + key_mgmt);
+ if (!ok) {
+ if (!networkKnown)
+ d->call(QLatin1String("REMOVE_NETWORK ") + id);
+ d->updateLastError(QLatin1String("failed to set properties on network: ") + id);
+ return false;
+ }
+
+ d->call(QLatin1String("SELECT_NETWORK ") + id);
+ d->call(QStringLiteral("RECONNECT"));
+
+ return true;
+}
+
+/*!
+ \qmlmethod void WifiManager::disconnect()
+
+ Disconnect from currently connected network connection.
+
+ \sa connect, networkState
+*/
+
+void QWifiManager::disconnect()
+{
+ Q_D(QWifiManager);
+ d->call(QStringLiteral("DISCONNECT"));
+ d->m_wifiController->call(QWifiController::StopDhcp);
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwifimanager.h b/src/wifi/qwifimanager.h
new file mode 100644
index 0000000..cd55f4d
--- /dev/null
+++ b/src/wifi/qwifimanager.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFIMANAGER_H
+#define QWIFIMANAGER_H
+
+#include "qwificonfiguration.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+#include <QtCore/QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI)
+
+class QWifiManagerPrivate;
+class QAbstractListModel;
+class QWifiController;
+class QWifiNetworkListModel;
+
+class Q_DECL_EXPORT QWifiManager : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(NetworkState)
+ Q_ENUMS(BackendState)
+ Q_PROPERTY(NetworkState networkState READ networkState NOTIFY networkStateChanged)
+ Q_PROPERTY(BackendState backendState READ backendState NOTIFY backendStateChanged)
+ Q_PROPERTY(bool scanning READ scanning WRITE setScanning NOTIFY scanningChanged)
+ Q_PROPERTY(QString currentSSID READ currentSSID NOTIFY currentSSIDChanged)
+ Q_PROPERTY(QString lastError READ lastError NOTIFY lastErrorChanged)
+ Q_PROPERTY(QAbstractListModel *networks READ networks CONSTANT)
+public:
+ enum NetworkState {
+ Disconnected,
+ Authenticating,
+ HandshakeFailed,
+ ObtainingIPAddress,
+ DhcpRequestFailed,
+ Connected
+ };
+
+ enum BackendState {
+ Initializing,
+ Running,
+ Terminating,
+ NotRunning
+ };
+
+ enum Roles {
+ SSIDRole = Qt::UserRole + 1,
+ BSSIDRole = Qt::UserRole + 2,
+ SignalRole = Qt::UserRole + 3,
+ WPARole = Qt::UserRole + 4,
+ WPA2Role = Qt::UserRole + 5,
+ WEPRole = Qt::UserRole + 6,
+ WPSRole = Qt::UserRole + 7
+ };
+
+ static QWifiManager *instance();
+ virtual ~QWifiManager();
+
+ QAbstractListModel *networks() const;
+ QString currentSSID() const;
+ bool scanning() const;
+ void setScanning(bool scanning);
+ NetworkState networkState() const;
+ BackendState backendState() const;
+ QString lastError() const;
+
+public slots:
+ void start();
+ void stop();
+ bool connect(QWifiConfiguration *config);
+ void disconnect();
+
+signals:
+ void scanningChanged(bool scanning);
+ void networkStateChanged(NetworkState networkState);
+ void backendStateChanged(BackendState backendState);
+ void currentSSIDChanged(const QString &currentSSID);
+ void lastErrorChanged(const QString &error);
+
+protected:
+ bool event(QEvent *event);
+
+private slots:
+ void handleBackendStateChanged(BackendState backendState);
+ void handleDhcpRequestFinished(const QString &status);
+
+private:
+ QWifiManager();
+ static QWifiManager* m_instance;
+ friend class QWifiController;
+ Q_DISABLE_COPY(QWifiManager)
+ Q_DECLARE_PRIVATE(QWifiManager)
+ QWifiManagerPrivate *const d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFIMANAGER_H
diff --git a/src/wifi/qwifimanager_p.h b/src/wifi/qwifimanager_p.h
new file mode 100644
index 0000000..ef8a0da
--- /dev/null
+++ b/src/wifi/qwifimanager_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFIMANAGER_P_H
+#define QWIFIMANAGER_P_H
+
+#include "qwifimanager.h"
+
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+class QWifiManagerPrivate
+{
+ Q_DECLARE_PUBLIC(QWifiManager)
+public:
+ QWifiManagerPrivate(QWifiManager *manager);
+ virtual ~QWifiManagerPrivate();
+
+ // methods
+ QString getConnectedNetwork();
+ void setCurrentSSID();
+ void emitCurrentSSIDChanged();
+ void handleConnected();
+ void handleDisconneced();
+
+ void updateNetworkState(QWifiManager::NetworkState networkState);
+ void updateBackendState(QWifiManager::BackendState backendState);
+ void updateWifiState();
+
+ QString call(const QString &command);
+ bool checkedCall(const QString &command);
+ void updateLastError(const QString &error);
+
+ // member variables
+ QWifiManager *const q_ptr;
+ QWifiController *m_wifiController;
+ QWifiNetworkListModel *m_networkListModel;
+
+ int m_scanTimer;
+ bool m_scanning;
+ QByteArray m_interface;
+ QWifiManager::BackendState m_backendState;
+ QWifiManager::NetworkState m_networkState;
+ bool m_setCurrentSSID;
+ QString m_currentSSID;
+ QString m_previousSSID;
+ QString m_lastError;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFIMANAGER_P_H
diff --git a/src/wifi/qwifinetwork.cpp b/src/wifi/qwifinetwork.cpp
new file mode 100644
index 0000000..937cbfc
--- /dev/null
+++ b/src/wifi/qwifinetwork.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwifinetwork_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype WifiNetwork
+ \inqmlmodule QtWifi
+ \ingroup wifi-qmltypes
+ \brief Represents a single WiFi network access point.
+
+ WifiNetwork provides a various information about WiFi network, like network
+ name, siganl strength and supported security protocols. Instances of WifiNetwork cannot
+ be created directly from the QML system, see WifiManager::networks.
+*/
+
+/*!
+ \qmlproperty string WifiNetwork::bssid
+ \readonly
+
+ This property holds basic service set identification of a network, used to uniquely
+ identify BSS.
+
+*/
+
+/*!
+ \qmlproperty string WifiNetwork::ssid
+ \readonly
+
+ This property holds a network name. The SSID is the informal (human) name of BSS.
+*/
+
+/*!
+ \qmlproperty int WifiNetwork::signalStrength
+ \readonly
+
+ This property holds the current strength of a WiFi signal, measured in dBm. New readings are
+ taken every 5 seconds.
+
+ \sa signalStrengthChanged
+*/
+
+/*!
+ \qmlproperty bool WifiNetwork::supportsWPA
+ \readonly
+
+ This property holds whether network access point supports WPA security protocol.
+*/
+
+/*!
+ \qmlproperty bool WifiNetwork::supportsWPA2
+ \readonly
+
+ This property holds whether network access point supports WPA2 security protocol.
+*/
+
+/*!
+ \qmlproperty bool WifiNetwork::supportsWEP
+ \readonly
+
+ This property holds whether network access point supports WEP security protocol.
+*/
+
+/*!
+ \qmlproperty bool WifiNetwork::supportsWPS
+ \readonly
+
+ This property holds whether network access point supports WPS security protocol.
+*/
+
+/*!
+ \qmlsignal void WifiNetwork::signalStrengthChanged(int strength)
+
+ This signal is emitted whenever signal strength has changed comparing the the
+ previous reading, the new signal's strength is \a strength.
+
+*/
+
+QWifiNetwork::QWifiNetwork(QObject *parent)
+ : QObject(parent)
+ , m_isOutOfRange(false)
+{
+}
+
+QWifiNetwork::~QWifiNetwork()
+{
+}
+
+void QWifiNetwork::setSsid(const QString &ssid)
+{
+ m_ssid = ssid;
+}
+
+void QWifiNetwork::setSignalStrength(int strength)
+{
+ m_signalStrength = strength;
+}
+
+void QWifiNetwork::setOutOfRange(bool outOfRange)
+{
+ m_isOutOfRange = outOfRange;
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwifinetwork_p.h b/src/wifi/qwifinetwork_p.h
new file mode 100644
index 0000000..db65e2d
--- /dev/null
+++ b/src/wifi/qwifinetwork_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFINETWORK_H
+#define QWIFINETWORK_H
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+class QWifiNetwork : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QWifiNetwork(QObject *parent = 0);
+ virtual ~QWifiNetwork();
+
+ void setBssid(const QString &bssid) { m_bssid = bssid; }
+ QString bssid() const { return m_bssid; }
+ void setSsid(const QString &ssid);
+ QString ssid() const { return m_ssid; }
+
+ void setSignalStrength(int strength);
+ int signalStrength() const { return m_signalStrength; }
+
+ void setOutOfRange(bool isOutOfRange);
+ // this could be exposed in QWifiManager::Roles,
+ // if there is a use case for it, this would require keeping
+ // the out of range networks in data model, currently they are
+ // removed from the model.
+ bool isOutOfRange() const { return m_isOutOfRange; }
+
+ void setFlags(const QString &flags) { m_flags = flags; }
+ QString flags() const { return m_flags; }
+ bool supportsWPA2() const { return m_flags.contains(QStringLiteral("WPA2")); }
+ bool supportsWPA() const { return m_flags.contains(QStringLiteral("WPA")); }
+ bool supportsWEP() const { return m_flags.contains(QStringLiteral("WEP")); }
+ bool supportsWPS() const { return m_flags.contains(QStringLiteral("WPS")); }
+
+private:
+ QString m_bssid;
+ QString m_ssid;
+ int m_signalStrength;
+ QString m_flags;
+ bool m_isOutOfRange;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFINETWORK_H
diff --git a/src/wifi/qwifinetworklistmodel.cpp b/src/wifi/qwifinetworklistmodel.cpp
new file mode 100644
index 0000000..7a5de2a
--- /dev/null
+++ b/src/wifi/qwifinetworklistmodel.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#include "qwifinetworklistmodel_p.h"
+#include "qwifinetwork_p.h"
+
+#include "qwifimanager.h"
+
+#include <QtCore/QSet>
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+QT_BEGIN_NAMESPACE
+
+QWifiNetworkListModel::QWifiNetworkListModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+QWifiNetworkListModel::~QWifiNetworkListModel()
+{
+ qDeleteAll(m_networks);
+ qDeleteAll(m_outOfRangeNetworks);
+ m_networks.clear();
+ m_outOfRangeNetworks.clear();
+}
+
+QHash<int, QByteArray> QWifiNetworkListModel::roleNames() const
+{
+ QHash<int, QByteArray> names;
+ names.insert(QWifiManager::SSIDRole, "ssid");
+ names.insert(QWifiManager::BSSIDRole, "bssid");
+ names.insert(QWifiManager::SignalRole, "signalStrength");
+ names.insert(QWifiManager::WPARole, "supportsWPA");
+ names.insert(QWifiManager::WPA2Role, "supportsWPA2");
+ names.insert(QWifiManager::WEPRole, "supportsWEP");
+ names.insert(QWifiManager::WPSRole, "supportsWPS");
+ return names;
+}
+
+QVariant QWifiNetworkListModel::data(const QModelIndex &index, int role) const
+{
+ QWifiNetwork *n = m_networks.at(index.row());
+
+ switch (role) {
+ case QWifiManager::SSIDRole:
+ return n->ssid();
+ break;
+ case QWifiManager::BSSIDRole:
+ return n->bssid();
+ break;
+ case QWifiManager::SignalRole:
+ return n->signalStrength();
+ break;
+ case QWifiManager::WPARole:
+ return n->supportsWPA();
+ break;
+ case QWifiManager::WPA2Role:
+ return n->supportsWPA2();
+ break;
+ case QWifiManager::WEPRole:
+ return n->supportsWEP();
+ break;
+ case QWifiManager::WPSRole:
+ return n->supportsWPS();
+ break;
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+QWifiNetwork *QWifiNetworkListModel::networkForSSID(const QString &ssid)
+{
+ int pos = 0; // unused
+ return networkForSSID(ssid, &pos);
+}
+
+QWifiNetwork *QWifiNetworkListModel::networkForSSID(const QString &ssid, int *pos)
+{
+ for (int i = 0; i < m_networks.size(); ++i) {
+ if (m_networks.at(i)->ssid() == ssid) {
+ if (pos)
+ *pos = i;
+ return m_networks.at(i);
+ }
+ }
+ return 0;
+}
+
+QWifiNetwork *QWifiNetworkListModel::outOfRangeListContains(const QString &ssid)
+{
+ for (int i = 0; i < m_outOfRangeNetworks.length(); ++i)
+ if (m_outOfRangeNetworks.at(i)->ssid() == ssid)
+ return m_outOfRangeNetworks.takeAt(i);
+ return 0;
+}
+
+void QWifiNetworkListModel::parseScanResults(const QString &results)
+{
+ QStringList lines = results.split('\n');
+ QSet<QString> sensibleNetworks;
+
+ for (int i = 1; i < lines.size(); ++i) {
+ QStringList info = lines.at(i).split('\t');
+ if (info.size() < 5 || info.at(4).isEmpty() || info.at(0).isEmpty())
+ continue;
+ int pos = 0;
+
+ QString ssid = info.at(4);
+ sensibleNetworks.insert(ssid);
+ QWifiNetwork *knownNetwork = networkForSSID(ssid, &pos);
+ if (!knownNetwork)
+ knownNetwork = outOfRangeListContains(ssid);
+ // signal strength is in dBm. Deprecated, but still widely used "wext"
+ // wifi driver reports positive values for signal strength, we workaround that.
+ int signalStrength = qAbs(info.at(2).trimmed().toInt()) * -1;
+ if (!knownNetwork) {
+ QWifiNetwork *network = new QWifiNetwork();
+ network->setOutOfRange(false);
+ network->setBssid(info.at(0));
+ network->setFlags(info.at(3));
+ network->setSignalStrength(signalStrength);
+ network->setSsid(ssid);
+ beginInsertRows(QModelIndex(), m_networks.size(), m_networks.size());
+ m_networks << network;
+ endInsertRows();
+ } else {
+ if (knownNetwork->isOutOfRange()) {
+ // known network has come back into a range
+ knownNetwork->setOutOfRange(false);
+ beginInsertRows(QModelIndex(), m_networks.size(), m_networks.size());
+ m_networks << knownNetwork;
+ endInsertRows();
+ pos = m_networks.length() - 1;
+ }
+ // ssids are the same, compare bssids..
+ if (knownNetwork->bssid() == info.at(0)) {
+ // same access point, simply update the signal strength
+ knownNetwork->setSignalStrength(signalStrength);
+ knownNetwork->setOutOfRange(false);
+ dataChanged(createIndex(pos, 0), createIndex(pos, 0));
+ } else if (knownNetwork->signalStrength() < signalStrength) {
+ // replace with a stronger access point within the same network
+ m_networks.at(pos)->setOutOfRange(false);
+ m_networks.at(pos)->setBssid(info.at(0));
+ m_networks.at(pos)->setFlags(info.at(3));
+ m_networks.at(pos)->setSignalStrength(signalStrength);
+ m_networks.at(pos)->setSsid(ssid);
+ dataChanged(createIndex(pos, 0), createIndex(pos, 0));
+ }
+ }
+ }
+ // remove out-of-range networks from the data model
+ for (int i = 0; i < m_networks.size();) {
+ if (!sensibleNetworks.contains(m_networks.at(i)->ssid())) {
+ beginRemoveRows(QModelIndex(), i, i);
+ QWifiNetwork *n = m_networks.takeAt(i);
+ n->setOutOfRange(true);
+ m_outOfRangeNetworks.append(n);
+ endRemoveRows();
+ } else {
+ ++i;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/wifi/qwifinetworklistmodel_p.h b/src/wifi/qwifinetworklistmodel_p.h
new file mode 100644
index 0000000..25a2b31
--- /dev/null
+++ b/src/wifi/qwifinetworklistmodel_p.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use the contact form at
+** http://www.qt.io
+**
+** 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://www.qt.io
+**
+****************************************************************************/
+#ifndef QWIFINETWORKLISTMODEL_H
+#define QWIFINETWORKLISTMODEL_H
+
+#include <QtCore/QAbstractListModel>
+#include <QtCore/QVariant>
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI)
+
+class QWifiNetwork;
+
+class QWifiNetworkListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit QWifiNetworkListModel(QObject *parent = 0);
+ virtual ~QWifiNetworkListModel();
+
+ int rowCount(const QModelIndex &) const { return m_networks.size(); }
+ QVariant data(const QModelIndex &index, int role) const;
+ QHash<int,QByteArray> roleNames() const;
+
+ void parseScanResults(const QString &data);
+ QWifiNetwork *networkForSSID(const QString &ssid);
+ QWifiNetwork *networkForSSID(const QString &ssid, int *pos);
+ QWifiNetwork *outOfRangeListContains(const QString &ssid);
+
+private:
+ QList<QWifiNetwork *> m_networks;
+ QList<QWifiNetwork *> m_outOfRangeNetworks;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWIFINETWORKLISTMODEL_H
diff --git a/src/wifi/wifi.pro b/src/wifi/wifi.pro
new file mode 100644
index 0000000..e558081
--- /dev/null
+++ b/src/wifi/wifi.pro
@@ -0,0 +1,43 @@
+load(qt_build_config)
+
+TARGET = B2QtWifi
+VERSION = 1.0
+CONFIG += dll warn_on
+
+QT += core network
+
+MODULE = b2qtwifi
+load(qt_module)
+
+HEADERS += \
+ $$PWD/qwifimanager.h \
+ $$PWD/qwifimanager_p.h \
+ $$PWD/qwifinetwork_p.h \
+ $$PWD/qwifinetworklistmodel_p.h \
+ $$PWD/qwificontroller_p.h \
+ $$PWD/qwifidevice.h \
+ $$PWD/qwificonfiguration.h
+
+SOURCES += \
+ $$PWD/qwifimanager.cpp \
+ $$PWD/qwifinetwork.cpp \
+ $$PWD/qwifinetworklistmodel.cpp \
+ $$PWD/qwificontroller.cpp \
+ $$PWD/qwifidevice.cpp \
+ $$PWD/qwificonfiguration.cpp
+
+android: {
+ LIBS += -lhardware_legacy -lcutils
+} else {
+ DEFINES += \
+ CONFIG_CTRL_IFACE \
+ CONFIG_CTRL_IFACE_UNIX
+
+ HEADERS += \
+ $$PWD/qwifielinux_p.h
+
+ SOURCES += \
+ $$PWD/qwifielinux.cpp \
+ $$[QT_SYSROOT]/usr/include/wpa-supplicant/wpa_ctrl.c \
+ $$[QT_SYSROOT]/usr/include/wpa-supplicant/os_unix.c
+}