diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index ca60133265..4c2a36db87 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -71,7 +71,7 @@ else: else: qt_libs += [f"Qt5{m}" for m in qt_modules] - qt_src = ["qt/ui.cc", "qt/window.cc", "qt/qt_sound.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc"] + src + qt_src = ["qt/ui.cc", "qt/window.cc", "qt/qt_sound.cc", "qt/offroad/settings.cc", "qt/offroad/onboarding.cc", "qt/offroad/wifi.cc", "qt/offroad/wifiManager.cc"] + src qt_env.Program("_ui", qt_src, LIBS=qt_libs + libs) # spinner and text window diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 6bb731a09a..8711bf5c5e 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -3,8 +3,6 @@ #include #include -#include "settings.hpp" - #include #include #include @@ -12,6 +10,9 @@ #include #include +#include "wifi.hpp" +#include "settings.hpp" + #include "common/params.h" ParamsToggle::ParamsToggle(QString param, QString title, QString description, QString icon_path, QWidget *parent): QFrame(parent) , param(param) { @@ -189,6 +190,17 @@ QWidget * developer_panel() { return widget; } +QWidget * network_panel() { + QVBoxLayout *main_layout = new QVBoxLayout; + + main_layout->addWidget(new WifiUI()); + + QWidget *widget = new QWidget; + widget->setLayout(main_layout); + return widget; +} + + void SettingsWindow::setActivePanel() { QPushButton *btn = qobject_cast(sender()); panel_layout->setCurrentWidget(panels[btn->text()]); @@ -217,6 +229,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QWidget(parent) { {"device", device_panel()}, {"toggles", toggles_panel()}, {"developer", developer_panel()}, + {"network", network_panel()}, }; for (auto &panel : panels) { diff --git a/selfdrive/ui/qt/offroad/settings.hpp b/selfdrive/ui/qt/offroad/settings.hpp index 456155e33b..e4f65a6944 100644 --- a/selfdrive/ui/qt/offroad/settings.hpp +++ b/selfdrive/ui/qt/offroad/settings.hpp @@ -21,7 +21,6 @@ public slots: void checkboxClicked(int state); }; - class SettingsWindow : public QWidget { Q_OBJECT diff --git a/selfdrive/ui/qt/offroad/wifi.cc b/selfdrive/ui/qt/offroad/wifi.cc new file mode 100644 index 0000000000..610ec0805b --- /dev/null +++ b/selfdrive/ui/qt/offroad/wifi.cc @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wifi.hpp" +#include "wifiManager.hpp" +CustomConnectButton::CustomConnectButton(QString text, int iid){ + setText(text); + id=iid; +} + +void clearLayout(QLayout* layout){ + while (QLayoutItem* item = layout->takeAt(0)){ + if (QWidget* widget = item->widget()){ + widget->deleteLater(); + } + if (QLayout* childLayout = item->layout()){ + clearLayout(childLayout); + } + delete item; + } +} + +WifiUI::WifiUI(QWidget *parent) : QWidget(parent) { + vlayout = new QVBoxLayout; + wifi = new WifiManager; + refresh(); + setLayout(vlayout); + + setStyleSheet(R"( + QLabel { font-size: 40px } + QPushButton:enabled { + background-color: #114265; + } + QPushButton:disabled { + background-color: #323C43; + } + * { + background-color: #114265; + } + )"); + + // TODO: implement (not) connecting with wrong password + + // Update network list every second + timer = new QTimer(this); + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(refresh())); + timer->start(1000); + + // Scan on startup + wifi->request_scan(); +} + +void WifiUI::refresh(){ + if (!this->isVisible()){ + return; + } + + wifi->request_scan(); + wifi->refreshNetworks(); + + clearLayout(vlayout); + int i=0; + + QButtonGroup* connectButtons=new QButtonGroup(this); + QObject::connect(connectButtons, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(handleButton(QAbstractButton*))); + for (Network &network : wifi->seen_networks){ + QHBoxLayout *hlayout = new QHBoxLayout; + hlayout->addWidget(new QLabel(QString::fromUtf8(network.ssid))); + unsigned int strength_scale = std::round(network.strength / 25.0) * 25; + QPixmap pix("../assets/offroad/indicator_wifi_" + QString::number(strength_scale) + ".png"); + QLabel *icon = new QLabel(); + icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation)); + icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + hlayout->addWidget(icon); + hlayout->addSpacing(20); + + CustomConnectButton* m_button = new CustomConnectButton(network.connected ? "Connected" : "Connect",i); + m_button->setFixedWidth(300); + m_button->setDisabled(network.connected || network.security_type == SecurityType::UNSUPPORTED); + connectButtons->addButton(m_button,i); + + hlayout->addWidget(m_button); + hlayout->addSpacing(20); + vlayout->addLayout(hlayout); + i+=1; + } +} + +void WifiUI::handleButton(QAbstractButton* button){ + CustomConnectButton* m_button = static_cast(button); + int id = m_button->id; + Network n = wifi->seen_networks[id]; + // qDebug() << "Clicked a button:" << id; + // qDebug() << n.ssid; + if(n.security_type==SecurityType::OPEN){ + wifi->connect(n); + } else if (n.security_type==SecurityType::WPA){ + bool ok = false; + QString password; + +#ifdef QCOM2 + // TODO: implement touch keyboard +#else + password = QInputDialog::getText(this, "Password for "+n.ssid, "Password", QLineEdit::Normal, "", &ok); +#endif + if (ok){ + wifi->connect(n, password); + } + + } else { + qDebug() << "Cannot determine a network's security type"; + } + +} diff --git a/selfdrive/ui/qt/offroad/wifi.hpp b/selfdrive/ui/qt/offroad/wifi.hpp new file mode 100644 index 0000000000..1caf2f4ee2 --- /dev/null +++ b/selfdrive/ui/qt/offroad/wifi.hpp @@ -0,0 +1,33 @@ +#pragma once +#include "wifiManager.hpp" +#include +#include +#include +#include +#include +#include +#include + + +class CustomConnectButton : public QPushButton{ + +public: + explicit CustomConnectButton(QString text, int iid); + int id; +}; + +class WifiUI : public QWidget { + Q_OBJECT + + private: + WifiManager* wifi; + QVBoxLayout* vlayout; + QTimer * timer; + + public: + explicit WifiUI(QWidget *parent = 0); + + private slots: + void handleButton(QAbstractButton* m_button); + void refresh(); +}; diff --git a/selfdrive/ui/qt/offroad/wifiManager.cc b/selfdrive/ui/qt/offroad/wifiManager.cc new file mode 100644 index 0000000000..a602b75e12 --- /dev/null +++ b/selfdrive/ui/qt/offroad/wifiManager.cc @@ -0,0 +1,255 @@ +#include +#include + +#include "wifiManager.hpp" +#include "wifi.hpp" +typedef QMap > Connection; + +QString nm_path = "/org/freedesktop/NetworkManager"; +QString nm_settings_path = "/org/freedesktop/NetworkManager/Settings"; + +QString nm_iface = "org.freedesktop.NetworkManager"; +QString props_iface = "org.freedesktop.DBus.Properties"; +QString nm_settings_iface = "org.freedesktop.NetworkManager.Settings"; +QString nm_settings_conn_iface = "org.freedesktop.NetworkManager.Settings.Connection"; +QString device_iface = "org.freedesktop.NetworkManager.Device"; +QString wireless_device_iface = "org.freedesktop.NetworkManager.Device.Wireless"; +QString ap_iface = "org.freedesktop.NetworkManager.AccessPoint"; +QString connection_iface = "org.freedesktop.NetworkManager.Connection.Active"; + +QString nm_service = "org.freedesktop.NetworkManager"; + + +template +T get_response(QDBusMessage response){ + QVariant first = response.arguments().at(0); + QDBusVariant dbvFirst = first.value(); + QVariant vFirst = dbvFirst.variant(); + return vFirst.value(); +} + +bool compare_by_strength(const Network &a, const Network &b){ + if (a.connected) return true; + if (b.connected) return false; + return a.strength > b.strength; +} + +WifiManager::WifiManager(){ + qDBusRegisterMetaType(); + + adapter = get_adapter(); + has_adapter = adapter != ""; +} + +void WifiManager::refreshNetworks(){ + if (!has_adapter) return; + + bus = QDBusConnection::systemBus(); + seen_networks.clear(); + seen_ssids.clear(); + + for (Network &network : get_networks()){ + if(seen_ssids.count(network.ssid)){ + continue; + } + seen_ssids.push_back(network.ssid); + seen_networks.push_back(network); + } +} + +QList WifiManager::get_networks(){ + QList r; + QDBusInterface nm(nm_service, adapter, wireless_device_iface, bus); + QDBusMessage response = nm.call("GetAllAccessPoints"); + QVariant first = response.arguments().at(0); + + QString active_ap = get_active_ap(); + + const QDBusArgument &args = first.value(); + args.beginArray(); + while (!args.atEnd()) { + QDBusObjectPath path; + args >> path; + + QByteArray ssid = get_property(path.path(), "Ssid"); + unsigned int strength = get_ap_strength(path.path()); + SecurityType security = getSecurityType(path.path()); + Network network = {path.path(), ssid, strength, path.path()==active_ap, security}; + + if (ssid.length()){ + r.push_back(network); + } + } + args.endArray(); + + std::sort(r.begin(), r.end(), compare_by_strength); + return r; +} + +SecurityType WifiManager::getSecurityType(QString path){ + int sflag = get_property(path, "Flags").toInt(); + int wpaflag = get_property(path, "WpaFlags").toInt(); + int rsnflag = get_property(path, "RsnFlags").toInt(); + int wpa_props = wpaflag | rsnflag; + if(sflag == 0){ + return SecurityType::OPEN; + }else if((sflag & 0x1) && (wpa_props & (0x333) && !(wpa_props & 0x200)) ){ + return SecurityType::WPA; + }else{ + // qDebug() << "Cannot determine security type for " << get_property(path, "Ssid") << " with flags"; + // qDebug() << "flag " << sflag; + // qDebug() << "WpaFlag " << wpaflag; + // qDebug() << "RsnFlag " << rsnflag; + return SecurityType::UNSUPPORTED; + } +} +void WifiManager::connect(Network n){ + return connect(n,"",""); +} +void WifiManager::connect(Network n, QString password){ + return connect(n, "", password); +} + +void WifiManager::connect(Network n, QString username, QString password){ + QString active_ap = get_active_ap(); + if(active_ap!=""){ + clear_connections(get_property(active_ap,"Ssid")); + } + clear_connections(n.ssid); + qDebug() << "Connecting to"<< n.ssid << "with username, password =" << username << "," < result = nm_settings.call("AddConnection", QVariant::fromValue(connection)); + if (!result.isValid()) { + qDebug() << result.error().name() << result.error().message(); + } else { + qDebug() << result.value().path(); + } + +} + +void WifiManager::print_active_connections(){ + //TO-DO clean up, the code is not currently in use. + QDBusInterface nm(nm_service, nm_path, props_iface, bus); + QDBusMessage response = nm.call("Get", nm_iface, "ActiveConnections"); + QVariant first = response.arguments().at(0); + QDBusVariant dbvFirst = first.value(); + QVariant vFirst = dbvFirst.variant(); + QDBusArgument step4 = vFirst.value(); + QDBusObjectPath path; + step4.beginArray(); + while (!step4.atEnd()){ + step4 >> path; + qDebug()<(); + args.beginArray(); + while (!args.atEnd()) { + QDBusObjectPath path; + args >> path; + QDBusInterface nm2(nm_service, path.path(), nm_settings_conn_iface, bus); + QDBusMessage response = nm2.call("GetSettings"); + + const QDBusArgument &dbusArg = response.arguments().at(0).value(); + + QMap > map; + dbusArg >> map; + for( QString outer_key : map.keys() ){ + QMap innerMap = map.value(outer_key); + for( QString inner_key : innerMap.keys() ){ + if(inner_key=="ssid"){ + QString value = innerMap.value(inner_key).value(); + if(value == ssid){ + // qDebug()<<"Deleting "<(response); + return resp; +} +QString WifiManager::get_active_ap(){ + QDBusInterface device_props(nm_service, adapter, props_iface, bus); + QDBusMessage response = device_props.call("Get", wireless_device_iface, "ActiveAccessPoint"); + QDBusObjectPath r = get_response(response); + return r.path(); +} +QByteArray WifiManager::get_property(QString network_path ,QString property){ + QDBusInterface device_props(nm_service, network_path, props_iface, bus); + QDBusMessage response = device_props.call("Get", ap_iface, property); + return get_response(response); +} + +unsigned int WifiManager::get_ap_strength(QString network_path){ + QDBusInterface device_props(nm_service, network_path, props_iface, bus); + QDBusMessage response = device_props.call("Get", ap_iface, "Strength"); + return get_response(response); +} + +QString WifiManager::get_adapter(){ + + QDBusInterface nm(nm_service, nm_path, nm_iface, bus); + QDBusMessage response = nm.call("GetDevices"); + QVariant first = response.arguments().at(0); + + QString adapter_path = ""; + + const QDBusArgument &args = first.value(); + args.beginArray(); + while (!args.atEnd()) { + QDBusObjectPath path; + args >> path; + + // Get device type + QDBusInterface device_props(nm_service, path.path(), props_iface, bus); + QDBusMessage response = device_props.call("Get", device_iface, "DeviceType"); + uint device_type = get_response(response); + + if (device_type == 2){ // Wireless + adapter_path = path.path(); + break; + } + } + args.endArray(); + + return adapter_path; +} diff --git a/selfdrive/ui/qt/offroad/wifiManager.hpp b/selfdrive/ui/qt/offroad/wifiManager.hpp new file mode 100644 index 0000000000..463e3520f8 --- /dev/null +++ b/selfdrive/ui/qt/offroad/wifiManager.hpp @@ -0,0 +1,43 @@ +#pragma once +#include +#include +enum class SecurityType{OPEN, WPA, UNSUPPORTED}; + +struct Network { + QString path; + QByteArray ssid; + unsigned int strength; + bool connected; + + SecurityType security_type; +}; + +class WifiManager{ + private: + QVector seen_ssids; + QString adapter;//Path to network manager wifi-device + QDBusConnection bus = QDBusConnection::systemBus(); + + QString get_adapter(); + QList get_networks(); + void connect(QByteArray ssid, QString username, QString password, SecurityType security_type); + QString get_active_ap(); + void clear_connections(QString ssid); + void print_active_connections(); + uint get_wifi_device_state(); + QByteArray get_ap_ssid(QString network_path); + QByteArray get_property(QString network_path, QString property); + unsigned int get_ap_strength(QString network_path); + SecurityType getSecurityType(QString ssid); + + public: + bool has_adapter; + void request_scan(); + QVector seen_networks; + + explicit WifiManager(); + void refreshNetworks(); + void connect(Network ssid); + void connect(Network ssid, QString password); + void connect(Network ssid, QString username, QString password); +}; diff --git a/selfdrive/ui/qt/wifi.cc b/selfdrive/ui/qt/wifi.cc deleted file mode 100644 index 1d66c2c862..0000000000 --- a/selfdrive/ui/qt/wifi.cc +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include - -typedef QMap > Connection; -Q_DECLARE_METATYPE(Connection) - -void wifi_stuff(){ - qDBusRegisterMetaType(); - - QString nm_path = "/org/freedesktop/NetworkManager"; - QString nm_settings_path = "/org/freedesktop/NetworkManager/Settings"; - - QString nm_iface = "org.freedesktop.NetworkManager"; - QString props_iface = "org.freedesktop.DBus.Properties"; - QString nm_settings_iface = "org.freedesktop.NetworkManager.Settings"; - - QString nm_service = "org.freedesktop.NetworkManager"; - QString device_service = "org.freedesktop.NetworkManager.Device"; - - QDBusConnection bus = QDBusConnection::systemBus(); - - // Get devices - QDBusInterface nm(nm_service, nm_path, nm_iface, bus); - QDBusMessage response = nm.call("GetDevices"); - QVariant first = response.arguments().at(0); - - const QDBusArgument &args = first.value(); - args.beginArray(); - while (!args.atEnd()) { - QDBusObjectPath path; - args >> path; - - // Get device type - QDBusInterface device_props(nm_service, path.path(), props_iface, bus); - QDBusMessage response = device_props.call("Get", device_service, "DeviceType"); - QVariant first = response.arguments().at(0); - QDBusVariant dbvFirst = first.value(); - QVariant vFirst = dbvFirst.variant(); - uint device_type = vFirst.value(); - qDebug() << path.path() << device_type; - } - args.endArray(); - - - // Add connection - Connection connection; - connection["connection"]["type"] = "802-11-wireless"; - connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}'); - connection["connection"]["id"] = "Connection 1"; - - connection["802-11-wireless"]["ssid"] = QByteArray(""); - connection["802-11-wireless"]["mode"] = "infrastructure"; - - connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk"; - connection["802-11-wireless-security"]["auth-alg"] = "open"; - connection["802-11-wireless-security"]["psk"] = ""; - - connection["ipv4"]["method"] = "auto"; - connection["ipv6"]["method"] = "ignore"; - - - QDBusInterface nm_settings(nm_service, nm_settings_path, nm_settings_iface, bus); - QDBusReply result = nm_settings.call("AddConnection", QVariant::fromValue(connection)); - if (!result.isValid()) { - qDebug() << result.error().name() << result.error().message(); - } else { - qDebug() << result.value().path(); - } -}