summaryrefslogtreecommitdiffstats
path: root/src/qconnectivity/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qconnectivity/main.cpp')
-rw-r--r--src/qconnectivity/main.cpp476
1 files changed, 0 insertions, 476 deletions
diff --git a/src/qconnectivity/main.cpp b/src/qconnectivity/main.cpp
deleted file mode 100644
index d25c42e..0000000
--- a/src/qconnectivity/main.cpp
+++ /dev/null
@@ -1,476 +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 <QtCore>
-#include <QtNetwork/QLocalSocket>
-#include <QtNetwork/QLocalServer>
-
-#include <unistd.h>
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
-#include <netutils/dhcp.h>
-#include <netutils/ifc.h>
-
-Q_LOGGING_CATEGORY(B2QT_QCONNECTIVITY, "qt.b2qt.qconnectivity")
-
-// Code values come from android/system/netd/ResponseCode.h
-const int InterfaceChange = 600;
-const char UNIQUE_HOSTNAME[] = "net.hostname";
-// sanity check a renewal time, lower value than
-// this might indicate a badly configured DHCP server
-const int MIN_RENEWAL_TIME_SECS = 300; // 5 min
-
-#define ETH_INTERFACE_HW "eth0"
-#define ETH_INTERFACE_EMULATOR "eth1"
-
-#if Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 3
-// this function is defined in android/system/core/libnetutils/dhcp_utils.c
-extern "C" {
-int dhcp_do_request_renew(const char *ifname,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns1,
- char *dns2,
- char *server,
- uint32_t *lease,
- char *vendorInfo);
-}
-#endif
-
-static int q_dhcp_do_request(bool renew,
- const char *ifname,
- char *ipaddr,
- char *gateway,
- uint32_t *prefixLength,
- char *dns1,
- char *dns2,
- char *server,
- uint32_t *lease,
- char *vendorInfo,
- char *domain)
-{
-#if Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 3
- if (!renew)
- return dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2, server, lease, vendorInfo);
- return dhcp_do_request_renew(ifname, ipaddr, gateway, prefixLength, dns1, dns2, server, lease, vendorInfo);
-#else
- char *dns[3] = {dns1, dns2, 0};
- char mtu[PROPERTY_VALUE_MAX];
-#if Q_ANDROID_VERSION_MAJOR == 4 && Q_ANDROID_VERSION_MINOR < 4
- if (!renew)
- return dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns, server, lease, vendorInfo);
- return dhcp_do_request_renew(ifname, ipaddr, gateway, prefixLength, dns, server, lease, vendorInfo);
-#else
- if (!renew)
- return dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns, server, lease, vendorInfo, domain, mtu);
- return dhcp_do_request_renew(ifname, ipaddr, gateway, prefixLength, dns, server, lease, vendorInfo, domain, mtu);
-#endif
-#endif
-}
-
-
-class LeaseTimer;
-class QConnectivityDaemon : public QObject
-{
- Q_OBJECT
-public:
- QConnectivityDaemon();
-
-protected:
- void setHostnamePropery(const char *interface) const;
- void sendCommand(const char *command) const;
- void handleInterfaceChange(const QList<QByteArray> &message);
- bool startDhcp(bool renew, const char *interface);
- void stopDhcp(const char *interface);
- bool ethernetSupported() const;
- bool isEmulator() const;
-
-protected slots:
- void initNetdConnection();
- void handleNetdEvent();
- void handleRequest();
- void handleNewConnection();
- void sendReply(QLocalSocket *requester, const QByteArray &reply) const;
- void updateLease();
- void handleError(QLocalSocket::LocalSocketError /*socketError*/) const;
-
-private:
- friend class LeaseTimer;
- QLocalSocket *m_netdSocket;
- // currently used to listen for requests from Qt Wifi library,
- // in future can be used also for Bluetooth and etc.
- QLocalServer *m_serverSocket;
- bool m_linkUp;
- LeaseTimer *m_leaseTimer;
- bool m_isEmulator;
- QByteArray m_ethInterface;
- // android initializes services in a separate threads, therefore it is
- // not guaranteed that the netd socket will be ready at the time when qconnectivity
- // is starting up - we try to reconnect again after 2 second intervals. This
- // variable holds the maximum attempt count (chosen arbitrarily).
- int m_attemptCount;
-};
-
-class LeaseTimer : public QTimer
-{
- Q_OBJECT
-public:
- LeaseTimer(QConnectivityDaemon *daemon) : m_daemon(daemon) {}
-
- void setInterface(const QByteArray &interface)
- {
- if (m_ifc.isEmpty()) {
- m_ifc = interface;
- } else {
- // for example when user switches from eth0 to wlan0, we
- // stop DHCP on the previous interface
- if (m_ifc != interface) {
- m_daemon->stopDhcp(m_ifc.constData());
- m_ifc = interface;
- }
- }
- }
-
- QByteArray interface() const { return m_ifc; }
-
-private:
- QConnectivityDaemon *m_daemon;
- QByteArray m_ifc;
-};
-
-QConnectivityDaemon::QConnectivityDaemon()
- : m_netdSocket(0),
- m_serverSocket(0),
- m_linkUp(false),
- m_leaseTimer(0),
- m_isEmulator(isEmulator()),
- m_attemptCount(50)
-{
- QLoggingCategory::setFilterRules(QStringLiteral("qt.b2qt.qconnectivity=true"));
- qCDebug(B2QT_QCONNECTIVITY) << "starting QConnectivityDaemon...";
-
- if (!m_isEmulator) {
- m_ethInterface = ETH_INTERFACE_HW;
- m_leaseTimer = new LeaseTimer(this);
- m_leaseTimer->setSingleShot(true);
- connect(m_leaseTimer, SIGNAL(timeout()), this, SLOT(updateLease()));
-
- int serverFd = socket_local_server("qconnectivity", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
- if (serverFd != -1) {
- m_serverSocket = new QLocalServer(this);
- if (m_serverSocket->listen(serverFd))
- connect(m_serverSocket, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
- else
- qCWarning(B2QT_QCONNECTIVITY) << "not able to listen on the server socket...";
- } else {
- qCWarning(B2QT_QCONNECTIVITY) << "failed to open qconnectivity server socket";
- }
- } else {
- m_ethInterface = ETH_INTERFACE_EMULATOR;
- }
- initNetdConnection();
-}
-
-bool QConnectivityDaemon::isEmulator() const
-{
- bool isEmulator = false;
- QFile conf("/system/bin/appcontroller.conf");
- if (conf.open(QIODevice::ReadOnly)) {
- QByteArray content = conf.readAll();
- isEmulator = content.contains("platform=emulator");
- conf.close();
- } else {
- qCWarning(B2QT_QCONNECTIVITY) << "Failed to read appcontroller.conf";
- }
- return isEmulator;
-}
-
-void QConnectivityDaemon::initNetdConnection()
-{
- int netdFd = socket_local_client("netd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
- if (netdFd != -1) {
- qCDebug(B2QT_QCONNECTIVITY) << "connected to netd socket";
- m_netdSocket = new QLocalSocket(this);
- m_netdSocket->setSocketDescriptor(netdFd);
- connect(m_netdSocket, SIGNAL(readyRead()), this, SLOT(handleNetdEvent()));
- connect(m_netdSocket, SIGNAL(error(QLocalSocket::LocalSocketError)),
- this, SLOT(handleError(QLocalSocket::LocalSocketError)));
- } else {
- if (--m_attemptCount != 0)
- QTimer::singleShot(200, this, SLOT(initNetdConnection()));
- else
- qCWarning(B2QT_QCONNECTIVITY) << "failed to connect to netd socket!";
- return;
- }
- if (ethernetSupported()) {
- // down-up sequence generates "linkstate" events, which we can use to setup
- // our daemon on initial startup (device boot) or on daemon restarts
- sendCommand(QByteArray("0 interface setcfg ").append(m_ethInterface).append(" down").constData());
- sendCommand(QByteArray("0 interface setcfg ").append(m_ethInterface).append(" up").constData());
- }
- // disable firewall - this setting seems to be enabled only when using "Always-on VPN"
- // mode on Android phones, see setLockdownTracker() in ConnectivityService.java
- sendCommand("0 firewall disable");
-}
-
-void QConnectivityDaemon::setHostnamePropery(const char *interface) const
-{
- // Setup our unique device name (used as a host name argument for dhcpcd call in
- // dhcp_do_request). On Android device name is set in ConnectivityService.java and
- // the id is generated with the help of SecureRandom.java class. We will use Mac
- // address as a unique hostname.
- char prop_value[PROPERTY_VALUE_MAX];
- property_get(UNIQUE_HOSTNAME, prop_value, NULL);
- if ((prop_value[0] == '\0')) {
- char hwaddr[6];
- memset(hwaddr, 0, sizeof(hwaddr));
- ifc_init();
- if (ifc_get_hwaddr(interface, (void *)hwaddr) == 0) {
- QByteArray macAddress(hwaddr, sizeof(hwaddr));
- property_set(UNIQUE_HOSTNAME, macAddress.toHex().prepend("b2qt-").constData());
- } else {
- qCWarning(B2QT_QCONNECTIVITY) << "failed to get MAC address";
- }
- ifc_close();
- }
-}
-
-void QConnectivityDaemon::sendCommand(const char *command) const
-{
- if (!m_netdSocket) {
- qCDebug(B2QT_QCONNECTIVITY) << "netd socket is not ready!";
- return;
- }
- qCDebug(B2QT_QCONNECTIVITY) << "sending command - " << command;
- // netd expects "\0" terminated commands...
- m_netdSocket->write(command, qstrlen(command) + 1);
- m_netdSocket->flush();
-}
-
-void QConnectivityDaemon::handleInterfaceChange(const QList<QByteArray> &message)
-{
- // Format: "Code Iface linkstate <name> <up/down>"
- if (message.size() < 5)
- return;
-
- if (message.at(2) == "linkstate" && message.at(3) == m_ethInterface) {
- if (message.at(4) == "up") {
- // ethernet cable has been plugged in
- if (!m_linkUp) {
- m_linkUp = true;
- startDhcp(false, m_ethInterface);
- }
- } else {
- // .. plugged out
- if (m_linkUp) {
- m_linkUp = false;
- stopDhcp(m_ethInterface);
- }
- }
- }
-}
-
-bool QConnectivityDaemon::startDhcp(bool renew, const char *interface)
-{
- qCDebug(B2QT_QCONNECTIVITY) << "startDhcp [ renew" << renew << "] "
- << "interface: " << interface;
- setHostnamePropery(interface);
-
- int result = 0;
- char ipaddr[PROPERTY_VALUE_MAX];
- quint32 prefixLength = 0;
- char gateway[PROPERTY_VALUE_MAX];
- char dns1[PROPERTY_VALUE_MAX];
- char dns2[PROPERTY_VALUE_MAX];
- char server[PROPERTY_VALUE_MAX];
- quint32 lease = 0;
- char vendorInfo[PROPERTY_VALUE_MAX];
- char domain[PROPERTY_VALUE_MAX] = {0};
-
- if (renew) {
- result = q_dhcp_do_request(true, interface, ipaddr, gateway, &prefixLength,
- dns1, dns2, server, &lease, vendorInfo, domain);
- } else {
- // stop any existing DHCP daemon before starting new
- dhcp_stop(interface);
- // this uses "ctl.start.*" mechanism to start "dhcpcd" daemon as defined by
- // the device init.rc. Android starts dhcpcd with argument -B which means that
- // we are responsible for renewing a lease before it expires
- ifc_clear_addresses(interface);
- result = q_dhcp_do_request(false, interface, ipaddr, gateway, &prefixLength,
- dns1, dns2, server, &lease, vendorInfo, domain);
- }
-
- bool success = (result == 0) ? true : false;
- if (success) {
- qCDebug(B2QT_QCONNECTIVITY) << "\nipaddr: " << ipaddr << "\nprefixLength: " << prefixLength
- << "\ngateway: " << gateway << "\ndns1: " << dns1 << "\ndns2: " << dns2;
-
- if (!renew) {
- in_addr _ipaddr, _gateway, _dns1, _dns2;
- inet_aton(ipaddr, &_ipaddr);
- inet_aton(gateway, &_gateway);
- inet_aton(dns1, &_dns1);
- inet_aton(dns2, &_dns2);
-
- ifc_configure(interface, _ipaddr.s_addr, prefixLength,
- _gateway.s_addr, _dns1.s_addr, _dns2.s_addr);
-
- // set DNS servers and domain for interface - see NetworkManagementService.java
- QByteArray dnsForInterface("0 resolver setifdns ");
- dnsForInterface.append(interface).append(" ");
- if (domain[0])
- dnsForInterface.append(domain);
- else
- dnsForInterface.append(" ");
- dnsForInterface.append(" ");
- dnsForInterface.append(dns1).append(" ").append(dns2);
- sendCommand(dnsForInterface.constData());
-
- // set default interface for DNS - see NetworkManagementService.java
- sendCommand(QByteArray("0 resolver setdefaultif ").append(interface).constData());
-
- property_set("net.dns1", dns1);
- property_set("net.dns2", dns2);
- }
-
- if (!m_isEmulator && lease >= 0) {
- if (lease < MIN_RENEWAL_TIME_SECS) {
- qCWarning(B2QT_QCONNECTIVITY) << "DHCP server proposes lease time " << lease
- << "seconds. We will use" << MIN_RENEWAL_TIME_SECS << " seconds instead.";
- lease = MIN_RENEWAL_TIME_SECS;
- }
- // update lease when 48% of lease time has elapsed
- if (m_leaseTimer->isActive())
- m_leaseTimer->stop();
- m_leaseTimer->setInterface(interface);
- m_leaseTimer->start(lease * 480);
- }
- } else {
- qCWarning(B2QT_QCONNECTIVITY, "DHCP request failed - %s", dhcp_get_errmsg());
- if (renew) {
- // If it fails to renew a lease (faulty server, proxy?) we re-connect.
- // Some users might prefer to use expired lease over having interrupt
- // in network connection
- if (qEnvironmentVariableIsSet("QT_USE_EXPIRED_LEASE"))
- return true;
- qCDebug(B2QT_QCONNECTIVITY) << "attempting to re-connect...";
- stopDhcp(interface);
- startDhcp(false, interface);
- }
- }
- return success;
-}
-
-void QConnectivityDaemon::stopDhcp(const char *interface)
-{
- qCDebug(B2QT_QCONNECTIVITY) << "stopDhcp: " << interface;
- ifc_clear_addresses(interface);
- dhcp_stop(interface);
- if (!m_isEmulator && m_leaseTimer->isActive())
- m_leaseTimer->stop();
-}
-
-bool QConnectivityDaemon::ethernetSupported() const
-{
- // standard linux kernel path
- return QDir().exists(QString("/sys/class/net/").append(m_ethInterface));
-}
-
-void QConnectivityDaemon::handleNetdEvent()
-{
- QByteArray data = m_netdSocket->readAll();
- qCDebug(B2QT_QCONNECTIVITY) << "netd event: " << data;
- if (data.endsWith('\0'))
- data.chop(1);
-
- QList<QByteArray> message = data.split(' ');
- int code = message.at(0).toInt();
- switch (code) {
- case InterfaceChange:
- handleInterfaceChange(message);
- break;
- default:
- break;
- }
-}
-
-void QConnectivityDaemon::handleRequest()
-{
- // Format: "interface <connect/disconnect>"
- QLocalSocket *requester = qobject_cast<QLocalSocket *>(QObject::sender());
- if (requester->canReadLine()) {
- QByteArray request = requester->readLine(requester->bytesAvailable());
-
- qCDebug(B2QT_QCONNECTIVITY) << "received a request: " << request;
- QList<QByteArray> cmd = request.split(' ');
- if (cmd.size() < 2)
- return;
-
- QByteArray interface = cmd.at(0);
- if (cmd.at(1) == "connect") {
- QByteArray reply;
- if (startDhcp(false, interface.constData()))
- reply = "success";
- else
- reply = "failed";
- sendReply(requester, reply);
- } else {
- stopDhcp(interface.constData());
- }
- }
-}
-
-void QConnectivityDaemon::handleNewConnection()
-{
- QLocalSocket *requester = m_serverSocket->nextPendingConnection();
- connect(requester, SIGNAL(readyRead()), this, SLOT(handleRequest()));
- connect(requester, SIGNAL(disconnected()), requester, SLOT(deleteLater()));
-}
-
-void QConnectivityDaemon::sendReply(QLocalSocket *requester, const QByteArray &reply) const
-{
- QByteArray r = reply;
- r.append("\n");
- requester->write(r.constData(), r.length());
- requester->flush();
- requester->disconnectFromServer();
-}
-
-void QConnectivityDaemon::updateLease()
-{
- qCDebug(B2QT_QCONNECTIVITY) << "updating lease";
- startDhcp(true, m_leaseTimer->interface().constData());
-}
-
-void QConnectivityDaemon::handleError(QLocalSocket::LocalSocketError /*socketError*/) const
-{
- qCWarning(B2QT_QCONNECTIVITY) << "QLocalSocket::LocalSocketError";
-}
-
-int main(int argc, char *argv[])
-{
- QCoreApplication a(argc, argv);
-
- QConnectivityDaemon connectivityDaemon;
-
- return a.exec();
-}
-
-#include "main.moc"