diff options
Diffstat (limited to 'src')
26 files changed, 2020 insertions, 1235 deletions
diff --git a/src/doc/src/qtee-changelog.qdoc b/src/doc/src/qtee-changelog.qdoc index 3c8ab3c..5f18e4c 100644 --- a/src/doc/src/qtee-changelog.qdoc +++ b/src/doc/src/qtee-changelog.qdoc @@ -179,7 +179,7 @@ \li Update new content to device without erasing it first on \B2QL \li All images now contain generally used CA certificates \li Toolchains updated to support Qt WebEngine - \li Documentation was added for QML types provided by the \l {WiFi Module} + \li Documentation was added for QML types provided by the \l {QtWifi Module} \li Emulator: Debug logging functionality was added \li Various documentation improvements \li \SDK installer error handling was improved diff --git a/src/doc/src/qtee-qml-reference.qdoc b/src/doc/src/qtee-qml-reference.qdoc index 7ee86a3..0107e97 100644 --- a/src/doc/src/qtee-qml-reference.qdoc +++ b/src/doc/src/qtee-qml-reference.qdoc @@ -31,7 +31,7 @@ \annotatedlist utils-qmltypes - \section1 WiFi Module + \section1 B2Qt Wifi Module \annotatedlist wifi-qmltypes */ diff --git a/src/imports/wifi/pluginmain.cpp b/src/imports/wifi/pluginmain.cpp index bc72906..f706023 100644 --- a/src/imports/wifi/pluginmain.cpp +++ b/src/imports/wifi/pluginmain.cpp @@ -16,15 +16,22 @@ ** the contact form at http://www.qt.io ** ****************************************************************************/ -#include "qwifimanager.h" -#include "qwifiinterface.h" +#include <B2QtWifi/QWifiManager> +#include <B2QtWifi/QWifiDevice> +#include <B2QtWifi/QWifiConfiguration> -#include <QtQml/QQmlExtensionPlugin> -#include <QtQml/qqml.h> +#include <QtQml> -static QObject *global_object_wifi(QQmlEngine *, QJSEngine *) +QT_BEGIN_NAMESPACE + +static QObject *globalWifiDevice(QQmlEngine *, QJSEngine *) { - return new QWifiInterface; + return new QWifiDevice; +} + +static QObject *globalWifiManager(QQmlEngine *, QJSEngine *) +{ + return QWifiManager::instance(); } class QWifiPlugin : public QQmlExtensionPlugin @@ -35,12 +42,15 @@ class QWifiPlugin : public QQmlExtensionPlugin public: virtual void registerTypes(const char *uri) { - Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.wifi")); + Q_ASSERT(QLatin1String(uri) == QLatin1String("B2Qt.Wifi")); - qmlRegisterType<QWifiManager>(uri, 0, 1, "WifiManager"); - qmlRegisterType<QWifiNetworkListModel>(); - qmlRegisterSingletonType<QWifiInterface>(uri, 0, 1, "Interface", global_object_wifi); + qmlRegisterType<QAbstractListModel>(); + qmlRegisterSingletonType<QWifiManager>(uri, 1, 0, "WifiManager", globalWifiManager); + qmlRegisterSingletonType<QWifiDevice>(uri, 1, 0, "WifiDevice", globalWifiDevice); + qmlRegisterType<QWifiConfiguration>(uri, 1, 0, "WifiConfiguration"); } }; +QT_END_NAMESPACE + #include "pluginmain.moc" diff --git a/src/imports/wifi/qmldir b/src/imports/wifi/qmldir index d4f65d9..7e99b56 100644 --- a/src/imports/wifi/qmldir +++ b/src/imports/wifi/qmldir @@ -1,3 +1,3 @@ -module Qt.labs.wifi -plugin qwifimodule +module B2Qt.Wifi +plugin b2qtwifiplugin typeinfo plugins.qmltypes diff --git a/src/imports/wifi/qwifiinterface.cpp b/src/imports/wifi/qwifiinterface.cpp deleted file mode 100644 index acd7716..0000000 --- a/src/imports/wifi/qwifiinterface.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** 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 "qwifiinterface.h" - -/*! - \qmltype Interface - \inqmlmodule Qt.labs.wifi - \ingroup wifi-qmltypes - \brief The Interface element provides the module API. - - This element cannot be directly created. It can only be accessed via a namespace import. - - \code - import Qt.labs.wifi 0.1 - import Qt.labs.wifi 0.1 as Wifi - - Component.onCompleted: { - if (Wifi.Interface.wifiSupported()) { - var component = Qt.createComponent("WifiMenu.qml") - } else { - print("WiFi functionality not available on this device.") - } - } - \endcode -*/ - -/*! - \qmlmethod bool Interface::wifiSupported() - - Returns true if the device is WiFi capable (provides a WiFi driver), otherwise returns false. -*/ - -bool QWifiInterface::wifiSupported() const -{ -#ifdef Q_OS_ANDROID - const char *fwpath = 0; - // reload wifi firmware - fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_STA); - if (!fwpath) { - qWarning() << "QWifiInterface: failed to get firmware path"; - return false; - } - if (wifi_change_fw_path((const char *)fwpath)) { - qWarning() << "QWifiInterface: failed to change firmware path"; - return false; - } -#endif - const bool hasInterface = QDir().exists(QStringLiteral("/sys/class/net/wlan0")); - if (!hasInterface) - qWarning() << "QWifiInterface: could not find wifi interface in /sys/class/net/"; -#ifdef Q_OS_ANDROID - if (hasInterface && wifi_load_driver() == 0 && wifi_start_supplicant(0) == 0) { - return true; - } else { - qWarning() << "QWifiInterface: wifi driver is not available"; - return false; - } -#else - return hasInterface; -#endif -} diff --git a/src/imports/wifi/qwifimanager.cpp b/src/imports/wifi/qwifimanager.cpp deleted file mode 100644 index 2574cd2..0000000 --- a/src/imports/wifi/qwifimanager.cpp +++ /dev/null @@ -1,818 +0,0 @@ -/**************************************************************************** -** -** 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 "qwifimanager.h" -#include "qwifiinterface.h" - -#include <QtCore> -#ifdef Q_OS_ANDROID -#include <hardware_legacy/wifi.h> -#include <cutils/sockets.h> -#include <unistd.h> -#else -#include <qwifi_elinux.h> -#endif - -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); -const QEvent::Type WIFI_HANDSHAKE_FAILED = (QEvent::Type) (QEvent::User + 2003); - -#ifdef Q_OS_ANDROID -/* - * Work around API differences between Android versions - */ - -static 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 -} - -static 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 -} - -static 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 -} - -static 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 -} - -static 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 -} - -static 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. - */ -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) -{ - 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 - -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 QWifiManagerEventThread : public QThread -{ -public: - QWifiManagerEventThread(QWifiManager *manager, const QByteArray &interface) - : m_manager(manager) - , m_if(interface) - { - - } - - void run() { - if (QT_WIFI_DEBUG) qDebug("WiFi event thread is running"); - QWifiManagerEvent *event = 0; - char buffer[2048]; - while (1) { - int size = q_wifi_wait_for_event(m_if.constData(), buffer, sizeof(buffer) - 1); - if (size > 0) { - buffer[size] = 0; - event = 0; - if (strstr(buffer, "SCAN-RESULTS")) { - if (m_manager->exitingEventThread()) { - if (QT_WIFI_DEBUG) qDebug() << "Exiting WiFi event thread"; - return; - } - event = new QWifiManagerEvent(WIFI_SCAN_RESULTS); - } else if (strstr(buffer, "CONNECTED")) { - event = new QWifiManagerEvent(WIFI_CONNECTED); - } else if (strstr(buffer, "Handshake failed")) { - event = new QWifiManagerEvent(WIFI_HANDSHAKE_FAILED); - } else if (strstr(buffer, "TERMINATING")) { - // stop monitoring for events when supplicant is terminating - return; - } - if (event) - QCoreApplication::postEvent(m_manager, event); - } - } - } - - QWifiManager *m_manager; - QByteArray m_if; -}; - -/*! - \qmlmodule Qt.labs.wifi 0.1 - \title WiFi Module - \ingroup qtee-qmlmodules - \brief Controlling wireless network interfaces. - - Provides QML types for controlling and accessing information about wireless network interfaces. - - The import command for adding these QML types is: - - \code - import Qt.labs.wifi 0.1 - \endcode - - If the module is imported into a namespace, some additional methods become available through the - \l Interface element. - - \code - import Qt.labs.wifi 0.1 as Wifi - \endcode - - \section1 QML Types -*/ - -/*! - - \qmltype WifiManager - \inqmlmodule Qt.labs.wifi - \ingroup wifi-qmltypes - \brief Provides information about the WiFi backend and available networks. - - This element is the main interface to the WiFi functionality. - - */ - -/*! - \qmlproperty enumeration WifiManager::networkState - - 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 -*/ - -/*! - \qmlproperty bool WifiManager::backendReady - - This property holds whether or not the backend has been successfully initialized. - - \code - WifiManager { - id: wifiManager - scanning: backendReady - } - - Button { - id: wifiOnOffButton - text: (wifiManager.backendReady) ? "Switch Off" : "Switch On" - onClicked: { - if (wifiManager.backendReady) { - wifiManager.stop() - } else { - wifiManager.start() - } - } - } - \endcode -*/ - -/*! - \qmlproperty bool WifiManager::scanning - - This property holds whether or not the backend is scanning for WiFi networks. To - preserve battery energy, stop scanning for networks once you are done with configuring a network. - - Before starting to scan for networks, you need to initialize the WiFi backend. - - \sa start -*/ - -/*! - \qmlproperty string WifiManager::connectedSSID - - This property holds the network name. -*/ - -/*! - \qmlproperty WifiNetworkListModel WifiManager::networks - - 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. Instances of WifiNetwork cannot be created directly from the QML system. - - \code - WifiManager { - id: wifiManager - scanning: backendReady - Component.onCompleted: start() - } - - Component { - id: listDelegate - Rectangle { - id: delegateBackground - height: 60 - width: parent.width - color: "#5C5C5C" - border.color: "black" - border.width: 1 - - Text { - id: ssidLabel - anchors.top: parent.top - anchors.left: parent.left - anchors.margins: 10 - font.pixelSize: 20 - font.bold: true - color: "#E6E6E6" - text: network.ssid - } - - Rectangle { - width: Math.max(100 + network.signalStrength, 0) / 100 * parent.width; - height: 20 - radius: 10 - antialiasing: true - anchors.margins: 20 - anchors.right: parent.right - anchors.top: parent.top - color: "#BF8888" - border.color: "#212126" - } - } - } - - - ListView { - id: networkView - anchors.fill: parent - model: wifiManager.networks - delegate: listDelegate - } - \endcode - -*/ - -/*! - \qmlmethod void WifiManager::start() - - Start an initialization of the WiFi backend. - - \sa stop - */ - -/*! - \qmlmethod void WifiManager::stop() - - Stop the WiFi backend and shut down all network functionality. - - \sa start - */ - -/*! - \qmlmethod void WifiManager::connect(WifiNetwork network, const string passphrase) - - Connect to network \a network and use passphrase \a passphrase for authentication. - - \sa disconnect, networkState - */ - -/*! - \qmlmethod void WifiManager::disconnect() - - Disconnect from currently connected network connection. - - \sa connect, networkState - */ - -/*! - \qmlsignal void WifiManager::scanningChanged(bool scanning) - - This signal is emitted when device starts or stops to scan for available wifi networks. - - \sa scanning - -*/ - -/*! - \qmlsignal void WifiManager::networkStateChanged(WifiNetwork network) - - This signal is emitted whenever changes in a network state occur. Network \a network is the - the currently active network connection. - - \sa networkState -*/ - -/*! - \qmlsignal void WifiManager::backendReadyChanged() - - This signal is emitted when backend has been successfully initialized or shut down. - - \sa start, stop -*/ - -/*! - \qmlsignal void WifiManager::connectedSSIDChanged(string ssid) - - This signal is emitted when the device has connected to or disconnected from a network. - \a ssid contains the name of the connected network, or an empty string if the network was disconnected. - - \sa connect, disconnect -*/ - -QWifiManager::QWifiManager() - : m_networkListModel(this) - , m_eventThread(0) - , m_scanTimer(0) - , m_scanning(false) -#ifdef Q_OS_ANDROID - , m_daemonClientSocket(0) -#endif - , m_exitingEventThread(false) - , m_startingUp(true) - , m_network(0) -{ - if (!QWifiInterface().wifiSupported()) - // give a warning about API misuse - qWarning() << "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 WifiManager!"; -#ifdef Q_OS_ANDROID - char interface[PROPERTY_VALUE_MAX]; - property_get(WIFI_INTERFACE, interface, NULL); - m_interface = interface; -#else - m_interface = "wlan0"; // use envvar for the interface name? - m_dhcpRunner = new ProcessRunner(m_interface); - QObject::connect(m_dhcpRunner, &ProcessRunner::processFinished, this, &QWifiManager::handleDhcpFinished); -#endif - qDebug("QWifiManager: using wifi interface: %s", m_interface.constData()); - m_eventThread = new QWifiManagerEventThread(this, m_interface); -#ifdef Q_OS_ANDROID - 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 backendStatus[PROPERTY_VALUE_MAX]; - if (property_get(QT_WIFI_BACKEND, backendStatus, NULL)) { - if (strcmp(backendStatus, "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(backendStatus, "stopped") == 0) { - // same here, cleans up the state - disconnectFromBackend(); - } - } else { -#endif - m_backendReady = false; - emit backendReadyChanged(); -#ifdef Q_OS_ANDROID - } -#endif -} - -QWifiManager::~QWifiManager() -{ - exitEventThread(); - delete m_eventThread; -#ifdef Q_OS_ANDROID - delete m_daemonClientSocket; -#endif -} - -#ifdef Q_OS_ANDROID -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") { - updateNetworkState(Connected); - emit connectedSSIDChanged(m_connectedSSID); - // Store settings of a working wifi connection - call("SAVE_CONFIG"); - } else if (receivedMessage == "failed") { - updateNetworkState(DhcpRequestFailed); - } 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(); -} -#endif - -void QWifiManager::handleDhcpFinished() -{ - // ### TODO - could be that dhcp request fails, how to determine? - updateNetworkState(Connected); - call("SAVE_CONFIG"); -} - -void QWifiManager::start() -{ - if (QT_WIFI_DEBUG) qDebug("QWifiManager: connecting to the backend"); - if (m_backendReady) - return; - connectToBackend(); -} - -void QWifiManager::stop() -{ - if (QT_WIFI_DEBUG) qDebug("QWifiManager: shutting down"); - if (!m_backendReady) - return; - disconnectFromBackend(); -} - -void QWifiManager::connectToBackend() -{ - // ### TODO: maybe it makes sense to move this functions in non-gui thread -#ifdef Q_OS_ANDROID - if (!(is_wifi_driver_loaded() || wifi_load_driver() == 0)) { - qWarning("QWifiManager: failed to load a driver"); - return; - } -#else - QProcess::execute(QStringLiteral("ifup"), QStringList() << m_interface.constData()); -#endif - if (q_wifi_start_supplicant() != 0) { - qWarning("QWifiManager: failed to start a supplicant"); - return; - } -#ifdef Q_OS_ANDROID - if (wait_for_property(SUPPLICANT_SVC, "running", 5) < 0) { - qWarning("QWifiManager: Timed out waiting for supplicant to start"); - return; - } -#endif - if (q_wifi_connect_to_supplicant(m_interface.constData()) == 0) { - m_backendReady = true; - emit backendReadyChanged(); -#ifdef Q_OS_ANDROID - property_set(QT_WIFI_BACKEND, "running"); -#endif - } else { - qWarning("QWifiManager: failed to connect to a supplicant"); - return; - } - if (QT_WIFI_DEBUG) qDebug("QWifiManager: started successfully"); - m_exitingEventThread = false; - m_eventThread->start(); - call("SCAN"); -} - -void QWifiManager::disconnectFromBackend() -{ - exitEventThread(); - if (q_wifi_stop_supplicant() < 0) - qWarning("QWifiManager: failed to stop supplicant"); - q_wifi_close_supplicant_connection(m_interface.constData()); - setScanning(false); -#ifdef Q_OS_ANDROID - property_set(QT_WIFI_BACKEND, "stopped"); -#else - QProcess::execute(QStringLiteral("ifdown"), QStringList() << m_interface.constData()); -#endif - m_backendReady = false; - emit backendReadyChanged(); -} - -void QWifiManager::exitEventThread() -{ - if (m_eventThread->isRunning()) { - m_exitingEventThread = true; - call("SCAN"); - m_eventThread->wait(); - } -} - -void QWifiManager::setScanning(bool scanning) -{ - if (!m_backendReady || m_scanning == scanning) - return; - - m_scanning = scanning; - emit scanningChanged(m_scanning); - - if (m_scanning) { - if (QT_WIFI_DEBUG) qDebug("QWifiManager: scanning"); - call("SCAN"); - m_scanTimer = startTimer(5000); // ### todo - this could be a qml property - } else { - if (QT_WIFI_DEBUG) qDebug("QWifiManager: stop scanning"); - killTimer(m_scanTimer); - } -} - -QByteArray QWifiManager::call(const char *command) const -{ - char data[2048]; - size_t len = sizeof(data) - 1; // -1: room to add a 0-terminator - QByteArray cmd; -#ifdef Q_OS_ANDROID -#if !(Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 4) - cmd.append("IFNAME=").append(m_interface).append(" "); -#endif -#endif - cmd.append(command); - if (q_wifi_command(m_interface.constData(), cmd.constData(), data, &len) < 0) { - qWarning("QWifiManager: call failed: %s", cmd.constData()); - return QByteArray(); - } - if (len < sizeof(data)) - data[len] = 0; - QByteArray result = QByteArray::fromRawData(data, len); - if (QT_WIFI_DEBUG) qDebug("QWifiManager::call: %s ==>\n%s", cmd.constData(), result.constData()); - return result; -} - -bool QWifiManager::checkedCall(const char *command) const -{ - return call(command).trimmed().toUpper() == "OK"; -} - -void QWifiManager::updateNetworkState(NetworkState state) -{ - m_state = state; - if (m_network) - emit networkStateChanged(m_network); -} - -bool QWifiManager::event(QEvent *e) -{ - switch ((int) e->type()) { - case WIFI_SCAN_RESULTS: - m_networkListModel.parseScanResults(call("SCAN_RESULTS")); - if (m_startingUp) - handleConnected(); - return true; - case WIFI_CONNECTED: - handleConnected(); - break; - case WIFI_HANDSHAKE_FAILED: - updateNetworkState(HandshakeFailed); - break; - case QEvent::Timer: { - int tid = static_cast<QTimerEvent *>(e)->timerId(); - if (tid == m_scanTimer) { - call("SCAN"); - return true; - } - break; - } - } - - return QObject::event(e); -} - -void QWifiManager::connect(QWifiNetwork *network, const QString &passphrase) -{ - m_network = network; - if (network->ssid() == m_connectedSSID) { - if (QT_WIFI_DEBUG) - qDebug("QWifiManager::connect(), already connected to %s", network->ssid().constData()); - return; - } - updateNetworkState(Authenticating); - call("DISABLE_NETWORK all"); - if (!m_connectedSSID.isEmpty()) { - m_connectedSSID.clear(); - emit connectedSSIDChanged(m_connectedSSID); - } - - bool networkKnown = false; - QByteArray id; - QByteArray listResult = call("LIST_NETWORKS"); - QList<QByteArray> lines = listResult.split('\n'); - foreach (const QByteArray &line, lines) { - if (line.contains(network->ssid())) { - id = line.split('\t').at(0); - networkKnown = true; - break; - } - } - - if (!networkKnown) { - bool ok; - id = call("ADD_NETWORK").trimmed(); - id.toInt(&ok); - if (!ok) { - qWarning("QWifiManager::connect(), failed to add network"); - return; - } - } - QByteArray setNetworkCommand = QByteArray("SET_NETWORK ") + id; - - bool ok = true; - if (!networkKnown) - ok = ok && checkedCall(setNetworkCommand + QByteArray(" ssid ") + '"' + network->ssid() + '"'); - - QByteArray key_mgmt; - if (network->supportsWPA() || network->supportsWPA2()) { - ok = ok && checkedCall(setNetworkCommand + QByteArray(" psk ") + '"' + passphrase.toLatin1() + '"'); - 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")); - key_mgmt = "NONE"; - } else if (!network->supportsWPS() && passphrase.length() == 0) { - // open network - key_mgmt = "NONE"; - } - ok = ok && checkedCall(setNetworkCommand + QByteArray(" key_mgmt ") + key_mgmt); - - if (!ok) { - if (!networkKnown) - call("REMOVE_NETWORK " + id); - qWarning("QWifiManager::connect(), failed to set properties on network '%s'", id.constData()); - return; - } - - call(QByteArray("SELECT_NETWORK ") + id); - call("RECONNECT"); -} - -void QWifiManager::disconnect() -{ - call("DISCONNECT"); -#ifdef Q_OS_ANDROID - QByteArray req = m_interface; - sendDhcpRequest(req.append(" disconnect")); -#endif - m_connectedSSID.clear(); - updateNetworkState(Disconnected); - emit connectedSSIDChanged(m_connectedSSID); -} - -void ProcessRunner::run() -{ - // kill existing udhcpc instance - QString filePath = QString("/var/run/udhcpc.").append(m_ifc).append(".pid"); - QFile pidFile(filePath); - if (pidFile.open(QIODevice::ReadOnly)) { - QByteArray pid = pidFile.readAll(); - pidFile.close(); - QProcess::execute(QStringLiteral("kill"), QStringList() << pid.trimmed().constData()); - } else { - qWarning() << "QWifiManager - Failed to read" << filePath; - } - QStringList args; - args << QStringLiteral("-R") << QStringLiteral("-n") << QStringLiteral("-p") - << filePath << QStringLiteral("-i") << m_ifc; - // start DHCP client - QProcess::execute(QStringLiteral("udhcpc"), args); - emit processFinished(); -} - -void QWifiManager::handleConnected() -{ - QList<QByteArray> lists = call("LIST_NETWORKS").split('\n'); - QByteArray connectedNetwork; - for (int i = 1; i < lists.size(); ++i) { - if (lists.at(i).toUpper().contains("[CURRENT]")) { - connectedNetwork = lists.at(i); - break; - } - } - - if (connectedNetwork.isEmpty()) { - if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: not connected to a network..."); - return; - } - - if (QT_WIFI_DEBUG) qDebug("QWifiManager::handleConnected: current is %s", connectedNetwork.constData()); - - QList<QByteArray> info = connectedNetwork.split('\t'); - m_connectedSSID = info.at(1); - - if (m_startingUp) { - m_startingUp = false; - if (!m_network) { - int pos = 0; // unused - m_network = m_networkListModel.networkForSSID(info.at(1), &pos); - } - } - - updateNetworkState(ObtainingIPAddress); -#ifdef Q_OS_ANDROID - QByteArray req = m_interface; - sendDhcpRequest(req.append(" connect")); -#else - m_dhcpRunner->start(); -#endif -} diff --git a/src/imports/wifi/qwifimanager.h b/src/imports/wifi/qwifimanager.h deleted file mode 100644 index 7acc360..0000000 --- a/src/imports/wifi/qwifimanager.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** 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 <QtCore/QObject> -#include <QtCore/QThread> -#include <QtCore/QByteArray> - -#ifdef Q_OS_ANDROID -#include <QtNetwork/QLocalSocket> -#include <cutils/properties.h> -#endif - -#include "qwifinetworklistmodel.h" - -class QWifiManagerEventThread; - -class ProcessRunner : public QThread -{ - Q_OBJECT -public: - ProcessRunner(const QByteArray &ifc) : m_ifc(ifc) {} - void run(); - -signals: - void processFinished(); - -private: - QByteArray m_ifc; -}; - -class QWifiManager : public QObject -{ - Q_OBJECT - Q_ENUMS(NetworkState) - Q_PROPERTY(NetworkState networkState READ networkState NOTIFY networkStateChanged) - Q_PROPERTY(bool backendReady READ isbackendReady NOTIFY backendReadyChanged) - Q_PROPERTY(bool scanning READ scanning WRITE setScanning NOTIFY scanningChanged) - Q_PROPERTY(QString connectedSSID READ connectedSSID NOTIFY connectedSSIDChanged) - Q_PROPERTY(QWifiNetworkListModel *networks READ networks CONSTANT) - -public: - enum NetworkState { - Disconnected, - Authenticating, - HandshakeFailed, - ObtainingIPAddress, - DhcpRequestFailed, - Connected - }; - - QWifiManager(); - ~QWifiManager(); - - QWifiNetworkListModel *networks() { return &m_networkListModel; } - QString connectedSSID() const { return m_connectedSSID; } - bool scanning() const { return m_scanning; } - void setScanning(bool scanning); - NetworkState networkState() const { return m_state; } - bool isbackendReady() const { return m_backendReady; } - bool exitingEventThread() const { return m_exitingEventThread; } - -public slots: - void start(); - void stop(); - void connect(QWifiNetwork *network, const QString &passphrase); - void disconnect(); - -signals: - void scanningChanged(bool scanning); - void networkStateChanged(QWifiNetwork *network); - void backendReadyChanged(); - void connectedSSIDChanged(const QString &ssid); - -protected: - bool event(QEvent *); - void handleConnected(); - void connectToBackend(); - void disconnectFromBackend(); - void exitEventThread(); - - QByteArray call(const char *command) const; - bool checkedCall(const char *command) const; - void updateNetworkState(NetworkState state); - -protected slots: -#if defined(FORCE_MOC) - void sendDhcpRequest(const QByteArray &request); - void connectedToDaemon(); - void handleDhcpReply(); -#endif - void handleDhcpFinished(); - -private: - friend class QWifiManagerEventThread; - friend class ProcessRunner; - - QString m_connectedSSID; - QWifiNetworkListModel m_networkListModel; - QWifiManagerEventThread *m_eventThread; - - int m_scanTimer; - bool m_scanning; - bool m_backendReady; - - QByteArray m_interface; - NetworkState m_state; -#ifdef Q_OS_ANDROID - QLocalSocket *m_daemonClientSocket; -#else - ProcessRunner *m_dhcpRunner; -#endif - QByteArray m_request; - bool m_exitingEventThread; - bool m_startingUp; - QWifiNetwork *m_network; -}; - -#endif // QWIFIMANAGER_H diff --git a/src/imports/wifi/qwifinetwork.h b/src/imports/wifi/qwifinetwork.h deleted file mode 100644 index 5a92e17..0000000 --- a/src/imports/wifi/qwifinetwork.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** 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/QByteArray> -#include <QtCore/QObject> - -class QWifiNetwork : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QByteArray bssid READ bssid CONSTANT) - Q_PROPERTY(QByteArray ssid READ ssid CONSTANT) - Q_PROPERTY(int signalStrength READ signalStrength NOTIFY signalStrengthChanged) - Q_PROPERTY(bool supportsWPA2 READ supportsWPA2 CONSTANT) - Q_PROPERTY(bool supportsWPA READ supportsWPA CONSTANT) - Q_PROPERTY(bool supportsWEP READ supportsWEP CONSTANT) - Q_PROPERTY(bool supportsWPS READ supportsWPS CONSTANT) - -public: - QWifiNetwork(); - - QByteArray bssid() const { return m_bssid; } - void setBssid(const QByteArray &id) { m_bssid = id; } - - QByteArray ssid() const { return m_ssid; } - void setSsid(const QByteArray &id) { m_ssid = id; } - - int signalStrength() const { return m_signalStrength; } - void setSignalStrength(int strength); - - void setOutOfRange(bool outOfRange); - bool outOfRange() { return m_outOfRange; } - - QByteArray flags() const { return m_flags; } - void setFlags(const QByteArray &f) { m_flags = f; } - bool supportsWPA2() const { return m_flags.contains("WPA2"); } - bool supportsWPA() const { return m_flags.contains("WPA"); } - bool supportsWEP() const { return m_flags.contains("WEP"); } - bool supportsWPS() const { return m_flags.contains("WPS"); } - -signals: - void signalStrengthChanged(int strength); - -private: - QByteArray m_bssid; - QByteArray m_ssid; - int m_signalStrength; - - QByteArray m_flags; - bool m_outOfRange; -}; - -#endif // QWIFINETWORK_H diff --git a/src/imports/wifi/wifi.pro b/src/imports/wifi/wifi.pro index dabf8cb..b130587 100644 --- a/src/imports/wifi/wifi.pro +++ b/src/imports/wifi/wifi.pro @@ -1,34 +1,12 @@ CXX_MODULE = qml -QT += qml quick network -TARGET = qwifimodule -TARGETPATH = Qt/labs/wifi -IMPORT_VERSION = 0.1 +TARGET = b2qtwifiplugin +TARGETPATH = B2Qt/Wifi +IMPORT_VERSION = 1.0 -HEADERS += \ - qwifimanager.h \ - qwifinetwork.h \ - qwifinetworklistmodel.h \ - qwifiinterface.h +QT += qml b2qtwifi -SOURCES += \ - pluginmain.cpp \ - qwifimanager.cpp \ - qwifinetwork.cpp \ - qwifinetworklistmodel.cpp \ - qwifiinterface.cpp +SOURCES += pluginmain.cpp -android: { - LIBS += -lhardware_legacy -lcutils - DEFINES += FORCE_MOC -} else { - DEFINES += CONFIG_CTRL_IFACE \ - CONFIG_CTRL_IFACE_UNIX - - HEADERS += qwifi_elinux.h - SOURCES += \ - qwifi_elinux.cpp \ - $$[QT_SYSROOT]/usr/include/wpa-supplicant/wpa_ctrl.c \ - $$[QT_SYSROOT]/usr/include/wpa-supplicant/os_unix.c -} +OTHER_FILES += qmldir load(qml_plugin) diff --git a/src/src.pro b/src/src.pro index e8ecbaf..a0e42dd 100644 --- a/src/src.pro +++ b/src/src.pro @@ -2,6 +2,7 @@ TEMPLATE = subdirs CONFIG += ordered SUBDIRS += \ utils \ + wifi \ imports \ doc \ plugins \ 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/imports/wifi/qwifiinterface.h b/src/wifi/qwifidevice.h index 06abd79..80b4891 100644 --- a/src/imports/wifi/qwifiinterface.h +++ b/src/wifi/qwifidevice.h @@ -16,25 +16,29 @@ ** the contact form at http://www.qt.io ** ****************************************************************************/ -#ifndef QWIFIINTERFACE_H -#define QWIFIINTERFACE_H +#ifndef QWIFIDEVICE_H +#define QWIFIDEVICE_H -#include <QtCore/QDir> -#include <QtCore/QDebug> -#ifdef Q_OS_ANDROID -#include <hardware_legacy/wifi.h> -#include <cutils/properties.h> -#endif +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QLoggingCategory> -class QWifiInterface : public QObject +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI) + +class Q_DECL_EXPORT QWifiDevice : public QObject { Q_OBJECT public: - explicit QWifiInterface(QObject *parent = 0) - : QObject(parent) {} - ~QWifiInterface() {} + explicit QWifiDevice(); + virtual ~QWifiDevice(); - Q_INVOKABLE bool wifiSupported() const; + Q_INVOKABLE static bool wifiSupported(); + static QByteArray wifiInterfaceName(); + static void setWifiInterfaceName(const QByteArray &name); }; -#endif // QWIFIHELPERS_H +QT_END_NAMESPACE + +#endif // QWIFIDEVICE_H diff --git a/src/imports/wifi/qwifi_elinux.cpp b/src/wifi/qwifielinux.cpp index 32e1aa3..0dc3a39 100644 --- a/src/imports/wifi/qwifi_elinux.cpp +++ b/src/wifi/qwifielinux.cpp @@ -16,9 +16,11 @@ ** the contact form at http://www.qt.io ** ****************************************************************************/ -#include "qwifi_elinux.h" +#include "qwifielinux_p.h" +#include "qwifidevice.h" -#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QProcess> #include "wpa-supplicant/wpa_ctrl.h" @@ -26,18 +28,17 @@ #include <unistd.h> #include <sys/socket.h> -static const char SUPP_CONFIG_FILE[] = "/etc/wpa_supplicant.conf"; -static const char IFACE_DIR[] = "/var/run/wpa_supplicant/"; -static const char WIFI[] = "wlan0"; +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]; - -static const char IFNAME[] = "IFNAME="; -#define IFNAMELEN (sizeof(IFNAME) - 1) -static const char WPA_EVENT_IGNORE[] = "CTRL-EVENT-IGNORE "; +QByteArray ctrlInterface; int wifi_connect_on_socket_path(const char *path); int wifi_ctrl_recv(char *reply, size_t *reply_len); @@ -47,30 +48,80 @@ void wifi_close_sockets(); int q_wifi_start_supplicant() { - // NOTE: supplicant started when bringing up the wifi interface in - // QWifiManager::connectToBackend() by: - // QProcess::execute(QStringLiteral("ifup") + // #### 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; + } - /* Clear out any stale socket files that might be left over. */ - //wpa_ctrl_cleanup(); - /* Reset sockets used for exiting from hung state */ + 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() { - // NOTE: supplicant stopped when bringing down the wifi - // interface in QWifiManager::disconnectFromBackend() by: - // QProcess::execute(QStringLiteral("ifdown") + 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) { - Q_UNUSED(ifname); static char path[4096]; - snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, WIFI); + snprintf(path, sizeof(path), "%s/%s", IFACE_DIR, ifname); return wifi_connect_on_socket_path(path); } @@ -79,8 +130,8 @@ 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) { - qWarning("Unable to open connection to supplicant on \"%s\": %s", - path, strerror(errno)); + qCWarning(B2QT_WIFI, "Unable to open connection to supplicant on \"%s\": %s", + path, strerror(errno)); return -1; } monitor_conn = wpa_ctrl_open(path); @@ -130,14 +181,14 @@ int wifi_wait_on_socket(char *buf, size_t buflen) } if (result < 0) { - qWarning("wifi_ctrl_recv failed: %s\n", strerror(errno)); + 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 */ - qWarning("Received EOF on supplicant socket\n"); + qCWarning(B2QT_WIFI, "Received EOF on supplicant socket"); return snprintf(buf, buflen, WPA_EVENT_TERMINATING " - signal 0 received"); } /* @@ -152,7 +203,7 @@ int wifi_wait_on_socket(char *buf, size_t buflen) * to us, so strip it off. */ - if (strncmp(buf, IFNAME, IFNAMELEN) == 0) { + if (strncmp(buf, "IFNAME=", (sizeof("IFNAME=") - 1)) == 0) { match = strchr(buf, ' '); if (match != NULL) { if (match[1] == '<') { @@ -170,11 +221,11 @@ int wifi_wait_on_socket(char *buf, size_t buflen) if (match != NULL) { nread -= (match + 1 - buf); memmove(buf, match + 1, nread + 1); - //qWarning("supplicant generated event without interface - %s\n", buf); + //qCWarning(B2QT_WIFI, "supplicant generated event without interface - %s", buf); } } else { /* let the event go as is! */ - qWarning("supplicant generated event without interface and without message level - %s\n", buf); + qCWarning(B2QT_WIFI, "supplicant generated event without interface and without message level - %s", buf); } return nread; @@ -193,7 +244,7 @@ int wifi_ctrl_recv(char *reply, size_t *reply_len) rfds[1].events |= POLLIN; res = TEMP_FAILURE_RETRY(poll(rfds, 2, -1)); if (res < 0) { - qWarning("Error poll = %d", res); + qCWarning(B2QT_WIFI, "Error poll = %d", res); return res; } if (rfds[0].revents & POLLIN) { @@ -210,12 +261,12 @@ int wifi_send_command(const char *cmd, char *reply, size_t *reply_len) { int ret; if (ctrl_conn == NULL) { - qWarning("Not connected to wpa_supplicant - \"%s\" command dropped.\n", cmd); + 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) { - qWarning("'%s' command timed out.\n", cmd); + 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; @@ -262,3 +313,5 @@ void wifi_close_sockets() exit_sockets[1] = -1; } } + +QT_END_NAMESPACE diff --git a/src/imports/wifi/qwifi_elinux.h b/src/wifi/qwifielinux_p.h index a89642f..bef90c3 100644 --- a/src/imports/wifi/qwifi_elinux.h +++ b/src/wifi/qwifielinux_p.h @@ -19,9 +19,15 @@ #ifndef LOCAL_WIFI_H #define LOCAL_WIFI_H +#include <QtCore/QLoggingCategory> + #include <string.h> -// This API mirrors Android's Wi-Fi libraries interface [1] and implementation, excluding Android OS specific parts. +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); @@ -31,4 +37,6 @@ 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 ¤tSSID); + 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/imports/wifi/qwifinetwork.cpp b/src/wifi/qwifinetwork.cpp index 84e3b5b..937cbfc 100644 --- a/src/imports/wifi/qwifinetwork.cpp +++ b/src/wifi/qwifinetwork.cpp @@ -16,20 +16,24 @@ ** the contact form at http://www.qt.io ** ****************************************************************************/ -#include "qwifinetwork.h" +#include "qwifinetwork_p.h" + +QT_BEGIN_NAMESPACE /*! \qmltype WifiNetwork - \inqmlmodule Qt.labs.wifi + \inqmlmodule QtWifi \ingroup wifi-qmltypes \brief Represents a single WiFi network access point. - Instances of WifiNetwork cannot be created directly from the QML system, use - WifiManager::networks. + 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. @@ -38,12 +42,14 @@ /*! \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. @@ -53,24 +59,28 @@ /*! \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. */ @@ -83,22 +93,29 @@ */ -QWifiNetwork::QWifiNetwork() : - m_outOfRange(false) +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) { - if (m_signalStrength == strength) - return; m_signalStrength = strength; - emit signalStrengthChanged(m_signalStrength); } void QWifiNetwork::setOutOfRange(bool outOfRange) { - if (m_outOfRange == outOfRange) - return; - m_outOfRange = 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/imports/wifi/qwifinetworklistmodel.cpp b/src/wifi/qwifinetworklistmodel.cpp index 1886413..7a5de2a 100644 --- a/src/imports/wifi/qwifinetworklistmodel.cpp +++ b/src/wifi/qwifinetworklistmodel.cpp @@ -16,14 +16,19 @@ ** the contact form at http://www.qt.io ** ****************************************************************************/ -#include "qwifinetworklistmodel.h" +#include "qwifinetworklistmodel_p.h" +#include "qwifinetwork_p.h" -#include <QtCore> +#include "qwifimanager.h" -const int ID_NETWORK = (Qt::ItemDataRole) (Qt::UserRole + 1); +#include <QtCore/QSet> +#include <QtCore/QString> +#include <QtCore/QByteArray> -QWifiNetworkListModel::QWifiNetworkListModel(QWifiManager *manager) - : m_manager(manager) +QT_BEGIN_NAMESPACE + +QWifiNetworkListModel::QWifiNetworkListModel(QObject *parent) + : QAbstractListModel(parent) { } @@ -38,23 +43,56 @@ QWifiNetworkListModel::~QWifiNetworkListModel() QHash<int, QByteArray> QWifiNetworkListModel::roleNames() const { QHash<int, QByteArray> names; - names.insert(ID_NETWORK, "network"); + 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 ID_NETWORK: return QVariant::fromValue((QObject *) n); + 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; } - qWarning("QWifiNetworkListModel::data(), undefined role: %d\n", role); - return QVariant(); } -QWifiNetwork *QWifiNetworkListModel::networkForSSID(const QByteArray &ssid, int *pos) +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) { @@ -66,7 +104,7 @@ QWifiNetwork *QWifiNetworkListModel::networkForSSID(const QByteArray &ssid, int return 0; } -QWifiNetwork *QWifiNetworkListModel::outOfRangeListContains(const QByteArray &ssid) +QWifiNetwork *QWifiNetworkListModel::outOfRangeListContains(const QString &ssid) { for (int i = 0; i < m_outOfRangeNetworks.length(); ++i) if (m_outOfRangeNetworks.at(i)->ssid() == ssid) @@ -74,20 +112,22 @@ QWifiNetwork *QWifiNetworkListModel::outOfRangeListContains(const QByteArray &ss return 0; } -void QWifiNetworkListModel::parseScanResults(const QByteArray &results) +void QWifiNetworkListModel::parseScanResults(const QString &results) { - QList<QByteArray> lines = results.split('\n'); - QSet<QByteArray> sensibleNetworks; + QStringList lines = results.split('\n'); + QSet<QString> sensibleNetworks; for (int i = 1; i < lines.size(); ++i) { - QList<QByteArray> info = lines.at(i).split('\t'); + QStringList info = lines.at(i).split('\t'); if (info.size() < 5 || info.at(4).isEmpty() || info.at(0).isEmpty()) continue; int pos = 0; - sensibleNetworks.insert(info.at(4)); - QWifiNetwork *knownNetwork = networkForSSID(info.at(4), &pos); + + QString ssid = info.at(4); + sensibleNetworks.insert(ssid); + QWifiNetwork *knownNetwork = networkForSSID(ssid, &pos); if (!knownNetwork) - knownNetwork = outOfRangeListContains(info.at(4)); + 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; @@ -97,12 +137,12 @@ void QWifiNetworkListModel::parseScanResults(const QByteArray &results) network->setBssid(info.at(0)); network->setFlags(info.at(3)); network->setSignalStrength(signalStrength); - network->setSsid(info.at(4)); + network->setSsid(ssid); beginInsertRows(QModelIndex(), m_networks.size(), m_networks.size()); m_networks << network; endInsertRows(); } else { - if (knownNetwork->outOfRange()) { + if (knownNetwork->isOutOfRange()) { // known network has come back into a range knownNetwork->setOutOfRange(false); beginInsertRows(QModelIndex(), m_networks.size(), m_networks.size()); @@ -122,7 +162,7 @@ void QWifiNetworkListModel::parseScanResults(const QByteArray &results) 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(info.at(4)); + m_networks.at(pos)->setSsid(ssid); dataChanged(createIndex(pos, 0), createIndex(pos, 0)); } } @@ -140,3 +180,5 @@ void QWifiNetworkListModel::parseScanResults(const QByteArray &results) } } } + +QT_END_NAMESPACE diff --git a/src/imports/wifi/qwifinetworklistmodel.h b/src/wifi/qwifinetworklistmodel_p.h index 0cb9846..25a2b31 100644 --- a/src/imports/wifi/qwifinetworklistmodel.h +++ b/src/wifi/qwifinetworklistmodel_p.h @@ -20,35 +20,38 @@ #define QWIFINETWORKLISTMODEL_H #include <QtCore/QAbstractListModel> +#include <QtCore/QVariant> #include <QtCore/QList> +#include <QtCore/QHash> +#include <QtCore/QLoggingCategory> -#include "qwifinetwork.h" +QT_BEGIN_NAMESPACE -class QWifiManager; +Q_DECLARE_LOGGING_CATEGORY(B2QT_WIFI) + +class QWifiNetwork; class QWifiNetworkListModel : public QAbstractListModel { Q_OBJECT - public: - - QWifiNetworkListModel(QWifiManager *manager); - ~QWifiNetworkListModel(); - - void parseScanResults(const QByteArray &data); - - QWifiNetwork *networkForSSID(const QByteArray &ssid, int *pos); - QWifiNetwork *outOfRangeListContains(const QByteArray &ssid); + 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: - QWifiManager *m_manager; 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 +} |