You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libcutefish/networkmanagement/connectionicon.cpp

658 lines
24 KiB
C++

/*
Copyright 2013 Jan Grulich <jgrulich@redhat.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "connectionicon.h"
#include "uiutils.h"
#include <NetworkManagerQt/BluetoothDevice>
#include <NetworkManagerQt/Connection>
#include <NetworkManagerQt/ConnectionSettings>
#include <NetworkManagerQt/Device>
#include <NetworkManagerQt/ModemDevice>
#include <NetworkManagerQt/WiredDevice>
#include <NetworkManagerQt/WirelessDevice>
#include <NetworkManagerQt/WirelessSetting>
#if WITH_MODEMMANAGER_SUPPORT
#include <ModemManagerQt/manager.h>
#endif
ConnectionIcon::ConnectionIcon(QObject* parent)
: QObject(parent)
, m_signal(0)
, m_wirelessNetwork(nullptr)
, m_connecting(false)
, m_limited(false)
, m_vpn(false)
#if WITH_MODEMMANAGER_SUPPORT
, m_modemNetwork(nullptr)
#endif
{
connect(NetworkManager::notifier(), &NetworkManager::Notifier::primaryConnectionChanged, this, &ConnectionIcon::primaryConnectionChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::activatingConnectionChanged, this, &ConnectionIcon::activatingConnectionChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionAdded, this, &ConnectionIcon::activeConnectionAdded);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::connectivityChanged, this, &ConnectionIcon::connectivityChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceAdded, this, &ConnectionIcon::deviceAdded);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceRemoved, this, &ConnectionIcon::deviceRemoved);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::networkingEnabledChanged, this, &ConnectionIcon::networkingEnabledChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::statusChanged, this, &ConnectionIcon::statusChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::wirelessEnabledChanged, this, &ConnectionIcon::wirelessEnabledChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::wirelessHardwareEnabledChanged, this, &ConnectionIcon::wirelessEnabledChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::wwanEnabledChanged, this, &ConnectionIcon::wwanEnabledChanged);
connect(NetworkManager::notifier(), &NetworkManager::Notifier::wwanHardwareEnabledChanged, this, &ConnectionIcon::wwanEnabledChanged);
for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
if (device->type() == NetworkManager::Device::Ethernet) {
NetworkManager::WiredDevice::Ptr wiredDevice = device.staticCast<NetworkManager::WiredDevice>();
if (wiredDevice) {
connect(wiredDevice.data(), &NetworkManager::WiredDevice::carrierChanged, this, &ConnectionIcon::carrierChanged);
}
} else if (device->type() == NetworkManager::Device::Wifi) {
NetworkManager::WirelessDevice::Ptr wifiDevice = device.staticCast<NetworkManager::WirelessDevice>();
if (wifiDevice) {
connect(wifiDevice.data(), &NetworkManager::WirelessDevice::availableConnectionAppeared, this, &ConnectionIcon::wirelessNetworkAppeared);
connect(wifiDevice.data(), &NetworkManager::WirelessDevice::networkAppeared, this, &ConnectionIcon::wirelessNetworkAppeared);
}
}
}
for (const NetworkManager::ActiveConnection::Ptr &activeConnection : NetworkManager::activeConnections()) {
addActiveConnection(activeConnection->path());
}
setStates();
setIcons();
QDBusPendingReply<uint> pendingReply = NetworkManager::checkConnectivity();
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(pendingReply);
connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<uint> reply = *watcher;
if (reply.isValid()) {
connectivityChanged((NetworkManager::Connectivity)reply.value());
}
watcher->deleteLater();
});
}
ConnectionIcon::~ConnectionIcon()
{
}
bool ConnectionIcon::connecting() const
{
return m_connecting;
}
QString ConnectionIcon::connectionIcon() const
{
if (m_vpn && !m_connectionIcon.contains("available")) {
return m_connectionIcon + "-locked";
}
if (m_limited && !m_connectionIcon.contains("available")) {
return m_connectionIcon + "-limited";
}
return m_connectionIcon;
}
QString ConnectionIcon::connectionTooltipIcon() const
{
return m_connectionTooltipIcon;
}
QString ConnectionIcon::currentSSID() const
{
return m_currentSSID;
}
void ConnectionIcon::activatingConnectionChanged(const QString& connection)
{
Q_UNUSED(connection);
setIcons();
}
void ConnectionIcon::addActiveConnection(const QString &activeConnection)
{
NetworkManager::ActiveConnection::Ptr active = NetworkManager::findActiveConnection(activeConnection);
if (active) {
NetworkManager::VpnConnection::Ptr vpnConnection;
connect(active.data(), &NetworkManager::ActiveConnection::destroyed, this, &ConnectionIcon::activeConnectionDestroyed);
if (active->vpn()) {
vpnConnection = active.objectCast<NetworkManager::VpnConnection>();
connect(vpnConnection.data(), &NetworkManager::VpnConnection::stateChanged, this, &ConnectionIcon::vpnConnectionStateChanged);
} else {
connect(active.data(), &NetworkManager::ActiveConnection::stateChanged, this, &ConnectionIcon::activeConnectionStateChanged, Qt::UniqueConnection);
}
}
}
void ConnectionIcon::activeConnectionAdded(const QString &activeConnection)
{
addActiveConnection(activeConnection);
setStates();
}
void ConnectionIcon::activeConnectionStateChanged(NetworkManager::ActiveConnection::State state)
{
Q_UNUSED(state);
setStates();
}
void ConnectionIcon::activeConnectionDestroyed()
{
setStates();
}
void ConnectionIcon::carrierChanged(bool carrier)
{
Q_UNUSED(carrier);
setIcons();
}
void ConnectionIcon::connectivityChanged(NetworkManager::Connectivity conn)
{
const bool needsPortal = conn == NetworkManager::Portal;
if (needsPortal != m_needsPortal) {
m_needsPortal = needsPortal;
Q_EMIT needsPortalChanged(needsPortal);
}
setLimited(conn == NetworkManager::Portal || conn == NetworkManager::Limited);
}
void ConnectionIcon::deviceAdded(const QString& device)
{
NetworkManager::Device::Ptr dev = NetworkManager::findNetworkInterface(device);
if (!dev) {
return;
}
if (dev->type() == NetworkManager::Device::Ethernet) {
NetworkManager::WiredDevice::Ptr wiredDev = dev.objectCast<NetworkManager::WiredDevice>();
connect(wiredDev.data(), &NetworkManager::WiredDevice::carrierChanged, this, &ConnectionIcon::carrierChanged);
}
}
void ConnectionIcon::deviceRemoved(const QString& device)
{
Q_UNUSED(device);
if (NetworkManager::status() == NetworkManager::Disconnected) {
setDisconnectedIcon();
}
}
#if WITH_MODEMMANAGER_SUPPORT
void ConnectionIcon::modemNetworkRemoved()
{
m_modemNetwork.clear();
}
void ConnectionIcon::modemSignalChanged(const ModemManager::SignalQualityPair &signalQuality)
{
int diff = m_signal - signalQuality.signal;
if (diff >= 10 ||
diff <= -10) {
m_signal = signalQuality.signal;
setIconForModem();
}
}
#endif
void ConnectionIcon::networkingEnabledChanged(bool enabled)
{
if (!enabled) {
setConnectionIcon("network-unavailable");
}
}
void ConnectionIcon::primaryConnectionChanged(const QString& connection)
{
if (!connection.isEmpty()) {
setIcons();
}
}
void ConnectionIcon::statusChanged(NetworkManager::Status status)
{
if (status == NetworkManager::Disconnected) {
setDisconnectedIcon();
}
}
void ConnectionIcon::vpnConnectionStateChanged(NetworkManager::VpnConnection::State state, NetworkManager::VpnConnection::StateChangeReason reason)
{
Q_UNUSED(state);
Q_UNUSED(reason);
setStates();
setIcons();
}
void ConnectionIcon::wirelessEnabledChanged(bool enabled)
{
Q_UNUSED(enabled);
m_currentSSID = "";
emit currentSSIDChanged();
setIcons();
}
void ConnectionIcon::wwanEnabledChanged(bool enabled)
{
Q_UNUSED(enabled);
setIcons();
}
void ConnectionIcon::wirelessNetworkAppeared(const QString& network)
{
Q_UNUSED(network);
setIcons();
}
void ConnectionIcon::setStates()
{
bool connecting = false;
bool vpn = false;
for (const NetworkManager::ActiveConnection::Ptr &activeConnection : NetworkManager::activeConnections()) {
NetworkManager::VpnConnection::Ptr vpnConnection;
if (activeConnection->vpn()) {
vpnConnection = activeConnection.objectCast<NetworkManager::VpnConnection>();
}
if (!vpnConnection) {
if (activeConnection->state() == NetworkManager::ActiveConnection::Activating && UiUtils::isConnectionTypeSupported(activeConnection->type())) {
connecting = true;
}
if (activeConnection->type() == NetworkManager::ConnectionSettings::ConnectionType::WireGuard) {
vpn = true;
}
} else {
if (vpnConnection->state() == NetworkManager::VpnConnection::Activated) {
vpn = true;
} else if (vpnConnection->state() == NetworkManager::VpnConnection::Prepare ||
vpnConnection->state() == NetworkManager::VpnConnection::NeedAuth ||
vpnConnection->state() == NetworkManager::VpnConnection::Connecting ||
vpnConnection->state() == NetworkManager::VpnConnection::GettingIpConfig) {
connecting = true;
}
}
}
setVpn(vpn);
setConnecting(connecting);
}
void ConnectionIcon::setIcons()
{
m_signal = 0;
#if WITH_MODEMMANAGER_SUPPORT
if (m_modemNetwork) {
disconnect(m_modemNetwork.data(), nullptr, this, nullptr);
m_modemNetwork.clear();
}
#endif
if (m_wirelessNetwork) {
disconnect(m_wirelessNetwork.data(), nullptr, this, nullptr);
m_wirelessNetwork.clear();
}
NetworkManager::ActiveConnection::Ptr connection = NetworkManager::activatingConnection();
// Set icon based on the current primary connection if the activating connection is virtual
// since we're not setting icons for virtual connections
if (!connection
|| (connection && UiUtils::isConnectionTypeVirtual(connection->type()))
|| connection->type() == NetworkManager::ConnectionSettings::WireGuard) {
connection = NetworkManager::primaryConnection();
}
/* Fallback: If we still don't have an active connection with default route or the default route goes through a connection
of generic type (some type of VPNs) we need to go through all other active connections and pick the one with
highest probability of being the main one (order is: vpn, wired, wireless, gsm, cdma, bluetooth) */
if ((!connection && !NetworkManager::activeConnections().isEmpty()) || (connection && connection->type() == NetworkManager::ConnectionSettings::Generic)
|| (connection && connection->type() == NetworkManager::ConnectionSettings::Tun)) {
for (const NetworkManager::ActiveConnection::Ptr &activeConnection : NetworkManager::activeConnections()) {
const NetworkManager::ConnectionSettings::ConnectionType type = activeConnection->type();
if (type == NetworkManager::ConnectionSettings::Bluetooth) {
if (connection && connection->type() <= NetworkManager::ConnectionSettings::Bluetooth) {
connection = activeConnection;
}
} else if (type == NetworkManager::ConnectionSettings::Cdma) {
if (connection && connection->type() <= NetworkManager::ConnectionSettings::Cdma) {
connection = activeConnection;
}
} else if (type == NetworkManager::ConnectionSettings::Gsm) {
if (connection && connection->type() <= NetworkManager::ConnectionSettings::Gsm) {
connection = activeConnection;
}
} else if (type == NetworkManager::ConnectionSettings::Vpn) {
connection = activeConnection;
} else if (type == NetworkManager::ConnectionSettings::WireGuard) {
connection = activeConnection;
} else if (type == NetworkManager::ConnectionSettings::Wired) {
if (connection && (connection->type() != NetworkManager::ConnectionSettings::Vpn
|| connection->type() != NetworkManager::ConnectionSettings::WireGuard)) {
connection = activeConnection;
}
} else if (type == NetworkManager::ConnectionSettings::Wireless) {
if (connection && (connection->type() != NetworkManager::ConnectionSettings::Vpn &&
(connection->type() != NetworkManager::ConnectionSettings::Wired))) {
connection = activeConnection;
}
}
}
}
if (connection && !connection->devices().isEmpty()) {
NetworkManager::Device::Ptr device = NetworkManager::findNetworkInterface(connection->devices().first());
if (device) {
NetworkManager::Device::Type type = device->type();
if (type == NetworkManager::Device::Wifi) {
NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast<NetworkManager::WirelessDevice>();
if (wifiDevice->mode() == NetworkManager::WirelessDevice::Adhoc) {
setWirelessIconForSignalStrength(100);
} else {
NetworkManager::AccessPoint::Ptr ap = wifiDevice->activeAccessPoint();
if (ap) {
setWirelessIcon(device, ap->ssid());
}
}
} else if (type == NetworkManager::Device::Ethernet) {
setConnectionIcon("network-wired-activated");
setConnectionTooltipIcon("network-wired-activated");
} else if (type == NetworkManager::Device::Modem) {
#if WITH_MODEMMANAGER_SUPPORT
setModemIcon(device);
#else
setConnectionIcon("network-mobile-0");
setConnectionTooltipIcon("phone");
#endif
} else if (type == NetworkManager::Device::Bluetooth) {
NetworkManager::BluetoothDevice::Ptr btDevice = device.objectCast<NetworkManager::BluetoothDevice>();
if (btDevice) {
if (btDevice->bluetoothCapabilities().testFlag(NetworkManager::BluetoothDevice::Dun)) {
#if WITH_MODEMMANAGER_SUPPORT
setModemIcon(device);
#else
setConnectionIcon("network-mobile-0");
setConnectionTooltipIcon("phone");
#endif
} else {
setConnectionIcon("network-bluetooth-activated");
setConnectionTooltipIcon("preferences-system-bluetooth");
}
}
} else if (type == 29) { // TODO change to WireGuard enum value once it is added
// WireGuard is a VPN but is not implemented
// in NetworkManager as a VPN, so we don't want to
// do anything just because it has a device
// associated with it.
} else {
// Ignore other devices (bond/bridge/team etc.)
setDisconnectedIcon();
}
}
} else {
setDisconnectedIcon();
}
}
void ConnectionIcon::setDisconnectedIcon()
{
// if (Configuration::airplaneModeEnabled()) {
// setConnectionIcon(QStringLiteral("network-flightmode-on"));
// return;
// }
if (NetworkManager::status() == NetworkManager::Unknown ||
NetworkManager::status() == NetworkManager::Asleep) {
setConnectionIcon("network-unavailable");
return;
}
bool wired = false;
bool wireless = false;
bool modem = false;
m_limited = false;
m_vpn = false;
for (const NetworkManager::Device::Ptr &device : NetworkManager::networkInterfaces()) {
if (device->type() == NetworkManager::Device::Ethernet) {
NetworkManager::WiredDevice::Ptr wiredDev = device.objectCast<NetworkManager::WiredDevice>();
if (wiredDev->carrier()) {
wired = true;
}
} else if (device->type() == NetworkManager::Device::Wifi &&
NetworkManager::isWirelessEnabled() &&
NetworkManager::isWirelessHardwareEnabled()) {
NetworkManager::WirelessDevice::Ptr wifiDevice = device.objectCast<NetworkManager::WirelessDevice>();
if (!wifiDevice->accessPoints().isEmpty() || !wifiDevice->availableConnections().isEmpty()) {
wireless = true;
}
} else if (device->type() == NetworkManager::Device::Modem &&
NetworkManager::isWwanEnabled() &&
NetworkManager::isWwanHardwareEnabled()) {
modem = true;
}
}
if (wired) {
setConnectionIcon("network-wired-available");
setConnectionTooltipIcon("network-wired");
return;
} else if (wireless) {
setConnectionIcon("network-wireless-available");
setConnectionTooltipIcon("network-wireless-connected-00");
return;
} else if (modem) {
setConnectionIcon("network-mobile-available");
setConnectionTooltipIcon("phone");
return;
} else {
setConnectionIcon("network-unavailable");
setConnectionTooltipIcon("network-wired");
}
}
#if WITH_MODEMMANAGER_SUPPORT
void ConnectionIcon::setModemIcon(const NetworkManager::Device::Ptr & device)
{
NetworkManager::ModemDevice::Ptr modemDevice = device.objectCast<NetworkManager::ModemDevice>();
if (!modemDevice) {
setConnectionIcon("network-mobile-100");
return;
}
ModemManager::ModemDevice::Ptr modem = ModemManager::findModemDevice(device->udi());
if (modem) {
if (modem->hasInterface(ModemManager::ModemDevice::ModemInterface)) {
m_modemNetwork = modem->interface(ModemManager::ModemDevice::ModemInterface).objectCast<ModemManager::Modem>();
}
}
if (m_modemNetwork) {
connect(m_modemNetwork.data(), &ModemManager::Modem::signalQualityChanged, this, &ConnectionIcon::modemSignalChanged, Qt::UniqueConnection);
connect(m_modemNetwork.data(), &ModemManager::Modem::accessTechnologiesChanged, this, &ConnectionIcon::setIconForModem, Qt::UniqueConnection);
connect(m_modemNetwork.data(), &ModemManager::Modem::destroyed, this, &ConnectionIcon::modemNetworkRemoved);
m_signal = m_modemNetwork->signalQuality().signal;
setIconForModem();
} else {
setConnectionIcon("network-mobile-0");
setConnectionTooltipIcon("phone");
return;
}
}
void ConnectionIcon::setIconForModem()
{
if (!m_signal) {
m_signal = m_modemNetwork->signalQuality().signal;
}
QString strength = "00";
if (m_signal == 0) {
strength = '0';
} else if (m_signal < 20) {
strength = "20";
} else if (m_signal < 40) {
strength = "40";
} else if (m_signal < 60) {
strength = "60";
} else if (m_signal < 80) {
strength = "80";
} else {
strength = "100";
}
QString result;
switch(m_modemNetwork->accessTechnologies()) {
case MM_MODEM_ACCESS_TECHNOLOGY_GSM:
case MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT:
result = "network-mobile-%1";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_GPRS:
result = "network-mobile-%1-gprs";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_EDGE:
result = "network-mobile-%1-edge";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_UMTS:
result = "network-mobile-%1-umts";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_HSDPA:
result = "network-mobile-%1-hsdpa";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_HSUPA:
result = "network-mobile-%1-hsupa";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_HSPA:
case MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS:
result = "network-mobile-%1-hspa";
break;
case MM_MODEM_ACCESS_TECHNOLOGY_LTE:
result = "network-mobile-%1-lte";
break;
default:
result = "network-mobile-%1";
break;
}
setConnectionIcon(QString(result).arg(strength));
setConnectionTooltipIcon("phone");
}
#endif
void ConnectionIcon::setWirelessIcon(const NetworkManager::Device::Ptr &device, const QString& ssid)
{
NetworkManager::WirelessDevice::Ptr wirelessDevice = device.objectCast<NetworkManager::WirelessDevice>();
if (device) {
m_wirelessNetwork = wirelessDevice->findNetwork(ssid);
} else {
m_wirelessNetwork.clear();
}
if (m_wirelessNetwork) {
m_currentSSID = ssid;
emit currentSSIDChanged();
connect(m_wirelessNetwork.data(), &NetworkManager::WirelessNetwork::signalStrengthChanged, this, &ConnectionIcon::setWirelessIconForSignalStrength, Qt::UniqueConnection);
setWirelessIconForSignalStrength(m_wirelessNetwork->signalStrength());
} else {
setDisconnectedIcon();
m_currentSSID = "";
emit currentSSIDChanged();
}
}
void ConnectionIcon::setWirelessIconForSignalStrength(int strength)
{
int iconStrength = 100;
if (strength == 0) {
iconStrength = 0;
setConnectionTooltipIcon("network-wireless-connected-00");
} else if (strength <= 25) {
iconStrength = 25;
setConnectionTooltipIcon("network-wireless-connected-25");
} else if (strength <= 50) {
iconStrength = 50;
setConnectionTooltipIcon("network-wireless-connected-50");
} else if (strength < 75) {
iconStrength = 75;
setConnectionTooltipIcon("network-wireless-connected-75");
} else if (strength < 100) {
setConnectionTooltipIcon("network-wireless-connected-100");
}
QString icon = QString("network-wireless-%1").arg(iconStrength);
setConnectionIcon(icon);
}
void ConnectionIcon::setConnecting(bool connecting)
{
if (connecting != m_connecting) {
m_connecting = connecting;
Q_EMIT connectingChanged(m_connecting);
}
}
void ConnectionIcon::setConnectionIcon(const QString & icon)
{
if (icon != m_connectionIcon) {
m_connectionIcon = icon;
Q_EMIT connectionIconChanged(connectionIcon());
}
}
void ConnectionIcon::setConnectionTooltipIcon(const QString & icon)
{
if (icon != m_connectionTooltipIcon) {
m_connectionTooltipIcon = icon;
Q_EMIT connectionTooltipIconChanged(m_connectionTooltipIcon);
}
}
void ConnectionIcon::setVpn(bool vpn)
{
if (m_vpn != vpn) {
m_vpn = vpn;
Q_EMIT connectionIconChanged(connectionIcon());
}
}
void ConnectionIcon::setLimited(bool limited)
{
if (m_limited != limited) {
m_limited = limited;
Q_EMIT connectionIconChanged(connectionIcon());
}
}