Make capabilities static
As discussed with Albert, move the population of capabilities into the identity package, making them static along the execution of the link. When we receive the identityPackage, we collect the plugins we can use with the device and stick to those. This should simplify the implementation and remove the possibility to lose packages if packages are received before the capabilities are processed in the former approach. REVIEW: 128386
This commit is contained in:
parent
afdac88885
commit
81634303b2
9 changed files with 69 additions and 97 deletions
|
@ -40,8 +40,6 @@
|
|||
#include "kdeconnectconfig.h"
|
||||
#include "daemon.h"
|
||||
|
||||
#define MIN_VERSION_WITH_CAPPABILITIES_SUPPORT 6
|
||||
|
||||
Q_LOGGING_CATEGORY(KDECONNECT_CORE, "kdeconnect.core")
|
||||
|
||||
static void warn(const QString &info)
|
||||
|
@ -69,8 +67,6 @@ Device::Device(QObject* parent, const NetworkPackage& identityPackage, DeviceLin
|
|||
: QObject(parent)
|
||||
, m_deviceId(identityPackage.get<QString>("deviceId"))
|
||||
, m_deviceName(identityPackage.get<QString>("deviceName"))
|
||||
, m_deviceType(str2type(identityPackage.get<QString>("deviceType")))
|
||||
, m_protocolVersion(identityPackage.get<int>("protocolVersion", -1))
|
||||
{
|
||||
addLink(identityPackage, dl);
|
||||
|
||||
|
@ -99,45 +95,17 @@ QStringList Device::loadedPlugins() const
|
|||
void Device::reloadPlugins()
|
||||
{
|
||||
QHash<QString, KdeConnectPlugin*> newPluginMap;
|
||||
QMultiMap<QString, KdeConnectPlugin*> newPluginsByIncomingInterface;
|
||||
QMultiMap<QString, KdeConnectPlugin*> newPluginsByOutgoingInterface;
|
||||
QSet<QString> supportedIncomingInterfaces;
|
||||
QSet<QString> supportedOutgoingInterfaces;
|
||||
QStringList unsupportedPlugins;
|
||||
QMultiMap<QString, KdeConnectPlugin*> newPluginsByIncomingCapability;
|
||||
|
||||
if (isTrusted() && isReachable()) { //Do not load any plugin for unpaired devices, nor useless loading them for unreachable devices
|
||||
|
||||
PluginLoader* loader = PluginLoader::instance();
|
||||
const bool capabilitiesSupported = (m_protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT);
|
||||
|
||||
Q_FOREACH (const QString& pluginName, loader->getPluginList()) {
|
||||
Q_FOREACH (const QString& pluginName, m_supportedPlugins) {
|
||||
const KPluginMetaData service = loader->getPluginInfo(pluginName);
|
||||
const QSet<QString> incomingInterfaces = KPluginMetaData::readStringList(service.rawData(), "X-KdeConnect-SupportedPackageType").toSet();
|
||||
const QSet<QString> outgoingInterfaces = KPluginMetaData::readStringList(service.rawData(), "X-KdeConnect-OutgoingPackageType").toSet();
|
||||
|
||||
const bool pluginEnabled = isPluginEnabled(pluginName);
|
||||
|
||||
if (pluginEnabled) {
|
||||
supportedIncomingInterfaces += incomingInterfaces;
|
||||
supportedOutgoingInterfaces += outgoingInterfaces;
|
||||
}
|
||||
|
||||
const bool pluginNeedsCapabilities = !incomingInterfaces.isEmpty() || !outgoingInterfaces.isEmpty();
|
||||
|
||||
//If we don't find intersection with the received on one end and the sent on the other, we don't
|
||||
//let the plugin stay
|
||||
if (capabilitiesSupported && pluginNeedsCapabilities
|
||||
&& (m_incomingCapabilities & outgoingInterfaces).isEmpty()
|
||||
&& (m_outgoingCapabilities & incomingInterfaces).isEmpty()
|
||||
) {
|
||||
if (!m_incomingCapabilities.isEmpty() || !m_outgoingCapabilities.isEmpty()) {
|
||||
qCWarning(KDECONNECT_CORE) << "not loading" << pluginName << "because of unmatched capabilities" <<
|
||||
outgoingInterfaces << incomingInterfaces;
|
||||
}
|
||||
|
||||
unsupportedPlugins.append(pluginName);
|
||||
continue;
|
||||
}
|
||||
const QSet<QString> incomingCapabilities = KPluginMetaData::readStringList(service.rawData(), "X-KdeConnect-SupportedPackageType").toSet();
|
||||
|
||||
if (pluginEnabled) {
|
||||
KdeConnectPlugin* plugin = m_plugins.take(pluginName);
|
||||
|
@ -145,12 +113,10 @@ void Device::reloadPlugins()
|
|||
if (!plugin) {
|
||||
plugin = loader->instantiatePluginForDevice(pluginName, this);
|
||||
}
|
||||
Q_ASSERT(plugin);
|
||||
|
||||
Q_FOREACH (const QString& interface, incomingInterfaces) {
|
||||
newPluginsByIncomingInterface.insert(interface, plugin);
|
||||
}
|
||||
Q_FOREACH (const QString& interface, outgoingInterfaces) {
|
||||
newPluginsByOutgoingInterface.insert(interface, plugin);
|
||||
Q_FOREACH (const QString& interface, incomingCapabilities) {
|
||||
newPluginsByIncomingCapability.insert(interface, plugin);
|
||||
}
|
||||
|
||||
newPluginMap[pluginName] = plugin;
|
||||
|
@ -158,31 +124,20 @@ void Device::reloadPlugins()
|
|||
}
|
||||
}
|
||||
|
||||
const bool differentPlugins = m_plugins != newPluginMap;
|
||||
|
||||
//Erase all left plugins in the original map (meaning that we don't want
|
||||
//them anymore, otherwise they would have been moved to the newPluginMap)
|
||||
const QStringList newSupportedIncomingInterfaces = supportedIncomingInterfaces.toList();
|
||||
const QStringList newSupportedOutgoingInterfaces = supportedOutgoingInterfaces.toList();
|
||||
const bool capabilitiesChanged = (m_pluginsByOutgoingInterface != newPluginsByOutgoingInterface
|
||||
|| m_supportedIncomingInterfaces != newSupportedIncomingInterfaces);
|
||||
qDeleteAll(m_plugins);
|
||||
m_plugins = newPluginMap;
|
||||
m_supportedIncomingInterfaces = newSupportedIncomingInterfaces;
|
||||
m_supportedOutgoingInterfaces = newSupportedOutgoingInterfaces;
|
||||
m_pluginsByOutgoingInterface = newPluginsByOutgoingInterface;
|
||||
m_pluginsByIncomingInterface = newPluginsByIncomingInterface;
|
||||
m_unsupportedPlugins = unsupportedPlugins;
|
||||
m_pluginsByIncomingCapability = newPluginsByIncomingCapability;
|
||||
|
||||
//TODO: see how it works in Android (only done once, when created)
|
||||
Q_FOREACH(KdeConnectPlugin* plugin, m_plugins) {
|
||||
plugin->connected();
|
||||
}
|
||||
if (differentPlugins) {
|
||||
Q_EMIT pluginsChanged();
|
||||
|
||||
if (capabilitiesChanged && isReachable() && isTrusted())
|
||||
{
|
||||
NetworkPackage np(PACKAGE_TYPE_CAPABILITIES);
|
||||
np.set<QStringList>("IncomingCapabilities", newSupportedIncomingInterfaces);
|
||||
np.set<QStringList>("OutgoingCapabilities", newSupportedOutgoingInterfaces);
|
||||
sendPackage(np);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,13 +226,21 @@ void Device::addLink(const NetworkPackage& identityPackage, DeviceLink* link)
|
|||
|
||||
qSort(m_deviceLinks.begin(), m_deviceLinks.end(), lessThan);
|
||||
|
||||
if (m_deviceLinks.size() == 1) {
|
||||
reloadPlugins(); //Will load the plugins
|
||||
Q_EMIT reachableStatusChanged();
|
||||
const bool capabilitiesSupported = identityPackage.has("incomingCapabilities") || identityPackage.has("outgoingCapabilities");
|
||||
if (capabilitiesSupported) {
|
||||
const QSet<QString> outgoingCapabilities = identityPackage.get<QStringList>("outgoingCapabilities").toSet()
|
||||
, incomingCapabilities = identityPackage.get<QStringList>("incomingCapabilities").toSet();
|
||||
|
||||
m_supportedPlugins = PluginLoader::instance()->pluginsForCapabilities(incomingCapabilities, outgoingCapabilities);
|
||||
qDebug() << "new plugins for" << m_deviceName << m_supportedPlugins << incomingCapabilities << outgoingCapabilities;
|
||||
} else {
|
||||
Q_FOREACH(KdeConnectPlugin* plugin, m_plugins) {
|
||||
plugin->connected();
|
||||
m_supportedPlugins = PluginLoader::instance()->getPluginList().toSet();
|
||||
}
|
||||
|
||||
reloadPlugins();
|
||||
|
||||
if (m_deviceLinks.size() == 1) {
|
||||
Q_EMIT reachableStatusChanged();
|
||||
}
|
||||
|
||||
connect(link, &DeviceLink::pairStatusChanged, this, &Device::pairStatusChanged);
|
||||
|
@ -296,6 +259,7 @@ void Device::removeLink(DeviceLink* link)
|
|||
//qCDebug(KDECONNECT_CORE) << "RemoveLink" << m_deviceLinks.size() << "links remaining";
|
||||
|
||||
if (m_deviceLinks.isEmpty()) {
|
||||
m_supportedPlugins.clear();
|
||||
reloadPlugins();
|
||||
Q_EMIT reachableStatusChanged();
|
||||
}
|
||||
|
@ -317,17 +281,8 @@ bool Device::sendPackage(NetworkPackage& np)
|
|||
void Device::privateReceivedPackage(const NetworkPackage& np)
|
||||
{
|
||||
Q_ASSERT(np.type() != PACKAGE_TYPE_PAIR);
|
||||
if (np.type() == PACKAGE_TYPE_CAPABILITIES) {
|
||||
QSet<QString> newIncomingCapabilities = np.get<QStringList>("IncomingCapabilities", QStringList()).toSet();
|
||||
QSet<QString> newOutgoingCapabilities = np.get<QStringList>("OutgoingCapabilities", QStringList()).toSet();
|
||||
|
||||
if (newOutgoingCapabilities != m_outgoingCapabilities || newIncomingCapabilities != m_incomingCapabilities) {
|
||||
m_incomingCapabilities = newIncomingCapabilities;
|
||||
m_outgoingCapabilities = newOutgoingCapabilities;
|
||||
reloadPlugins();
|
||||
}
|
||||
} else if (isTrusted()) {
|
||||
const QList<KdeConnectPlugin*> plugins = m_pluginsByIncomingInterface.values(np.type());
|
||||
if (isTrusted()) {
|
||||
const QList<KdeConnectPlugin*> plugins = m_pluginsByIncomingCapability.values(np.type());
|
||||
if (plugins.isEmpty()) {
|
||||
qWarning() << "discarding unsupported package" << np.type() << "for" << name();
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class KDECONNECTCORE_EXPORT Device
|
|||
Q_PROPERTY(QString statusIconName READ statusIconName)
|
||||
Q_PROPERTY(bool isReachable READ isReachable NOTIFY reachableStatusChanged)
|
||||
Q_PROPERTY(bool isTrusted READ isTrusted NOTIFY trustedChanged)
|
||||
Q_PROPERTY(QStringList unsupportedPlugins READ unsupportedPlugins NOTIFY pluginsChanged)
|
||||
Q_PROPERTY(QStringList supportedPlugins READ supportedPlugins NOTIFY pluginsChanged)
|
||||
|
||||
public:
|
||||
|
||||
|
@ -82,7 +82,6 @@ public:
|
|||
QString type() const { return type2str(m_deviceType); }
|
||||
QString iconName() const;
|
||||
QString statusIconName() const;
|
||||
QStringList unsupportedPlugins() const { return m_unsupportedPlugins; }
|
||||
Q_SCRIPTABLE QString encryptionInfo() const;
|
||||
|
||||
//Add and remove links
|
||||
|
@ -106,6 +105,7 @@ public:
|
|||
void cleanUnneededLinks();
|
||||
|
||||
int protocolVersion() { return m_protocolVersion; }
|
||||
QStringList supportedPlugins() const { return m_supportedPlugins.toList(); }
|
||||
|
||||
public Q_SLOTS:
|
||||
///sends a @p np package to the device
|
||||
|
@ -147,13 +147,8 @@ private: //Fields (TODO: dPointer!)
|
|||
QHash<QString, KdeConnectPlugin*> m_plugins;
|
||||
|
||||
//Capabilities stuff
|
||||
QMultiMap<QString, KdeConnectPlugin*> m_pluginsByIncomingInterface;
|
||||
QMultiMap<QString, KdeConnectPlugin*> m_pluginsByOutgoingInterface;
|
||||
QSet<QString> m_incomingCapabilities;
|
||||
QSet<QString> m_outgoingCapabilities;
|
||||
QStringList m_supportedIncomingInterfaces;
|
||||
QStringList m_supportedOutgoingInterfaces;
|
||||
QStringList m_unsupportedPlugins;
|
||||
QMultiMap<QString, KdeConnectPlugin*> m_pluginsByIncomingCapability;
|
||||
QSet<QString> m_supportedPlugins;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Device*)
|
||||
|
|
|
@ -67,6 +67,8 @@ void NetworkPackage::createIdentityPackage(NetworkPackage* np)
|
|||
np->set("deviceName", config->name());
|
||||
np->set("deviceType", config->deviceType());
|
||||
np->set("protocolVersion", NetworkPackage::ProtocolVersion);
|
||||
np->set("incomingCapabilities", PluginLoader::instance()->incomingCapabilities());
|
||||
np->set("outgoingCapabilities", PluginLoader::instance()->outgoingCapabilities());
|
||||
|
||||
//qCDebug(KDECONNECT_CORE) << "createIdentityPackage" << np->serialize();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,5 @@
|
|||
|
||||
#define PACKAGE_TYPE_IDENTITY QLatin1String("kdeconnect.identity")
|
||||
#define PACKAGE_TYPE_PAIR QLatin1String("kdeconnect.pair")
|
||||
#define PACKAGE_TYPE_CAPABILITIES QLatin1String("kdeconnect.capabilities")
|
||||
|
||||
#endif // NETWORKPACKAGETYPES_H
|
||||
|
|
|
@ -83,7 +83,7 @@ KdeConnectPlugin* PluginLoader::instantiatePluginForDevice(const QString& plugin
|
|||
return ret;
|
||||
}
|
||||
|
||||
QStringList PluginLoader::incomingInterfaces() const
|
||||
QStringList PluginLoader::incomingCapabilities() const
|
||||
{
|
||||
QSet<QString> ret;
|
||||
Q_FOREACH (const KPluginMetaData& service, plugins) {
|
||||
|
@ -92,7 +92,7 @@ QStringList PluginLoader::incomingInterfaces() const
|
|||
return ret.toList();
|
||||
}
|
||||
|
||||
QStringList PluginLoader::outgoingInterfaces() const
|
||||
QStringList PluginLoader::outgoingCapabilities() const
|
||||
{
|
||||
QSet<QString> ret;
|
||||
Q_FOREACH (const KPluginMetaData& service, plugins) {
|
||||
|
@ -100,3 +100,22 @@ QStringList PluginLoader::outgoingInterfaces() const
|
|||
}
|
||||
return ret.toList();
|
||||
}
|
||||
|
||||
QSet<QString> PluginLoader::pluginsForCapabilities(const QSet<QString>& incoming, const QSet<QString>& outgoing)
|
||||
{
|
||||
QSet<QString> ret;
|
||||
|
||||
Q_FOREACH (const KPluginMetaData& service, plugins) {
|
||||
const QSet<QString> pluginIncomingCapabilities = KPluginMetaData::readStringList(service.rawData(), "X-KdeConnect-SupportedPackageType").toSet();
|
||||
const QSet<QString> pluginOutgoingCapabilities = KPluginMetaData::readStringList(service.rawData(), "X-KdeConnect-OutgoingPackageType").toSet();
|
||||
|
||||
if ((pluginIncomingCapabilities.isEmpty() && pluginOutgoingCapabilities.isEmpty())
|
||||
|| incoming.intersects(pluginOutgoingCapabilities) || outgoing.intersects(pluginIncomingCapabilities)) {
|
||||
ret += service.pluginId();
|
||||
} else {
|
||||
qDebug() << "discarding..." << service.pluginId();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -36,12 +36,14 @@ class PluginLoader
|
|||
public:
|
||||
static PluginLoader* instance();
|
||||
|
||||
QStringList incomingInterfaces() const;
|
||||
QStringList outgoingInterfaces() const;
|
||||
QStringList getPluginList() const;
|
||||
KPluginMetaData getPluginInfo(const QString& name) const;
|
||||
KdeConnectPlugin* instantiatePluginForDevice(const QString& name, Device* device) const;
|
||||
|
||||
QStringList incomingCapabilities() const;
|
||||
QStringList outgoingCapabilities() const;
|
||||
QSet<QString> pluginsForCapabilities(const QSet<QString> &incoming, const QSet<QString> &outgoing);
|
||||
|
||||
private:
|
||||
PluginLoader();
|
||||
QHash<QString, KPluginMetaData> plugins;
|
||||
|
|
12
kcm/kcm.cpp
12
kcm/kcm.cpp
|
@ -199,9 +199,9 @@ void KdeConnectKcm::deviceSelected(const QModelIndex& current)
|
|||
|
||||
void KdeConnectKcm::resetCurrentDevice()
|
||||
{
|
||||
const QStringList unsupportedPluginNames = currentDevice->unsupportedPlugins();
|
||||
const QStringList supportedPluginNames = currentDevice->supportedPlugins();
|
||||
|
||||
if (m_oldUnsupportedPluginNames != unsupportedPluginNames) {
|
||||
if (m_oldSupportedPluginNames != supportedPluginNames) {
|
||||
resetDeviceView();
|
||||
}
|
||||
}
|
||||
|
@ -222,12 +222,12 @@ void KdeConnectKcm::resetDeviceView()
|
|||
QList<KPluginInfo> availablePluginInfo;
|
||||
QList<KPluginInfo> unsupportedPluginInfo;
|
||||
|
||||
m_oldUnsupportedPluginNames = currentDevice->unsupportedPlugins();
|
||||
m_oldSupportedPluginNames = currentDevice->supportedPlugins();
|
||||
for (auto it = pluginInfo.cbegin(), itEnd = pluginInfo.cend(); it!=itEnd; ++it) {
|
||||
if (m_oldUnsupportedPluginNames.contains(it->pluginName())) {
|
||||
unsupportedPluginInfo.append(*it);
|
||||
} else {
|
||||
if (m_oldSupportedPluginNames.contains(it->pluginName())) {
|
||||
availablePluginInfo.append(*it);
|
||||
} else {
|
||||
unsupportedPluginInfo.append(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ private:
|
|||
DevicesSortProxyModel* sortProxyModel;
|
||||
DeviceDbusInterface* currentDevice;
|
||||
QModelIndex currentIndex;
|
||||
QStringList m_oldUnsupportedPluginNames;
|
||||
QStringList m_oldSupportedPluginNames;
|
||||
|
||||
public Q_SLOTS:
|
||||
void unpair();
|
||||
|
|
|
@ -68,11 +68,11 @@ class PluginLoadTest : public QObject
|
|||
|
||||
d->setPluginEnabled("kdeconnect_mousepad", false);
|
||||
QCOMPARE(d->isPluginEnabled("kdeconnect_mousepad"), false);
|
||||
QVERIFY(d->unsupportedPlugins().contains("kdeconnect_remotecontrol"));
|
||||
QVERIFY(d->supportedPlugins().contains("kdeconnect_remotecontrol"));
|
||||
|
||||
d->setPluginEnabled("kdeconnect_mousepad", true);
|
||||
QCOMPARE(d->isPluginEnabled("kdeconnect_mousepad"), true);
|
||||
QVERIFY(!d->unsupportedPlugins().contains("kdeconnect_remotecontrol"));
|
||||
QVERIFY(d->supportedPlugins().contains("kdeconnect_remotecontrol"));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in a new issue