summaryrefslogtreecommitdiff
path: root/src/intent-server-lib
diff options
context:
space:
mode:
authorRobert Griebl <robert.griebl@pelagicore.com>2018-10-08 18:22:12 +0200
committerDominik Holland <dominik.holland@pelagicore.com>2018-10-12 09:44:35 +0000
commita54b61a7b17c0dc94441dba10243f364a57bcbb6 (patch)
tree6d6e649faa4542456e54266f9aea7cf5dc28be8d /src/intent-server-lib
parent8ced28c4c8d96be151fbb646d9bf08265f29249b (diff)
downloadqtapplicationmanager-a54b61a7b17c0dc94441dba10243f364a57bcbb6.tar.gz
Add support for Intents for both single- and multi-process mode
This commit adds support for Intents, aka. a loosely coupled IPC between arbitrary applications in the AM system. (please read https://wiki.qt.io/QtAppMan-Intents for same background information). The core implementation on both server and client side in this patch is not dependent on the AM itself (apart from the common-lib for convenience sake, but this dependency could easily be removed). There are 2 interfaces that are implemented in the manager-lib and launcher-lib that connect the Intent core to the actual AM and AM's qml runtime launcher. Missing features: - updating the list of intents on app installation and removal - support for background services in the AM itself - support for "file-handles" in the request and reply part - documentation - an example that is better focused on intents themselves Change-Id: Ia7cab2bb569fb2cdb8e5ab7e8167e477cff3068c Reviewed-by: Dominik Holland <dominik.holland@pelagicore.com>
Diffstat (limited to 'src/intent-server-lib')
-rw-r--r--src/intent-server-lib/intent-server-lib.pro25
-rw-r--r--src/intent-server-lib/intent.cpp133
-rw-r--r--src/intent-server-lib/intent.h86
-rw-r--r--src/intent-server-lib/intentserver.cpp417
-rw-r--r--src/intent-server-lib/intentserver.h138
-rw-r--r--src/intent-server-lib/intentserverrequest.cpp121
-rw-r--r--src/intent-server-lib/intentserverrequest.h104
-rw-r--r--src/intent-server-lib/intentserversysteminterface.cpp68
-rw-r--r--src/intent-server-lib/intentserversysteminterface.h89
9 files changed, 1181 insertions, 0 deletions
diff --git a/src/intent-server-lib/intent-server-lib.pro b/src/intent-server-lib/intent-server-lib.pro
new file mode 100644
index 00000000..5dadab01
--- /dev/null
+++ b/src/intent-server-lib/intent-server-lib.pro
@@ -0,0 +1,25 @@
+TEMPLATE = lib
+TARGET = QtAppManIntentServer
+MODULE = appman_intent_server
+
+load(am-config)
+
+QT = core network qml
+QT_FOR_PRIVATE *= \
+ appman_common-private \
+
+CONFIG *= static internal_module
+
+HEADERS += \
+ intent.h \
+ intentserver.h \
+ intentserverrequest.h \
+ intentserversysteminterface.h
+
+SOURCES += \
+ intent.cpp \
+ intentserver.cpp \
+ intentserverrequest.cpp \
+ intentserversysteminterface.cpp
+
+load(qt_module)
diff --git a/src/intent-server-lib/intent.cpp b/src/intent-server-lib/intent.cpp
new file mode 100644
index 00000000..fedb998e
--- /dev/null
+++ b/src/intent-server-lib/intent.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#include "intent.h"
+
+#include <QRegularExpression>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE_AM
+
+QString Intent::id() const
+{
+ return m_id;
+}
+
+Intent::Visibility Intent::visibility() const
+{
+ return m_visibility;
+}
+
+QStringList Intent::requiredCapabilities() const
+{
+ return m_requiredCapabilities;
+}
+
+QVariantMap Intent::parameterMatch() const
+{
+ return m_parameterMatch;
+}
+
+QString Intent::applicationId() const
+{
+ return m_applicationId;
+}
+
+QString Intent::backgroundServiceId() const
+{
+ return m_backgroundServiceId;
+}
+
+bool Intent::checkParameterMatch(const QVariantMap &parameters) const
+{
+ QMapIterator<QString, QVariant> rit(m_parameterMatch);
+ while (rit.hasNext()) {
+ rit.next();
+ const QString &paramName = rit.key();
+ auto pit = parameters.find(paramName);
+ if (pit == parameters.cend())
+ return false;
+
+ const QVariant requiredValue = rit.value();
+ const QVariant actualValue = pit.value();
+
+ switch (requiredValue.type()) {
+ case QVariant::String: {
+ QRegularExpression regexp(requiredValue.toString());
+ auto match = regexp.match(actualValue.toString());
+ if (!match.hasMatch())
+ return false;
+ break;
+ }
+ case QVariant::List: {
+ bool foundMatch = false;
+ const QVariantList rvlist = requiredValue.toList();
+ for (const QVariant &rv2 : rvlist) {
+ if (actualValue.canConvert(rv2.type()) && actualValue == rv2) {
+ foundMatch = true;
+ break;
+ }
+ }
+ if (!foundMatch)
+ return false;
+ break;
+ }
+ default: {
+ if (requiredValue != actualValue)
+ return false;
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+Intent::Intent(const QString &id, const QString &applicationId, const QString &backgroundHandlerId,
+ const QStringList &capabilities, Intent::Visibility visibility, const QVariantMap &parameterMatch)
+ : m_id(id)
+ , m_visibility(visibility)
+ , m_requiredCapabilities(capabilities)
+ , m_parameterMatch(parameterMatch)
+ , m_applicationId(applicationId)
+ , m_backgroundServiceId(backgroundHandlerId)
+{ }
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intent.h b/src/intent-server-lib/intent.h
new file mode 100644
index 00000000..24250615
--- /dev/null
+++ b/src/intent-server-lib/intent.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+#include <QtAppManCommon/global.h>
+
+QT_BEGIN_NAMESPACE_AM
+
+class Intent
+{
+public:
+ enum Visibility {
+ Public,
+ Hidden,
+ Private
+ };
+
+ QString id() const;
+ Visibility visibility() const;
+ QStringList requiredCapabilities() const;
+ QVariantMap parameterMatch() const;
+
+ QString applicationId() const;
+ QString backgroundServiceId() const;
+
+ bool checkParameterMatch(const QVariantMap &parameters) const;
+
+private:
+ Intent(const QString &id, const QString &applicationId, const QString &backgroundHandlerId,
+ const QStringList &capabilities, Intent::Visibility visibility,
+ const QVariantMap &parameterMatch = QVariantMap());
+
+ QString m_id;
+ Visibility m_visibility = Private;
+ QStringList m_requiredCapabilities;
+ QVariantMap m_parameterMatch;
+
+ QString m_applicationId;
+ QString m_backgroundServiceId;
+
+ friend class IntentServer;
+};
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserver.cpp b/src/intent-server-lib/intentserver.cpp
new file mode 100644
index 00000000..285e0770
--- /dev/null
+++ b/src/intent-server-lib/intentserver.cpp
@@ -0,0 +1,417 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#include "intentserver.h"
+#include "intentserversysteminterface.h"
+#include "intentserverrequest.h"
+#include <QtAppManCommon/logging.h>
+
+#include <algorithm>
+
+#include <QRegularExpression>
+#include <QUuid>
+#include <QTimer>
+#include <QDebug>
+
+#include <QQmlEngine>
+#include <QQmlInfo>
+
+
+QT_BEGIN_NAMESPACE_AM
+
+IntentServer *IntentServer::s_instance = nullptr;
+
+IntentServer *IntentServer::createInstance(IntentServerSystemInterface *systemInterface)
+{
+ if (Q_UNLIKELY(s_instance))
+ qFatal("IntentServer::createInstance() was called a second time.");
+ if (Q_UNLIKELY(!systemInterface))
+ qFatal("IntentServer::createInstance() was called without a systemInterface.");
+
+ QScopedPointer<IntentServer> is(new IntentServer(systemInterface));
+ systemInterface->initialize(is.data());
+
+ qmlRegisterSingletonType<IntentServer>("QtApplicationManager", 1, 0, "IntentManager",
+ &IntentServer::instanceForQml);
+ return s_instance = is.take();
+}
+
+IntentServer *IntentServer::instance()
+{
+ if (!s_instance)
+ qFatal("IntentServer::instance() was called before createInstance().");
+ return s_instance;
+}
+
+QObject *IntentServer::instanceForQml(QQmlEngine *, QJSEngine *)
+{
+ QQmlEngine::setObjectOwnership(instance(), QQmlEngine::CppOwnership);
+ return instance();
+}
+
+
+IntentServer::IntentServer(IntentServerSystemInterface *systemInterface, QObject *parent)
+ : QObject(parent)
+ , m_systemInterface(systemInterface)
+{
+ m_systemInterface->setParent(this);
+}
+
+IntentServer::~IntentServer()
+{
+ s_instance = nullptr;
+}
+
+bool IntentServer::addApplication(const QString &applicationId)
+{
+ if (m_knownApplications.contains(applicationId))
+ return false;
+ m_knownApplications << applicationId;
+ return true;
+}
+
+bool IntentServer::addApplicationBackgroundHandler(const QString &applicationId, const QString &backgroundServiceId)
+{
+ if (!m_knownApplications.contains(applicationId))
+ return false;
+ const QStringList services = m_knownBackgroundServices.value(applicationId);
+ if (services.contains(backgroundServiceId))
+ return false;
+ m_knownBackgroundServices[applicationId].append(backgroundServiceId);
+ return true;
+}
+
+const Intent *IntentServer::addIntent(const QString &id, const QString &applicationId, const QStringList &capabilities, Intent::Visibility visibility, const QVariantMap &parameterMatch)
+{
+ return addIntent(id, applicationId, QString(), capabilities, visibility, parameterMatch);
+}
+
+const Intent *IntentServer::addIntent(const QString &id, const QString &applicationId, const QString &backgroundHandlerId, const QStringList &capabilities, Intent::Visibility visibility, const QVariantMap &parameterMatch)
+{
+ if (id.isEmpty()
+ || !m_knownApplications.contains(applicationId)
+ || find(id, applicationId)
+ || (!backgroundHandlerId.isEmpty()
+ && !m_knownBackgroundServices[applicationId].contains(backgroundHandlerId))) {
+ return nullptr;
+ }
+
+ auto intent = new Intent(id, applicationId, backgroundHandlerId, capabilities, visibility,
+ parameterMatch);
+ m_intents << intent;
+ emit intentAdded(intent);
+ return intent;
+}
+
+void IntentServer::removeIntent(const Intent *intent)
+{
+ if (intent) {
+ int pos = m_intents.indexOf(intent);
+
+ if (pos >= 0) {
+ m_intents.removeAt(pos);
+ emit intentRemoved(intent);
+ }
+ }
+}
+
+QVector<const Intent *> IntentServer::all() const
+{
+ return m_intents;
+}
+
+QVector<const Intent *> IntentServer::filterByIntentId(const QString &intentId, const QVariantMap &parameters) const
+{
+ QVector<const Intent *> result;
+ std::copy_if(m_intents.cbegin(), m_intents.cend(), std::back_inserter(result),
+ [intentId, parameters](const Intent *intent) -> bool {
+ return (intent->id() == intentId) && intent->checkParameterMatch(parameters);
+
+ });
+ return result;
+}
+
+QVector<const Intent *> IntentServer::filterByApplicationId(const QString &applicationId, const QVariantMap &parameters) const
+{
+ QVector<const Intent *> result;
+ std::copy_if(m_intents.cbegin(), m_intents.cend(), std::back_inserter(result),
+ [applicationId, parameters](const Intent *intent) -> bool {
+ return (intent->applicationId() == applicationId) && intent->checkParameterMatch(parameters);
+
+ });
+ return result;
+}
+
+const Intent *IntentServer::find(const QString &intentId, const QString &applicationId, const QVariantMap &parameters) const
+{
+ auto it = std::find_if(m_intents.cbegin(), m_intents.cend(),
+ [intentId, applicationId, parameters](const Intent *intent) -> bool {
+ return (intent->applicationId() == applicationId) && (intent->id() == intentId)
+ && intent->checkParameterMatch(parameters);
+ });
+ return (it != m_intents.cend()) ? *it : nullptr;
+}
+
+
+void IntentServer::triggerRequestQueue()
+{
+ QTimer::singleShot(0, this, &IntentServer::processRequestQueue);
+}
+
+void IntentServer::enqueueRequest(IntentServerRequest *irs)
+{
+ qCDebug(LogIntents) << "Enqueueing Intent request:" << irs << irs->id() << irs->state() << m_requestQueue.size();
+ m_requestQueue.enqueue(irs);
+ triggerRequestQueue();
+}
+
+void IntentServer::processRequestQueue()
+{
+ if (m_requestQueue.isEmpty())
+ return;
+
+ IntentServerRequest *irs = m_requestQueue.takeFirst();
+
+ qCDebug(LogIntents) << "Processing intent request" << irs << irs->id() << "in state" << irs->state() << m_requestQueue.size();
+
+ if (irs->state() == IntentServerRequest::State::ReceivedRequest) { // step 1) disambiguate
+ if (!irs->m_actualIntent) {
+ // not disambiguated yet
+
+ if (!isSignalConnected(QMetaMethod::fromSignal(&IntentServer::disambiguationRequest))) {
+ // If the System-UI does not react to the signal, then just use the first match.
+ irs->m_actualIntent = irs->m_intents.first();
+ } else {
+ m_disambiguationQueue.enqueue(irs);
+ irs->setState(IntentServerRequest::State::WaitingForDisambiguation);
+ emit disambiguationRequest(irs->id(), irs->m_intents.first()->id(), irs->m_intents, irs->m_parameters);
+ }
+ }
+ if (irs->intent()) {
+ qCDebug(LogIntents) << "No disambiguation necessary/required for intent" << irs->intent()->id();
+ irs->setState(IntentServerRequest::State::Disambiguated);
+ }
+ }
+
+ if (irs->state() == IntentServerRequest::State::Disambiguated) { // step 2) start app
+ auto handlerIPC = m_systemInterface->findClientIpc(irs->intent()->applicationId());
+ if (!handlerIPC) {
+ qCDebug(LogIntents) << "Intent target app" << irs->intent()->applicationId() << "is not running";
+ m_startingAppQueue.enqueue(irs);
+ irs->setState(IntentServerRequest::State::WaitingForApplicationStart);
+ m_systemInterface->startApplication(irs->intent()->applicationId());
+ } else {
+ qCDebug(LogIntents) << "Intent target app" << irs->intent()->applicationId() << "is already running";
+ irs->setState(IntentServerRequest::State::StartedApplication);
+ }
+ }
+
+ if (irs->state() == IntentServerRequest::State::StartedApplication) { // step 3) send request out
+ auto clientIPC = m_systemInterface->findClientIpc(irs->intent()->applicationId());
+ if (!clientIPC) {
+ qCWarning(LogIntents) << "Could not find an IPC connection for application"
+ << irs->intent()->applicationId() << "to forward the intent request"
+ << irs->id();
+ irs->requestFailed(qSL("No IPC channel to reach target application."));
+ } else {
+ qCDebug(LogIntents) << "Sending intent request to application" << irs->intent()->applicationId();
+ m_sentToAppQueue.enqueue(irs);
+ m_systemInterface->requestToApplication(clientIPC, irs);
+ irs->setState(IntentServerRequest::State::WaitingForReplyFromApplication);
+ }
+ }
+
+ if (irs->state() == IntentServerRequest::State::ReceivedReplyFromApplication) { // step 5) send reply to requesting app
+ auto clientIPC = m_systemInterface->findClientIpc(irs->m_requestingAppId);
+ if (!clientIPC) {
+ qCWarning(LogIntents) << "Could not find an IPC connection for application"
+ << irs->m_requestingAppId << "to forward the Intent reply"
+ << irs->id();
+ } else {
+ qCDebug(LogIntents) << "Forwarding intent reply" << irs->id() << "to requesting application"
+ << irs->m_requestingAppId;
+ m_systemInterface->replyFromSystem(clientIPC, irs);
+ }
+ QTimer::singleShot(0, this, [irs]() { delete irs; }); // aka deleteLater for non-QObject
+ irs = nullptr;
+ }
+
+ triggerRequestQueue();
+}
+
+void IntentServer::acknowledgeDisambiguationRequest(const QUuid &requestId, const Intent *intent)
+{
+ IntentServerRequest *irs = nullptr;
+ for (int i = 0; i < m_disambiguationQueue.size(); ++i) {
+ if (m_disambiguationQueue.at(i)->id() == requestId) {
+ irs = m_disambiguationQueue.takeAt(i);
+ break;
+ }
+ }
+
+ if (!irs) {
+ qmlWarning(this) << "Got a disambiguation acknowledge for intent" << requestId
+ << "but no disambiguation was expected for this intent";
+ } else {
+ if (irs->m_intents.contains(intent)) {
+ irs->m_actualIntent = intent;
+ irs->setState(IntentServerRequest::State::Disambiguated);
+ } else {
+ qCWarning(LogIntents) << "IntentServer::acknowledgeDisambiguationRequest for intent"
+ << requestId << "tried to disambiguate to the intent"
+ << (intent ? intent->id() : qSL("<null>"))
+ << "which was not in the list of available disambiguations";
+
+ irs->requestFailed(qSL("Failed to disambiguate"));
+ }
+ enqueueRequest(irs);
+ }
+}
+
+void IntentServer::applicationWasStarted(const QString &applicationId)
+{
+ // check if any intent request is waiting for this app to start
+ bool foundOne = false;
+ for (int i = 0; i < m_startingAppQueue.size(); ++i) {
+ auto irs = m_startingAppQueue.at(i);
+ if (irs->intent()->applicationId() == applicationId) {
+ qCDebug(LogIntents) << "Intent request" << irs->intent()->id()
+ << "can now be forwarded to application" << applicationId;
+
+ irs->setState(IntentServerRequest::State::StartedApplication);
+ m_requestQueue << m_startingAppQueue.takeAt(i);
+ foundOne = true;
+ }
+ }
+ if (foundOne)
+ triggerRequestQueue();
+}
+
+void IntentServer::replyFromApplication(const QString &replyingApplicationId, const QString &requestId, bool error, const QVariantMap &result)
+{
+ IntentServerRequest *irs = nullptr;
+ for (int i = 0; i < m_sentToAppQueue.size(); ++i) {
+ if (m_sentToAppQueue.at(i)->id() == requestId) {
+ irs = m_sentToAppQueue.takeAt(i);
+ break;
+ }
+ }
+
+ if (!irs) {
+ qCWarning(LogIntents) << "Got a reply for intent" << requestId << "from application"
+ << replyingApplicationId << "but no reply was expected for this intent";
+ } else {
+ if (irs->intent()->applicationId() != replyingApplicationId) {
+ qCWarning(LogIntents) << "Got a reply for intent" << irs->id() << "from application"
+ << replyingApplicationId << "but expected a reply from"
+ << irs->intent()->applicationId() << "instead";
+ irs->requestFailed(qSL("Request reply received from wrong application"));
+ } else {
+ QString errorMessage;
+ if (error) {
+ errorMessage = result.value(qSL("errorMessage")).toString();
+ qCDebug(LogIntents) << "Got an error reply for intent" << irs->id() << "from application"
+ << replyingApplicationId << ":" << errorMessage;
+ irs->requestFailed(errorMessage);
+ } else {
+ qCDebug(LogIntents) << "Got a reply for intent" << irs->id() << "from application"
+ << replyingApplicationId << ":" << result;
+ irs->requestSucceeded(result);
+ }
+ }
+ enqueueRequest(irs);
+ }
+}
+
+IntentServerRequest *IntentServer::requestToSystem(const QString &requestingApplicationId, const QString &intentId, const QString &applicationId, const QVariantMap &parameters)
+{
+ qCDebug(LogIntents) << "Incoming intent request" << intentId << "from application"
+ << requestingApplicationId << "to application" << applicationId;
+
+ QVector<const Intent *> intents;
+ if (applicationId.isEmpty())
+ intents = filterByIntentId(intentId, parameters);
+ else if (const Intent *intent = find(intentId, applicationId, parameters))
+ intents << intent;
+
+ if (intents.isEmpty()) {
+ qCWarning(LogIntents) << "Unknown intent" << intentId << "was requested from application"
+ << requestingApplicationId;
+ return nullptr;
+ }
+
+ // filter on visibility and capabilities
+ //TODO: move this part to a separate filter() function?
+ for (auto it = intents.begin(); it != intents.end(); ) {
+ const Intent *intent = *it;
+ bool keep = true;
+
+ if ((intent->visibility() == Intent::Private)
+ && (intent->applicationId() != requestingApplicationId)) {
+ qCDebug(LogIntents) << "Not considering" << intent->id() << "/" << intent->applicationId()
+ << "due to private visibility";
+ keep = false;
+ }
+ else if (!intent->requiredCapabilities().isEmpty()
+ && !m_systemInterface->checkApplicationCapabilities(requestingApplicationId,
+ intent->requiredCapabilities())) {
+ qCDebug(LogIntents) << "Not considering" << intent->id() << "/" << intent->applicationId()
+ << "due to missing capabilities";
+ keep = false;
+ }
+ if (!keep)
+ intents.erase(it);
+ else
+ ++it;
+ }
+
+ if (intents.isEmpty()) {
+ qCWarning(LogIntents) << "Inaccessible intent" << intentId << "was requested from application"
+ << requestingApplicationId;
+ return nullptr;
+ }
+
+ auto irs = new IntentServerRequest(true, requestingApplicationId, intents, parameters);
+ enqueueRequest(irs);
+ return irs;
+}
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserver.h b/src/intent-server-lib/intentserver.h
new file mode 100644
index 00000000..2333be08
--- /dev/null
+++ b/src/intent-server-lib/intentserver.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+#include <QVariantMap>
+#include <QVector>
+#include <QUuid>
+#include <QQueue>
+#include <QtAppManCommon/global.h>
+#include <QtAppManIntentServer/intent.h>
+
+QT_FORWARD_DECLARE_CLASS(QIODevice)
+QT_FORWARD_DECLARE_CLASS(QQmlEngine)
+QT_FORWARD_DECLARE_CLASS(QJSEngine)
+
+
+QT_BEGIN_NAMESPACE_AM
+
+class AbstractRuntime;
+class IntentServerRequest;
+class IntentServerSystemInterface;
+
+class IntentServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~IntentServer() override;
+ static IntentServer *createInstance(IntentServerSystemInterface *systemInterface);
+ static IntentServer *instance();
+ static QObject *instanceForQml(QQmlEngine *qmlEngine, QJSEngine *);
+
+ Q_INVOKABLE QVector<const Intent *> all() const;
+ Q_INVOKABLE QVector<const Intent *> filterByIntentId(const QString &intentId,
+ const QVariantMap &parameters = QVariantMap{}) const;
+ Q_INVOKABLE QVector<const Intent *> filterByApplicationId(const QString &applicationId,
+ const QVariantMap &parameters = QVariantMap{}) const;
+ Q_INVOKABLE const Intent *find(const QString &intentId, const QString &applicationId,
+ const QVariantMap &parameters = QVariantMap{}) const;
+
+
+ bool addApplication(const QString &applicationId);
+ bool addApplicationBackgroundHandler(const QString &applicationId, const QString &backgroundServiceId);
+
+ const Intent *addIntent(const QString &id, const QString &applicationId,
+ const QStringList &capabilities, Intent::Visibility visibility,
+ const QVariantMap &parameterMatch = QVariantMap());
+
+ const Intent *addIntent(const QString &id, const QString &applicationId,
+ const QString &backgroundHandlerId,
+ const QStringList &capabilities, Intent::Visibility visibility,
+ const QVariantMap &parameterMatch = QVariantMap());
+
+ void removeIntent(const Intent *intent);
+
+signals:
+ void intentAdded(const Intent *intent);
+ void intentRemoved(const Intent *intent);
+
+ void disambiguationRequest(const QUuid &requestId, const QString &intentId,
+ const QVector<const Intent *> &intents, const QVariantMap &parameters);
+
+
+public slots:
+ void acknowledgeDisambiguationRequest(const QUuid &requestId, const Intent *intent);
+
+private:
+ void applicationWasStarted(const QString &applicationId);
+ void replyFromApplication(const QString &replyingApplicationId, const QString &requestId,
+ bool error, const QVariantMap &result);
+ IntentServerRequest *requestToSystem(const QString &requestingApplicationId, const QString &intentId,
+ const QString &applicationId, const QVariantMap &parameters);
+
+ void triggerRequestQueue();
+ void enqueueRequest(IntentServerRequest *irs);
+ void processRequestQueue();
+
+private:
+ IntentServer(IntentServerSystemInterface *systemInterface, QObject *parent = nullptr);
+ Q_DISABLE_COPY(IntentServer)
+ static IntentServer *s_instance;
+
+ QStringList m_knownApplications;
+ QMap<QString, QStringList> m_knownBackgroundServices;
+
+ QQueue<IntentServerRequest *> m_requestQueue;
+
+ QQueue<IntentServerRequest *> m_disambiguationQueue;
+ QQueue<IntentServerRequest *> m_startingAppQueue;
+ QQueue<IntentServerRequest *> m_sentToAppQueue;
+
+ QVector<const Intent *> m_intents;
+
+ IntentServerSystemInterface *m_systemInterface;
+ friend class IntentServerSystemInterface;
+};
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserverrequest.cpp b/src/intent-server-lib/intentserverrequest.cpp
new file mode 100644
index 00000000..df71a93f
--- /dev/null
+++ b/src/intent-server-lib/intentserverrequest.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#include <QDebug>
+#include "intentserverrequest.h"
+
+QT_BEGIN_NAMESPACE_AM
+
+IntentServerRequest::IntentServerRequest(bool external, const QString &requestingAppId,
+ const QVector<const Intent *> &intents,
+ const QVariantMap &parameters)
+ : m_id(QUuid::createUuid())
+ , m_state(State::ReceivedRequest)
+ , m_external(external)
+ , m_requestingAppId(requestingAppId)
+ , m_intents(intents)
+ , m_parameters(parameters)
+ , m_actualIntent(intents.size() == 1 ? intents.first() : nullptr)
+{ }
+
+IntentServerRequest::State IntentServerRequest::state() const
+{
+ return m_state;
+}
+
+QUuid IntentServerRequest::id() const
+{
+ return m_id;
+}
+
+const Intent *IntentServerRequest::intent() const
+{
+ return m_actualIntent;
+}
+
+QVariantMap IntentServerRequest::parameters() const
+{
+ return m_parameters;
+}
+
+bool IntentServerRequest::isWaiting() const
+{
+ switch (state()) {
+ case State::WaitingForDisambiguation:
+ case State::WaitingForApplicationStart:
+ case State::WaitingForReplyFromApplication:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IntentServerRequest::hasSucceeded() const
+{
+ return m_succeeded;
+}
+
+QVariantMap IntentServerRequest::result() const
+{
+ return m_result;
+}
+
+void IntentServerRequest::requestFailed(const QString &errorMessage)
+{
+ m_succeeded = false;
+ m_result.clear();
+ m_result[qSL("errorMessage")] = errorMessage;
+ m_state = State::ReceivedReplyFromApplication;
+}
+
+void IntentServerRequest::requestSucceeded(const QVariantMap &result)
+{
+ m_succeeded = true;
+ m_result = result;
+ m_state = State::ReceivedReplyFromApplication;
+}
+
+void IntentServerRequest::setState(IntentServerRequest::State newState)
+{
+ m_state = newState;
+}
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserverrequest.h b/src/intent-server-lib/intentserverrequest.h
new file mode 100644
index 00000000..d523adde
--- /dev/null
+++ b/src/intent-server-lib/intentserverrequest.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+#include <QString>
+#include <QVariantMap>
+#include <QUuid>
+#include <QVector>
+#include <QtAppManCommon/global.h>
+
+QT_BEGIN_NAMESPACE_AM
+
+class IntentServer;
+class Intent;
+
+class IntentServerRequest
+{
+ Q_GADGET
+
+public:
+ IntentServerRequest(bool external, const QString &requestingAppId, const QVector<const Intent *> &intents,
+ const QVariantMap &parameters);
+
+ enum class State {
+ ReceivedRequest,
+ WaitingForDisambiguation,
+ Disambiguated,
+ WaitingForApplicationStart,
+ StartedApplication,
+ WaitingForReplyFromApplication,
+ ReceivedReplyFromApplication,
+ };
+
+ Q_ENUM(State)
+
+ State state() const;
+ QUuid id() const;
+ const QtAM::Intent *intent() const;
+ QVariantMap parameters() const;
+ bool isWaiting() const;
+ bool hasSucceeded() const;
+ QVariantMap result() const;
+
+ void requestFailed(const QString &errorMessage);
+ void requestSucceeded(const QVariantMap &result);
+
+private:
+ void setState(State newState);
+
+private:
+ QUuid m_id;
+ State m_state;
+ bool m_external;
+ bool m_succeeded = false;
+ QString m_requestingAppId;
+ QVector<const Intent *> m_intents;
+ QVariantMap m_parameters;
+ QVariantMap m_result;
+ const Intent *m_actualIntent;
+
+ friend class IntentServer;
+};
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserversysteminterface.cpp b/src/intent-server-lib/intentserversysteminterface.cpp
new file mode 100644
index 00000000..bc8310ac
--- /dev/null
+++ b/src/intent-server-lib/intentserversysteminterface.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#include "intentserversysteminterface.h"
+#include "intentserver.h"
+
+QT_BEGIN_NAMESPACE_AM
+
+void IntentServerSystemInterface::initialize(IntentServer *intentServer)
+{
+ m_is = intentServer;
+
+ connect(this, &IntentServerSystemInterface::replyFromApplication,
+ m_is, &IntentServer::replyFromApplication);
+ connect(this, &IntentServerSystemInterface::applicationWasStarted,
+ m_is, &IntentServer::applicationWasStarted);
+}
+
+IntentServer *IntentServerSystemInterface::intentServer() const
+{
+ return m_is;
+}
+
+IntentServerRequest *IntentServerSystemInterface::requestToSystem(const QString &requestingApplicationId, const QString &intentId, const QString &applicationId, const QVariantMap &parameters)
+{
+ // not possible to do via signal, due to return value
+ return m_is->requestToSystem(requestingApplicationId, intentId, applicationId, parameters);
+}
+
+QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentserversysteminterface.h b/src/intent-server-lib/intentserversysteminterface.h
new file mode 100644
index 00000000..f75cb7e2
--- /dev/null
+++ b/src/intent-server-lib/intentserversysteminterface.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Pelagicore Application Manager.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite licenses may use
+** this file in accordance with the commercial license agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and The Qt Company. For
+** licensing terms and conditions see https://www.qt.io/terms-conditions.
+** For further information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QObject>
+#include <QVariantMap>
+#include <QString>
+#include <QStringList>
+#include <QtAppManCommon/global.h>
+
+QT_BEGIN_NAMESPACE_AM
+
+class IntentServer;
+class IntentServerRequest;
+
+class IntentServerSystemInterface : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~IntentServerSystemInterface() = default;
+
+ virtual void initialize(IntentServer *intentServer);
+ IntentServer *intentServer() const;
+
+ class IpcConnection;
+ virtual IpcConnection *findClientIpc(const QString &appId) = 0;
+
+ virtual void startApplication(const QString &appId) = 0;
+
+
+ virtual bool checkApplicationCapabilities(const QString &applicationId,
+ const QStringList &requiredCapabilities) = 0;
+
+ IntentServerRequest *requestToSystem(const QString &requestingApplicationId, const QString &intentId,
+ const QString &applicationId, const QVariantMap &parameters);
+ virtual void replyFromSystem(IpcConnection *clientIPC, IntentServerRequest *irs) = 0;
+
+ virtual void requestToApplication(IpcConnection *clientIPC, IntentServerRequest *irs) = 0;
+
+signals:
+ void applicationWasStarted(const QString &appId);
+ void replyFromApplication(const QString &replyingApplicationId, const QString &requestId,
+ bool error, const QVariantMap &result);
+
+private:
+ IntentServer *m_is = nullptr;
+};
+
+QT_END_NAMESPACE_AM