wip
This commit is contained in:
384
selfdrive/ui/qt/network/networking.cc
Executable file
384
selfdrive/ui/qt/network/networking.cc
Executable file
@@ -0,0 +1,384 @@
|
||||
#include "selfdrive/ui/qt/network/networking.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QStyle>
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
#include "selfdrive/ui/qt/qt_window.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
|
||||
static const int ICON_WIDTH = 49;
|
||||
|
||||
// Networking functions
|
||||
|
||||
Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) {
|
||||
main_layout = new QStackedLayout(this);
|
||||
|
||||
wifi = new WifiManager(this);
|
||||
connect(wifi, &WifiManager::refreshSignal, this, &Networking::refresh);
|
||||
connect(wifi, &WifiManager::wrongPassword, this, &Networking::wrongPassword);
|
||||
|
||||
wifiScreen = new QWidget(this);
|
||||
QVBoxLayout* vlayout = new QVBoxLayout(wifiScreen);
|
||||
vlayout->setContentsMargins(20, 20, 20, 20);
|
||||
if (show_advanced) {
|
||||
QPushButton* advancedSettings = new QPushButton(tr("Advanced"));
|
||||
advancedSettings->setObjectName("advanced_btn");
|
||||
advancedSettings->setStyleSheet("margin-right: 30px;");
|
||||
advancedSettings->setFixedSize(400, 100);
|
||||
connect(advancedSettings, &QPushButton::clicked, [=]() { main_layout->setCurrentWidget(an); });
|
||||
vlayout->addSpacing(10);
|
||||
vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
|
||||
vlayout->addSpacing(10);
|
||||
}
|
||||
|
||||
wifiWidget = new WifiUI(this, wifi);
|
||||
wifiWidget->setObjectName("wifiWidget");
|
||||
connect(wifiWidget, &WifiUI::connectToNetwork, this, &Networking::connectToNetwork);
|
||||
|
||||
ScrollView *wifiScroller = new ScrollView(wifiWidget, this);
|
||||
wifiScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
vlayout->addWidget(wifiScroller, 1);
|
||||
main_layout->addWidget(wifiScreen);
|
||||
|
||||
an = new AdvancedNetworking(this, wifi);
|
||||
connect(an, &AdvancedNetworking::backPress, [=]() { main_layout->setCurrentWidget(wifiScreen); });
|
||||
connect(an, &AdvancedNetworking::requestWifiScreen, [=]() { main_layout->setCurrentWidget(wifiScreen); });
|
||||
main_layout->addWidget(an);
|
||||
|
||||
QPalette pal = palette();
|
||||
pal.setColor(QPalette::Window, QColor(0x29, 0x29, 0x29));
|
||||
setAutoFillBackground(true);
|
||||
setPalette(pal);
|
||||
|
||||
setStyleSheet(R"(
|
||||
#wifiWidget > QPushButton, #back_btn, #advanced_btn {
|
||||
font-size: 50px;
|
||||
margin: 0px;
|
||||
padding: 15px;
|
||||
border-width: 0;
|
||||
border-radius: 30px;
|
||||
color: #dddddd;
|
||||
background-color: #393939;
|
||||
}
|
||||
#back_btn:pressed, #advanced_btn:pressed {
|
||||
background-color: #4a4a4a;
|
||||
}
|
||||
)");
|
||||
main_layout->setCurrentWidget(wifiScreen);
|
||||
}
|
||||
|
||||
void Networking::refresh() {
|
||||
wifiWidget->refresh();
|
||||
an->refresh();
|
||||
}
|
||||
|
||||
void Networking::connectToNetwork(const Network n) {
|
||||
if (wifi->isKnownConnection(n.ssid)) {
|
||||
wifi->activateWifiConnection(n.ssid);
|
||||
} else if (n.security_type == SecurityType::OPEN) {
|
||||
wifi->connect(n);
|
||||
} else if (n.security_type == SecurityType::WPA) {
|
||||
QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
||||
if (!pass.isEmpty()) {
|
||||
wifi->connect(n, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Networking::wrongPassword(const QString &ssid) {
|
||||
if (wifi->seenNetworks.contains(ssid)) {
|
||||
const Network &n = wifi->seenNetworks.value(ssid);
|
||||
QString pass = InputDialog::getText(tr("Wrong password"), this, tr("for \"%1\"").arg(QString::fromUtf8(n.ssid)), true, 8);
|
||||
if (!pass.isEmpty()) {
|
||||
wifi->connect(n, pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Networking::showEvent(QShowEvent *event) {
|
||||
wifi->start();
|
||||
}
|
||||
|
||||
void Networking::hideEvent(QHideEvent *event) {
|
||||
main_layout->setCurrentWidget(wifiScreen);
|
||||
wifi->stop();
|
||||
}
|
||||
|
||||
// AdvancedNetworking functions
|
||||
|
||||
AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi) {
|
||||
|
||||
QVBoxLayout* main_layout = new QVBoxLayout(this);
|
||||
main_layout->setMargin(40);
|
||||
main_layout->setSpacing(20);
|
||||
|
||||
// Back button
|
||||
QPushButton* back = new QPushButton(tr("Back"));
|
||||
back->setObjectName("back_btn");
|
||||
back->setFixedSize(400, 100);
|
||||
connect(back, &QPushButton::clicked, [=]() { emit backPress(); });
|
||||
main_layout->addWidget(back, 0, Qt::AlignLeft);
|
||||
|
||||
ListWidget *list = new ListWidget(this);
|
||||
// Enable tethering layout
|
||||
tetheringToggle = new ToggleControl(tr("Enable Tethering"), "", "", wifi->isTetheringEnabled());
|
||||
list->addItem(tetheringToggle);
|
||||
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
|
||||
if (params.getBool("TetheringEnabled")) {
|
||||
tetheringToggle->setVisualOn();
|
||||
uiState()->scene.tethering_enabled = true;
|
||||
}
|
||||
|
||||
// Change tethering password
|
||||
ButtonControl *editPasswordButton = new ButtonControl(tr("Tethering Password"), tr("EDIT"));
|
||||
connect(editPasswordButton, &ButtonControl::clicked, [=]() {
|
||||
QString pass = InputDialog::getText(tr("Enter new tethering password"), this, "", true, 8, wifi->getTetheringPassword());
|
||||
if (!pass.isEmpty()) {
|
||||
wifi->changeTetheringPassword(pass);
|
||||
}
|
||||
});
|
||||
list->addItem(editPasswordButton);
|
||||
|
||||
// IP address
|
||||
ipLabel = new LabelControl(tr("IP Address"), wifi->ipv4_address);
|
||||
list->addItem(ipLabel);
|
||||
|
||||
// SSH keys
|
||||
list->addItem(new SshToggle());
|
||||
list->addItem(new SshControl());
|
||||
|
||||
// Roaming toggle
|
||||
const bool roamingEnabled = params.getBool("GsmRoaming");
|
||||
roamingToggle = new ToggleControl(tr("Enable Roaming"), "", "", roamingEnabled);
|
||||
QObject::connect(roamingToggle, &ToggleControl::toggleFlipped, [=](bool state) {
|
||||
params.putBool("GsmRoaming", state);
|
||||
wifi->updateGsmSettings(state, QString::fromStdString(params.get("GsmApn")), params.getBool("GsmMetered"));
|
||||
});
|
||||
list->addItem(roamingToggle);
|
||||
|
||||
// APN settings
|
||||
editApnButton = new ButtonControl(tr("APN Setting"), tr("EDIT"));
|
||||
connect(editApnButton, &ButtonControl::clicked, [=]() {
|
||||
const QString cur_apn = QString::fromStdString(params.get("GsmApn"));
|
||||
QString apn = InputDialog::getText(tr("Enter APN"), this, tr("leave blank for automatic configuration"), false, -1, cur_apn).trimmed();
|
||||
|
||||
if (apn.isEmpty()) {
|
||||
params.remove("GsmApn");
|
||||
} else {
|
||||
params.put("GsmApn", apn.toStdString());
|
||||
}
|
||||
wifi->updateGsmSettings(params.getBool("GsmRoaming"), apn, params.getBool("GsmMetered"));
|
||||
});
|
||||
list->addItem(editApnButton);
|
||||
|
||||
// Metered toggle
|
||||
const bool metered = params.getBool("GsmMetered");
|
||||
meteredToggle = new ToggleControl(tr("Cellular Metered"), tr("Prevent large data uploads when on a metered connection"), "", metered);
|
||||
QObject::connect(meteredToggle, &SshToggle::toggleFlipped, [=](bool state) {
|
||||
params.putBool("GsmMetered", state);
|
||||
wifi->updateGsmSettings(params.getBool("GsmRoaming"), QString::fromStdString(params.get("GsmApn")), state);
|
||||
});
|
||||
list->addItem(meteredToggle);
|
||||
|
||||
// Hidden Network
|
||||
hiddenNetworkButton = new ButtonControl(tr("Hidden Network"), tr("CONNECT"));
|
||||
connect(hiddenNetworkButton, &ButtonControl::clicked, [=]() {
|
||||
QString ssid = InputDialog::getText(tr("Enter SSID"), this, "", false, 1);
|
||||
if (!ssid.isEmpty()) {
|
||||
QString pass = InputDialog::getText(tr("Enter password"), this, tr("for \"%1\"").arg(ssid), true, -1);
|
||||
Network hidden_network;
|
||||
hidden_network.ssid = ssid.toUtf8();
|
||||
if (!pass.isEmpty()) {
|
||||
hidden_network.security_type = SecurityType::WPA;
|
||||
wifi->connect(hidden_network, pass);
|
||||
} else {
|
||||
wifi->connect(hidden_network);
|
||||
}
|
||||
emit requestWifiScreen();
|
||||
}
|
||||
});
|
||||
list->addItem(hiddenNetworkButton);
|
||||
|
||||
// Set initial config
|
||||
wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered);
|
||||
|
||||
connect(uiState(), &UIState::primeTypeChanged, this, [=](PrimeType prime_type) {
|
||||
bool gsmVisible = prime_type == PrimeType::NONE || prime_type == PrimeType::LITE;
|
||||
roamingToggle->setVisible(gsmVisible);
|
||||
editApnButton->setVisible(gsmVisible);
|
||||
meteredToggle->setVisible(gsmVisible);
|
||||
});
|
||||
|
||||
main_layout->addWidget(new ScrollView(list, this));
|
||||
main_layout->addStretch(1);
|
||||
}
|
||||
|
||||
void AdvancedNetworking::refresh() {
|
||||
ipLabel->setText(wifi->ipv4_address);
|
||||
tetheringToggle->setEnabled(true);
|
||||
update();
|
||||
}
|
||||
|
||||
void AdvancedNetworking::toggleTethering(bool enabled) {
|
||||
wifi->setTetheringEnabled(enabled);
|
||||
tetheringToggle->setEnabled(false);
|
||||
params.putBool("TetheringEnabled", enabled);
|
||||
uiState()->scene.tethering_enabled = enabled;
|
||||
}
|
||||
|
||||
// WifiUI functions
|
||||
|
||||
WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) {
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(0, 0, 0, 0);
|
||||
main_layout->setSpacing(0);
|
||||
|
||||
// load imgs
|
||||
for (const auto &s : {"low", "medium", "high", "full"}) {
|
||||
QPixmap pix(ASSET_PATH + "/offroad/icon_wifi_strength_" + s + ".svg");
|
||||
strengths.push_back(pix.scaledToHeight(68, Qt::SmoothTransformation));
|
||||
}
|
||||
lock = QPixmap(ASSET_PATH + "offroad/icon_lock_closed.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation);
|
||||
checkmark = QPixmap(ASSET_PATH + "offroad/icon_checkmark.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation);
|
||||
circled_slash = QPixmap(ASSET_PATH + "img_circled_slash.svg").scaledToWidth(ICON_WIDTH, Qt::SmoothTransformation);
|
||||
|
||||
scanningLabel = new QLabel(tr("Scanning for networks..."));
|
||||
scanningLabel->setStyleSheet("font-size: 65px;");
|
||||
main_layout->addWidget(scanningLabel, 0, Qt::AlignCenter);
|
||||
|
||||
wifi_list_widget = new ListWidget(this);
|
||||
wifi_list_widget->setVisible(false);
|
||||
main_layout->addWidget(wifi_list_widget);
|
||||
|
||||
setStyleSheet(R"(
|
||||
QScrollBar::handle:vertical {
|
||||
min-height: 0px;
|
||||
border-radius: 4px;
|
||||
background-color: #8A8A8A;
|
||||
}
|
||||
#forgetBtn {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #292929;
|
||||
background-color: #BDBDBD;
|
||||
border-width: 1px solid #828282;
|
||||
border-radius: 5px;
|
||||
padding: 40px;
|
||||
padding-bottom: 16px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
#forgetBtn:pressed {
|
||||
background-color: #828282;
|
||||
}
|
||||
#connecting {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
border-radius: 0;
|
||||
padding: 27px;
|
||||
padding-left: 43px;
|
||||
padding-right: 43px;
|
||||
background-color: black;
|
||||
}
|
||||
#ssidLabel {
|
||||
text-align: left;
|
||||
border: none;
|
||||
padding-top: 50px;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
#ssidLabel:disabled {
|
||||
color: #696969;
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
void WifiUI::refresh() {
|
||||
bool is_empty = wifi->seenNetworks.isEmpty();
|
||||
scanningLabel->setVisible(is_empty);
|
||||
wifi_list_widget->setVisible(!is_empty);
|
||||
if (is_empty) return;
|
||||
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
const bool is_tethering_enabled = wifi->isTetheringEnabled();
|
||||
QList<Network> sortedNetworks = wifi->seenNetworks.values();
|
||||
std::sort(sortedNetworks.begin(), sortedNetworks.end(), compare_by_strength);
|
||||
|
||||
int n = 0;
|
||||
for (Network &network : sortedNetworks) {
|
||||
QPixmap status_icon;
|
||||
if (network.connected == ConnectedType::CONNECTED) {
|
||||
status_icon = checkmark;
|
||||
} else if (network.security_type == SecurityType::UNSUPPORTED) {
|
||||
status_icon = circled_slash;
|
||||
} else if (network.security_type == SecurityType::WPA) {
|
||||
status_icon = lock;
|
||||
}
|
||||
bool show_forget_btn = wifi->isKnownConnection(network.ssid) && !is_tethering_enabled;
|
||||
QPixmap strength = strengths[strengthLevel(network.strength)];
|
||||
|
||||
auto item = getItem(n++);
|
||||
item->setItem(network, status_icon, show_forget_btn, strength);
|
||||
item->setVisible(true);
|
||||
}
|
||||
for (; n < wifi_items.size(); ++n) wifi_items[n]->setVisible(false);
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
WifiItem *WifiUI::getItem(int n) {
|
||||
auto item = n < wifi_items.size() ? wifi_items[n] : wifi_items.emplace_back(new WifiItem(tr("CONNECTING..."), tr("FORGET")));
|
||||
if (!item->parentWidget()) {
|
||||
QObject::connect(item, &WifiItem::connectToNetwork, this, &WifiUI::connectToNetwork);
|
||||
QObject::connect(item, &WifiItem::forgotNetwork, [this](const Network n) {
|
||||
if (ConfirmationDialog::confirm(tr("Forget Wi-Fi Network \"%1\"?").arg(QString::fromUtf8(n.ssid)), tr("Forget"), this))
|
||||
wifi->forgetConnection(n.ssid);
|
||||
});
|
||||
wifi_list_widget->addItem(item);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
// WifiItem
|
||||
|
||||
WifiItem::WifiItem(const QString &connecting_text, const QString &forget_text, QWidget *parent) : QWidget(parent) {
|
||||
QHBoxLayout *hlayout = new QHBoxLayout(this);
|
||||
hlayout->setContentsMargins(44, 0, 73, 0);
|
||||
hlayout->setSpacing(50);
|
||||
|
||||
hlayout->addWidget(ssidLabel = new ElidedLabel());
|
||||
ssidLabel->setObjectName("ssidLabel");
|
||||
ssidLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
hlayout->addWidget(connecting = new QPushButton(connecting_text), 0, Qt::AlignRight);
|
||||
connecting->setObjectName("connecting");
|
||||
hlayout->addWidget(forgetBtn = new QPushButton(forget_text), 0, Qt::AlignRight);
|
||||
forgetBtn->setObjectName("forgetBtn");
|
||||
hlayout->addWidget(iconLabel = new QLabel(), 0, Qt::AlignRight);
|
||||
hlayout->addWidget(strengthLabel = new QLabel(), 0, Qt::AlignRight);
|
||||
|
||||
iconLabel->setFixedWidth(ICON_WIDTH);
|
||||
QObject::connect(forgetBtn, &QPushButton::clicked, [this]() { emit forgotNetwork(network); });
|
||||
QObject::connect(ssidLabel, &ElidedLabel::clicked, [this]() {
|
||||
if (network.connected == ConnectedType::DISCONNECTED) emit connectToNetwork(network);
|
||||
});
|
||||
}
|
||||
|
||||
void WifiItem::setItem(const Network &n, const QPixmap &status_icon, bool show_forget_btn, const QPixmap &strength_icon) {
|
||||
network = n;
|
||||
|
||||
ssidLabel->setText(n.ssid);
|
||||
ssidLabel->setEnabled(n.security_type != SecurityType::UNSUPPORTED);
|
||||
ssidLabel->setFont(InterFont(55, network.connected == ConnectedType::DISCONNECTED ? QFont::Normal : QFont::Bold));
|
||||
|
||||
connecting->setVisible(n.connected == ConnectedType::CONNECTING);
|
||||
forgetBtn->setVisible(show_forget_btn);
|
||||
|
||||
iconLabel->setPixmap(status_icon);
|
||||
strengthLabel->setPixmap(strength_icon);
|
||||
}
|
||||
101
selfdrive/ui/qt/network/networking.h
Executable file
101
selfdrive/ui/qt/network/networking.h
Executable file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "selfdrive/ui/qt/network/wifi_manager.h"
|
||||
#include "selfdrive/ui/qt/widgets/input.h"
|
||||
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
|
||||
#include "selfdrive/ui/qt/widgets/toggle.h"
|
||||
|
||||
class WifiItem : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WifiItem(const QString &connecting_text, const QString &forget_text, QWidget* parent = nullptr);
|
||||
void setItem(const Network& n, const QPixmap &icon, bool show_forget_btn, const QPixmap &strength);
|
||||
|
||||
signals:
|
||||
// Cannot pass Network by reference. it may change after the signal is sent.
|
||||
void connectToNetwork(const Network n);
|
||||
void forgotNetwork(const Network n);
|
||||
|
||||
protected:
|
||||
ElidedLabel* ssidLabel;
|
||||
QPushButton* connecting;
|
||||
QPushButton* forgetBtn;
|
||||
QLabel* iconLabel;
|
||||
QLabel* strengthLabel;
|
||||
Network network;
|
||||
};
|
||||
|
||||
class WifiUI : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WifiUI(QWidget *parent = 0, WifiManager* wifi = 0);
|
||||
|
||||
private:
|
||||
WifiItem *getItem(int n);
|
||||
|
||||
WifiManager *wifi = nullptr;
|
||||
QLabel *scanningLabel = nullptr;
|
||||
QPixmap lock;
|
||||
QPixmap checkmark;
|
||||
QPixmap circled_slash;
|
||||
QVector<QPixmap> strengths;
|
||||
ListWidget *wifi_list_widget = nullptr;
|
||||
std::vector<WifiItem*> wifi_items;
|
||||
|
||||
signals:
|
||||
void connectToNetwork(const Network n);
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
};
|
||||
|
||||
class AdvancedNetworking : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AdvancedNetworking(QWidget* parent = 0, WifiManager* wifi = 0);
|
||||
|
||||
private:
|
||||
LabelControl* ipLabel;
|
||||
ToggleControl* tetheringToggle;
|
||||
ToggleControl* roamingToggle;
|
||||
ButtonControl* editApnButton;
|
||||
ButtonControl* hiddenNetworkButton;
|
||||
ToggleControl* meteredToggle;
|
||||
WifiManager* wifi = nullptr;
|
||||
Params params;
|
||||
|
||||
signals:
|
||||
void backPress();
|
||||
void requestWifiScreen();
|
||||
|
||||
public slots:
|
||||
void toggleTethering(bool enabled);
|
||||
void refresh();
|
||||
};
|
||||
|
||||
class Networking : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Networking(QWidget* parent = 0, bool show_advanced = true);
|
||||
WifiManager* wifi = nullptr;
|
||||
|
||||
private:
|
||||
QStackedLayout* main_layout = nullptr;
|
||||
QWidget* wifiScreen = nullptr;
|
||||
AdvancedNetworking* an = nullptr;
|
||||
WifiUI* wifiWidget;
|
||||
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
|
||||
private slots:
|
||||
void connectToNetwork(const Network n);
|
||||
void wrongPassword(const QString &ssid);
|
||||
};
|
||||
47
selfdrive/ui/qt/network/networkmanager.h
Executable file
47
selfdrive/ui/qt/network/networkmanager.h
Executable file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* We are using a NetworkManager DBUS API : https://developer.gnome.org/NetworkManager/1.26/spec.html
|
||||
* */
|
||||
|
||||
// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApFlags
|
||||
const int NM_802_11_AP_FLAGS_NONE = 0x00000000;
|
||||
const int NM_802_11_AP_FLAGS_PRIVACY = 0x00000001;
|
||||
const int NM_802_11_AP_FLAGS_WPS = 0x00000002;
|
||||
|
||||
// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApSecurityFlags
|
||||
const int NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001;
|
||||
const int NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002;
|
||||
const int NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010;
|
||||
const int NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020;
|
||||
const int NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100;
|
||||
const int NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200;
|
||||
|
||||
const QString NM_DBUS_PATH = "/org/freedesktop/NetworkManager";
|
||||
const QString NM_DBUS_PATH_SETTINGS = "/org/freedesktop/NetworkManager/Settings";
|
||||
|
||||
const QString NM_DBUS_INTERFACE = "org.freedesktop.NetworkManager";
|
||||
const QString NM_DBUS_INTERFACE_PROPERTIES = "org.freedesktop.DBus.Properties";
|
||||
const QString NM_DBUS_INTERFACE_SETTINGS = "org.freedesktop.NetworkManager.Settings";
|
||||
const QString NM_DBUS_INTERFACE_SETTINGS_CONNECTION = "org.freedesktop.NetworkManager.Settings.Connection";
|
||||
const QString NM_DBUS_INTERFACE_DEVICE = "org.freedesktop.NetworkManager.Device";
|
||||
const QString NM_DBUS_INTERFACE_DEVICE_WIRELESS = "org.freedesktop.NetworkManager.Device.Wireless";
|
||||
const QString NM_DBUS_INTERFACE_ACCESS_POINT = "org.freedesktop.NetworkManager.AccessPoint";
|
||||
const QString NM_DBUS_INTERFACE_ACTIVE_CONNECTION = "org.freedesktop.NetworkManager.Connection.Active";
|
||||
const QString NM_DBUS_INTERFACE_IP4_CONFIG = "org.freedesktop.NetworkManager.IP4Config";
|
||||
|
||||
const QString NM_DBUS_SERVICE = "org.freedesktop.NetworkManager";
|
||||
|
||||
const int NM_DEVICE_STATE_ACTIVATED = 100;
|
||||
const int NM_DEVICE_STATE_NEED_AUTH = 60;
|
||||
const int NM_DEVICE_TYPE_WIFI = 2;
|
||||
const int NM_DEVICE_TYPE_MODEM = 8;
|
||||
const int NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8;
|
||||
const int DBUS_TIMEOUT = 100;
|
||||
|
||||
// https://developer-old.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NMMetered
|
||||
const int NM_METERED_UNKNOWN = 0;
|
||||
const int NM_METERED_YES = 1;
|
||||
const int NM_METERED_NO = 2;
|
||||
const int NM_METERED_GUESS_YES = 3;
|
||||
const int NM_METERED_GUESS_NO = 4;
|
||||
493
selfdrive/ui/qt/network/wifi_manager.cc
Executable file
493
selfdrive/ui/qt/network/wifi_manager.cc
Executable file
@@ -0,0 +1,493 @@
|
||||
#include "selfdrive/ui/qt/network/wifi_manager.h"
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
#include "selfdrive/ui/qt/widgets/prime.h"
|
||||
|
||||
#include "common/params.h"
|
||||
#include "common/swaglog.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
|
||||
bool compare_by_strength(const Network &a, const Network &b) {
|
||||
return std::tuple(a.connected, strengthLevel(a.strength), b.ssid) >
|
||||
std::tuple(b.connected, strengthLevel(b.strength), a.ssid);
|
||||
}
|
||||
|
||||
template <typename T = QDBusMessage, typename... Args>
|
||||
T call(const QString &path, const QString &interface, const QString &method, Args &&...args) {
|
||||
QDBusInterface nm = QDBusInterface(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus());
|
||||
nm.setTimeout(DBUS_TIMEOUT);
|
||||
QDBusMessage response = nm.call(method, args...);
|
||||
if constexpr (std::is_same_v<T, QDBusMessage>) {
|
||||
return response;
|
||||
} else if (response.arguments().count() >= 1) {
|
||||
QVariant vFirst = response.arguments().at(0).value<QDBusVariant>().variant();
|
||||
if (vFirst.canConvert<T>()) {
|
||||
return vFirst.value<T>();
|
||||
}
|
||||
QDebug critical = qCritical();
|
||||
critical << "Variant unpacking failure :" << method << ',';
|
||||
(critical << ... << args);
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
QDBusPendingCall asyncCall(const QString &path, const QString &interface, const QString &method, Args &&...args) {
|
||||
QDBusInterface nm = QDBusInterface(NM_DBUS_SERVICE, path, interface, QDBusConnection::systemBus());
|
||||
return nm.asyncCall(method, args...);
|
||||
}
|
||||
|
||||
bool emptyPath(const QString &path) {
|
||||
return path == "" || path == "/";
|
||||
}
|
||||
|
||||
WifiManager::WifiManager(QObject *parent) : QObject(parent) {
|
||||
qDBusRegisterMetaType<Connection>();
|
||||
qDBusRegisterMetaType<IpConfig>();
|
||||
|
||||
// Set tethering ssid as "weedle" + first 4 characters of a dongle id
|
||||
tethering_ssid = "weedle";
|
||||
if (auto dongle_id = getDongleId()) {
|
||||
tethering_ssid += "-" + dongle_id->left(4);
|
||||
}
|
||||
|
||||
adapter = getAdapter();
|
||||
if (!adapter.isEmpty()) {
|
||||
setup();
|
||||
} else {
|
||||
QDBusConnection::systemBus().connect(NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_INTERFACE, "DeviceAdded", this, SLOT(deviceAdded(QDBusObjectPath)));
|
||||
}
|
||||
|
||||
timer.callOnTimeout(this, &WifiManager::requestScan);
|
||||
|
||||
initConnections();
|
||||
}
|
||||
|
||||
void WifiManager::setup() {
|
||||
auto bus = QDBusConnection::systemBus();
|
||||
bus.connect(NM_DBUS_SERVICE, adapter, NM_DBUS_INTERFACE_DEVICE, "StateChanged", this, SLOT(stateChange(unsigned int, unsigned int, unsigned int)));
|
||||
bus.connect(NM_DBUS_SERVICE, adapter, NM_DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", this, SLOT(propertyChange(QString, QVariantMap, QStringList)));
|
||||
|
||||
bus.connect(NM_DBUS_SERVICE, NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "ConnectionRemoved", this, SLOT(connectionRemoved(QDBusObjectPath)));
|
||||
bus.connect(NM_DBUS_SERVICE, NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "NewConnection", this, SLOT(newConnection(QDBusObjectPath)));
|
||||
|
||||
raw_adapter_state = call<uint>(adapter, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_DEVICE, "State");
|
||||
activeAp = call<QDBusObjectPath>(adapter, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_DEVICE_WIRELESS, "ActiveAccessPoint").path();
|
||||
|
||||
requestScan();
|
||||
}
|
||||
|
||||
void WifiManager::start() {
|
||||
timer.start(5000);
|
||||
refreshNetworks();
|
||||
}
|
||||
|
||||
void WifiManager::stop() {
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
void WifiManager::refreshNetworks() {
|
||||
if (adapter.isEmpty() || !timer.isActive()) return;
|
||||
|
||||
QDBusPendingCall pending_call = asyncCall(adapter, NM_DBUS_INTERFACE_DEVICE_WIRELESS, "GetAllAccessPoints");
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending_call);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &WifiManager::refreshFinished);
|
||||
}
|
||||
|
||||
void WifiManager::refreshFinished(QDBusPendingCallWatcher *watcher) {
|
||||
ipv4_address = getIp4Address();
|
||||
seenNetworks.clear();
|
||||
|
||||
const QDBusReply<QList<QDBusObjectPath>> wather_reply = *watcher;
|
||||
for (const QDBusObjectPath &path : wather_reply.value()) {
|
||||
QDBusReply<QVariantMap> replay = call(path.path(), NM_DBUS_INTERFACE_PROPERTIES, "GetAll", NM_DBUS_INTERFACE_ACCESS_POINT);
|
||||
auto properties = replay.value();
|
||||
|
||||
const QByteArray ssid = properties["Ssid"].toByteArray();
|
||||
if (ssid.isEmpty()) continue;
|
||||
|
||||
// May be multiple access points for each SSID.
|
||||
// Use first for ssid and security type, then update connected status and strength using all
|
||||
if (!seenNetworks.contains(ssid)) {
|
||||
seenNetworks[ssid] = {ssid, 0U, ConnectedType::DISCONNECTED, getSecurityType(properties)};
|
||||
}
|
||||
|
||||
if (path.path() == activeAp) {
|
||||
seenNetworks[ssid].connected = (ssid == connecting_to_network) ? ConnectedType::CONNECTING : ConnectedType::CONNECTED;
|
||||
}
|
||||
|
||||
uint32_t strength = properties["Strength"].toUInt();
|
||||
if (seenNetworks[ssid].strength < strength) {
|
||||
seenNetworks[ssid].strength = strength;
|
||||
}
|
||||
}
|
||||
|
||||
emit refreshSignal();
|
||||
watcher->deleteLater();
|
||||
}
|
||||
|
||||
QString WifiManager::getIp4Address() {
|
||||
if (raw_adapter_state != NM_DEVICE_STATE_ACTIVATED) return "";
|
||||
|
||||
for (const auto &p : getActiveConnections()) {
|
||||
QString type = call<QString>(p.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type");
|
||||
if (type == "802-11-wireless") {
|
||||
auto ip4config = call<QDBusObjectPath>(p.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Ip4Config");
|
||||
const auto &arr = call<QDBusArgument>(ip4config.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_IP4_CONFIG, "AddressData");
|
||||
QVariantMap path;
|
||||
arr.beginArray();
|
||||
while (!arr.atEnd()) {
|
||||
arr >> path;
|
||||
arr.endArray();
|
||||
return path.value("address").value<QString>();
|
||||
}
|
||||
arr.endArray();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
SecurityType WifiManager::getSecurityType(const QVariantMap &properties) {
|
||||
int sflag = properties["Flags"].toUInt();
|
||||
int wpaflag = properties["WpaFlags"].toUInt();
|
||||
int rsnflag = properties["RsnFlags"].toUInt();
|
||||
int wpa_props = wpaflag | rsnflag;
|
||||
|
||||
// obtained by looking at flags of networks in the office as reported by an Android phone
|
||||
const int supports_wpa = NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104 | NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104 | NM_802_11_AP_SEC_KEY_MGMT_PSK;
|
||||
|
||||
if ((sflag == NM_802_11_AP_FLAGS_NONE) || ((sflag & NM_802_11_AP_FLAGS_WPS) && !(wpa_props & supports_wpa))) {
|
||||
return SecurityType::OPEN;
|
||||
} else if ((sflag & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_props & supports_wpa) && !(wpa_props & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
|
||||
return SecurityType::WPA;
|
||||
} else {
|
||||
LOGW("Unsupported network! sflag: %d, wpaflag: %d, rsnflag: %d", sflag, wpaflag, rsnflag);
|
||||
return SecurityType::UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
void WifiManager::connect(const Network &n, const QString &password, const QString &username) {
|
||||
setCurrentConnecting(n.ssid);
|
||||
forgetConnection(n.ssid); // Clear all connections that may already exist to the network we are connecting
|
||||
Connection connection;
|
||||
connection["connection"]["type"] = "802-11-wireless";
|
||||
connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}');
|
||||
connection["connection"]["id"] = "openpilot connection " + QString::fromStdString(n.ssid.toStdString());
|
||||
connection["connection"]["autoconnect-retries"] = 0;
|
||||
|
||||
connection["802-11-wireless"]["ssid"] = n.ssid;
|
||||
connection["802-11-wireless"]["mode"] = "infrastructure";
|
||||
|
||||
if (n.security_type == SecurityType::WPA) {
|
||||
connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk";
|
||||
connection["802-11-wireless-security"]["auth-alg"] = "open";
|
||||
connection["802-11-wireless-security"]["psk"] = password;
|
||||
}
|
||||
|
||||
connection["ipv4"]["method"] = "auto";
|
||||
connection["ipv4"]["dns-priority"] = 600;
|
||||
connection["ipv6"]["method"] = "ignore";
|
||||
|
||||
call(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "AddConnection", QVariant::fromValue(connection));
|
||||
}
|
||||
|
||||
void WifiManager::deactivateConnectionBySsid(const QString &ssid) {
|
||||
for (QDBusObjectPath active_connection : getActiveConnections()) {
|
||||
auto pth = call<QDBusObjectPath>(active_connection.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "SpecificObject");
|
||||
if (!emptyPath(pth.path())) {
|
||||
QString Ssid = get_property(pth.path(), "Ssid");
|
||||
if (Ssid == ssid) {
|
||||
deactivateConnection(active_connection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WifiManager::deactivateConnection(const QDBusObjectPath &path) {
|
||||
asyncCall(NM_DBUS_PATH, NM_DBUS_INTERFACE, "DeactivateConnection", QVariant::fromValue(path));
|
||||
}
|
||||
|
||||
QVector<QDBusObjectPath> WifiManager::getActiveConnections() {
|
||||
QVector<QDBusObjectPath> conns;
|
||||
QDBusObjectPath path;
|
||||
const QDBusArgument &arr = call<QDBusArgument>(NM_DBUS_PATH, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE, "ActiveConnections");
|
||||
arr.beginArray();
|
||||
while (!arr.atEnd()) {
|
||||
arr >> path;
|
||||
conns.push_back(path);
|
||||
}
|
||||
arr.endArray();
|
||||
return conns;
|
||||
}
|
||||
|
||||
bool WifiManager::isKnownConnection(const QString &ssid) {
|
||||
return !getConnectionPath(ssid).path().isEmpty();
|
||||
}
|
||||
|
||||
void WifiManager::forgetConnection(const QString &ssid) {
|
||||
const QDBusObjectPath &path = getConnectionPath(ssid);
|
||||
if (!path.path().isEmpty()) {
|
||||
call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Delete");
|
||||
}
|
||||
}
|
||||
|
||||
void WifiManager::setCurrentConnecting(const QString &ssid) {
|
||||
connecting_to_network = ssid;
|
||||
for (auto &network : seenNetworks) {
|
||||
network.connected = (network.ssid == ssid) ? ConnectedType::CONNECTING : ConnectedType::DISCONNECTED;
|
||||
}
|
||||
emit refreshSignal();
|
||||
}
|
||||
|
||||
uint WifiManager::getAdapterType(const QDBusObjectPath &path) {
|
||||
return call<uint>(path.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_DEVICE, "DeviceType");
|
||||
}
|
||||
|
||||
void WifiManager::requestScan() {
|
||||
if (!adapter.isEmpty()) {
|
||||
asyncCall(adapter, NM_DBUS_INTERFACE_DEVICE_WIRELESS, "RequestScan", QVariantMap());
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray WifiManager::get_property(const QString &network_path , const QString &property) {
|
||||
return call<QByteArray>(network_path, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACCESS_POINT, property);
|
||||
}
|
||||
|
||||
QString WifiManager::getAdapter(const uint adapter_type) {
|
||||
QDBusReply<QList<QDBusObjectPath>> response = call(NM_DBUS_PATH, NM_DBUS_INTERFACE, "GetDevices");
|
||||
for (const QDBusObjectPath &path : response.value()) {
|
||||
if (getAdapterType(path) == adapter_type) {
|
||||
return path.path();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void WifiManager::stateChange(unsigned int new_state, unsigned int previous_state, unsigned int change_reason) {
|
||||
raw_adapter_state = new_state;
|
||||
if (new_state == NM_DEVICE_STATE_NEED_AUTH && change_reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT && !connecting_to_network.isEmpty()) {
|
||||
forgetConnection(connecting_to_network);
|
||||
emit wrongPassword(connecting_to_network);
|
||||
} else if (new_state == NM_DEVICE_STATE_ACTIVATED) {
|
||||
connecting_to_network = "";
|
||||
refreshNetworks();
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Device.Wireless.html
|
||||
void WifiManager::propertyChange(const QString &interface, const QVariantMap &props, const QStringList &invalidated_props) {
|
||||
if (interface == NM_DBUS_INTERFACE_DEVICE_WIRELESS && props.contains("LastScan")) {
|
||||
refreshNetworks();
|
||||
} else if (interface == NM_DBUS_INTERFACE_DEVICE_WIRELESS && props.contains("ActiveAccessPoint")) {
|
||||
activeAp = props.value("ActiveAccessPoint").value<QDBusObjectPath>().path();
|
||||
}
|
||||
}
|
||||
|
||||
void WifiManager::deviceAdded(const QDBusObjectPath &path) {
|
||||
if (getAdapterType(path) == NM_DEVICE_TYPE_WIFI && emptyPath(adapter)) {
|
||||
adapter = path.path();
|
||||
setup();
|
||||
}
|
||||
}
|
||||
|
||||
void WifiManager::connectionRemoved(const QDBusObjectPath &path) {
|
||||
knownConnections.remove(path);
|
||||
}
|
||||
|
||||
void WifiManager::newConnection(const QDBusObjectPath &path) {
|
||||
Connection settings = getConnectionSettings(path);
|
||||
if (settings.value("connection").value("type") == "802-11-wireless") {
|
||||
knownConnections[path] = settings.value("802-11-wireless").value("ssid").toString();
|
||||
if (knownConnections[path] != tethering_ssid) {
|
||||
activateWifiConnection(knownConnections[path]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDBusObjectPath WifiManager::getConnectionPath(const QString &ssid) {
|
||||
return knownConnections.key(ssid);
|
||||
}
|
||||
|
||||
Connection WifiManager::getConnectionSettings(const QDBusObjectPath &path) {
|
||||
return QDBusReply<Connection>(call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "GetSettings")).value();
|
||||
}
|
||||
|
||||
void WifiManager::initConnections() {
|
||||
const QDBusReply<QList<QDBusObjectPath>> response = call(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "ListConnections");
|
||||
for (const QDBusObjectPath &path : response.value()) {
|
||||
const Connection settings = getConnectionSettings(path);
|
||||
if (settings.value("connection").value("type") == "802-11-wireless") {
|
||||
knownConnections[path] = settings.value("802-11-wireless").value("ssid").toString();
|
||||
} else if (settings.value("connection").value("id") == "lte") {
|
||||
lteConnectionPath = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<QDBusPendingCall> WifiManager::activateWifiConnection(const QString &ssid) {
|
||||
const QDBusObjectPath &path = getConnectionPath(ssid);
|
||||
if (!path.path().isEmpty()) {
|
||||
setCurrentConnecting(ssid);
|
||||
return asyncCall(NM_DBUS_PATH, NM_DBUS_INTERFACE, "ActivateConnection", QVariant::fromValue(path), QVariant::fromValue(QDBusObjectPath(adapter)), QVariant::fromValue(QDBusObjectPath("/")));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void WifiManager::activateModemConnection(const QDBusObjectPath &path) {
|
||||
QString modem = getAdapter(NM_DEVICE_TYPE_MODEM);
|
||||
if (!path.path().isEmpty() && !modem.isEmpty()) {
|
||||
asyncCall(NM_DBUS_PATH, NM_DBUS_INTERFACE, "ActivateConnection", QVariant::fromValue(path), QVariant::fromValue(QDBusObjectPath(modem)), QVariant::fromValue(QDBusObjectPath("/")));
|
||||
}
|
||||
}
|
||||
|
||||
// function matches tici/hardware.py
|
||||
NetworkType WifiManager::currentNetworkType() {
|
||||
auto primary_conn = call<QDBusObjectPath>(NM_DBUS_PATH, NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE, "PrimaryConnection");
|
||||
auto primary_type = call<QString>(primary_conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type");
|
||||
|
||||
if (primary_type == "802-3-ethernet") {
|
||||
return NetworkType::ETHERNET;
|
||||
} else if (primary_type == "802-11-wireless" && !isTetheringEnabled()) {
|
||||
return NetworkType::WIFI;
|
||||
} else {
|
||||
for (const QDBusObjectPath &conn : getActiveConnections()) {
|
||||
auto type = call<QString>(conn.path(), NM_DBUS_INTERFACE_PROPERTIES, "Get", NM_DBUS_INTERFACE_ACTIVE_CONNECTION, "Type");
|
||||
if (type == "gsm") {
|
||||
return NetworkType::CELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NetworkType::NONE;
|
||||
}
|
||||
|
||||
void WifiManager::updateGsmSettings(bool roaming, QString apn, bool metered) {
|
||||
if (!lteConnectionPath.path().isEmpty()) {
|
||||
bool changes = false;
|
||||
bool auto_config = apn.isEmpty();
|
||||
Connection settings = getConnectionSettings(lteConnectionPath);
|
||||
if (settings.value("gsm").value("auto-config").toBool() != auto_config) {
|
||||
qWarning() << "Changing gsm.auto-config to" << auto_config;
|
||||
settings["gsm"]["auto-config"] = auto_config;
|
||||
changes = true;
|
||||
}
|
||||
|
||||
if (settings.value("gsm").value("apn").toString() != apn) {
|
||||
qWarning() << "Changing gsm.apn to" << apn;
|
||||
settings["gsm"]["apn"] = apn;
|
||||
changes = true;
|
||||
}
|
||||
|
||||
if (settings.value("gsm").value("home-only").toBool() == roaming) {
|
||||
qWarning() << "Changing gsm.home-only to" << !roaming;
|
||||
settings["gsm"]["home-only"] = !roaming;
|
||||
changes = true;
|
||||
}
|
||||
|
||||
int meteredInt = metered ? NM_METERED_UNKNOWN : NM_METERED_NO;
|
||||
if (settings.value("connection").value("metered").toInt() != meteredInt) {
|
||||
qWarning() << "Changing connection.metered to" << meteredInt;
|
||||
settings["connection"]["metered"] = meteredInt;
|
||||
changes = true;
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
call(lteConnectionPath.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "UpdateUnsaved", QVariant::fromValue(settings)); // update is temporary
|
||||
deactivateConnection(lteConnectionPath);
|
||||
activateModemConnection(lteConnectionPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Functions for tethering
|
||||
void WifiManager::addTetheringConnection() {
|
||||
Connection connection;
|
||||
connection["connection"]["id"] = "Hotspot";
|
||||
connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}');
|
||||
connection["connection"]["type"] = "802-11-wireless";
|
||||
connection["connection"]["interface-name"] = "wlan0";
|
||||
connection["connection"]["autoconnect"] = false;
|
||||
|
||||
connection["802-11-wireless"]["band"] = "bg";
|
||||
connection["802-11-wireless"]["mode"] = "ap";
|
||||
connection["802-11-wireless"]["ssid"] = tethering_ssid.toUtf8();
|
||||
|
||||
connection["802-11-wireless-security"]["group"] = QStringList("ccmp");
|
||||
connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk";
|
||||
connection["802-11-wireless-security"]["pairwise"] = QStringList("ccmp");
|
||||
connection["802-11-wireless-security"]["proto"] = QStringList("rsn");
|
||||
connection["802-11-wireless-security"]["psk"] = defaultTetheringPassword;
|
||||
|
||||
connection["ipv4"]["method"] = "shared";
|
||||
QVariantMap address;
|
||||
address["address"] = "192.168.43.1";
|
||||
address["prefix"] = 24u;
|
||||
connection["ipv4"]["address-data"] = QVariant::fromValue(IpConfig() << address);
|
||||
connection["ipv4"]["gateway"] = "192.168.43.1";
|
||||
connection["ipv4"]["route-metric"] = 1100;
|
||||
connection["ipv6"]["method"] = "ignore";
|
||||
|
||||
call(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "AddConnection", QVariant::fromValue(connection));
|
||||
}
|
||||
|
||||
void WifiManager::tetheringActivated(QDBusPendingCallWatcher *call) {
|
||||
int prime_type = uiState()->primeType();
|
||||
int ipv4_forward = (prime_type == PrimeType::NONE || prime_type == PrimeType::LITE);
|
||||
|
||||
if (!ipv4_forward) {
|
||||
QTimer::singleShot(5000, this, [=] {
|
||||
qWarning() << "net.ipv4.ip_forward = 0";
|
||||
std::system("sudo sysctl net.ipv4.ip_forward=0");
|
||||
});
|
||||
}
|
||||
call->deleteLater();
|
||||
}
|
||||
|
||||
void WifiManager::setTetheringEnabled(bool enabled) {
|
||||
if (enabled) {
|
||||
if (!isKnownConnection(tethering_ssid)) {
|
||||
addTetheringConnection();
|
||||
}
|
||||
|
||||
auto pending_call = activateWifiConnection(tethering_ssid);
|
||||
|
||||
if (pending_call) {
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(*pending_call);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &WifiManager::tetheringActivated);
|
||||
}
|
||||
|
||||
} else {
|
||||
deactivateConnectionBySsid(tethering_ssid);
|
||||
}
|
||||
}
|
||||
|
||||
bool WifiManager::isTetheringEnabled() {
|
||||
if (!emptyPath(activeAp)) {
|
||||
return get_property(activeAp, "Ssid") == tethering_ssid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString WifiManager::getTetheringPassword() {
|
||||
if (!isKnownConnection(tethering_ssid)) {
|
||||
addTetheringConnection();
|
||||
}
|
||||
const QDBusObjectPath &path = getConnectionPath(tethering_ssid);
|
||||
if (!path.path().isEmpty()) {
|
||||
QDBusReply<QMap<QString, QVariantMap>> response = call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "GetSecrets", "802-11-wireless-security");
|
||||
return response.value().value("802-11-wireless-security").value("psk").toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void WifiManager::changeTetheringPassword(const QString &newPassword) {
|
||||
const QDBusObjectPath &path = getConnectionPath(tethering_ssid);
|
||||
if (!path.path().isEmpty()) {
|
||||
Connection settings = getConnectionSettings(path);
|
||||
settings["802-11-wireless-security"]["psk"] = newPassword;
|
||||
call(path.path(), NM_DBUS_INTERFACE_SETTINGS_CONNECTION, "Update", QVariant::fromValue(settings));
|
||||
if (isTetheringEnabled()) {
|
||||
activateWifiConnection(tethering_ssid);
|
||||
}
|
||||
}
|
||||
}
|
||||
102
selfdrive/ui/qt/network/wifi_manager.h
Executable file
102
selfdrive/ui/qt/network/wifi_manager.h
Executable file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <QtDBus>
|
||||
#include <QTimer>
|
||||
|
||||
#include "selfdrive/ui/qt/network/networkmanager.h"
|
||||
|
||||
enum class SecurityType {
|
||||
OPEN,
|
||||
WPA,
|
||||
UNSUPPORTED
|
||||
};
|
||||
enum class ConnectedType {
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED
|
||||
};
|
||||
enum class NetworkType {
|
||||
NONE,
|
||||
WIFI,
|
||||
CELL,
|
||||
ETHERNET
|
||||
};
|
||||
|
||||
typedef QMap<QString, QVariantMap> Connection;
|
||||
typedef QVector<QVariantMap> IpConfig;
|
||||
|
||||
struct Network {
|
||||
QByteArray ssid;
|
||||
unsigned int strength;
|
||||
ConnectedType connected;
|
||||
SecurityType security_type;
|
||||
};
|
||||
bool compare_by_strength(const Network &a, const Network &b);
|
||||
inline int strengthLevel(unsigned int strength) { return std::clamp((int)round(strength / 33.), 0, 3); }
|
||||
|
||||
class WifiManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QMap<QString, Network> seenNetworks;
|
||||
QMap<QDBusObjectPath, QString> knownConnections;
|
||||
QString ipv4_address;
|
||||
|
||||
explicit WifiManager(QObject* parent);
|
||||
void start();
|
||||
void stop();
|
||||
void requestScan();
|
||||
void forgetConnection(const QString &ssid);
|
||||
bool isKnownConnection(const QString &ssid);
|
||||
std::optional<QDBusPendingCall> activateWifiConnection(const QString &ssid);
|
||||
NetworkType currentNetworkType();
|
||||
void updateGsmSettings(bool roaming, QString apn, bool metered);
|
||||
void connect(const Network &ssid, const QString &password = {}, const QString &username = {});
|
||||
|
||||
// Tethering functions
|
||||
void setTetheringEnabled(bool enabled);
|
||||
bool isTetheringEnabled();
|
||||
void changeTetheringPassword(const QString &newPassword);
|
||||
QString getIp4Address();
|
||||
QString getTetheringPassword();
|
||||
|
||||
private:
|
||||
QString adapter; // Path to network manager wifi-device
|
||||
QTimer timer;
|
||||
unsigned int raw_adapter_state; // Connection status https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NMDeviceState
|
||||
QString connecting_to_network;
|
||||
QString tethering_ssid;
|
||||
const QString defaultTetheringPassword = "swagswagcomma";
|
||||
QString activeAp;
|
||||
QDBusObjectPath lteConnectionPath;
|
||||
|
||||
QString getAdapter(const uint = NM_DEVICE_TYPE_WIFI);
|
||||
uint getAdapterType(const QDBusObjectPath &path);
|
||||
void deactivateConnectionBySsid(const QString &ssid);
|
||||
void deactivateConnection(const QDBusObjectPath &path);
|
||||
QVector<QDBusObjectPath> getActiveConnections();
|
||||
QByteArray get_property(const QString &network_path, const QString &property);
|
||||
SecurityType getSecurityType(const QVariantMap &properties);
|
||||
QDBusObjectPath getConnectionPath(const QString &ssid);
|
||||
Connection getConnectionSettings(const QDBusObjectPath &path);
|
||||
void initConnections();
|
||||
void setup();
|
||||
void refreshNetworks();
|
||||
void activateModemConnection(const QDBusObjectPath &path);
|
||||
void addTetheringConnection();
|
||||
void setCurrentConnecting(const QString &ssid);
|
||||
|
||||
signals:
|
||||
void wrongPassword(const QString &ssid);
|
||||
void refreshSignal();
|
||||
|
||||
private slots:
|
||||
void stateChange(unsigned int new_state, unsigned int previous_state, unsigned int change_reason);
|
||||
void propertyChange(const QString &interface, const QVariantMap &props, const QStringList &invalidated_props);
|
||||
void deviceAdded(const QDBusObjectPath &path);
|
||||
void connectionRemoved(const QDBusObjectPath &path);
|
||||
void newConnection(const QDBusObjectPath &path);
|
||||
void refreshFinished(QDBusPendingCallWatcher *call);
|
||||
void tetheringActivated(QDBusPendingCallWatcher *call);
|
||||
};
|
||||
Reference in New Issue
Block a user