diff options
author | Robert Griebl <robert.griebl@pelagicore.com> | 2018-10-08 18:22:12 +0200 |
---|---|---|
committer | Dominik Holland <dominik.holland@pelagicore.com> | 2018-10-12 09:44:35 +0000 |
commit | a54b61a7b17c0dc94441dba10243f364a57bcbb6 (patch) | |
tree | 6d6e649faa4542456e54266f9aea7cf5dc28be8d | |
parent | 8ced28c4c8d96be151fbb646d9bf08265f29249b (diff) |
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>
56 files changed, 3518 insertions, 15 deletions
diff --git a/examples/applicationmanager/applicationmanager.pro b/examples/applicationmanager/applicationmanager.pro index 4f173f0d..6b7b8034 100644 --- a/examples/applicationmanager/applicationmanager.pro +++ b/examples/applicationmanager/applicationmanager.pro @@ -7,6 +7,7 @@ SUBDIRS = \ monitor \ multi-views \ startup-plugin \ + intents \ # remove the !headless and handle this in the example when we switch to the new configure system !headless:SUBDIRS += \ diff --git a/examples/applicationmanager/hello-world/apps/hello-world.red/info.yaml b/examples/applicationmanager/hello-world/apps/hello-world.red/info.yaml index fe0be9af..d6696c56 100644 --- a/examples/applicationmanager/hello-world/apps/hello-world.red/info.yaml +++ b/examples/applicationmanager/hello-world/apps/hello-world.red/info.yaml @@ -7,3 +7,4 @@ code: 'main.qml' runtime: 'qml' name: en: 'Hello Red' +capabilities: [ "foo" ] diff --git a/examples/applicationmanager/intents/apps/hello-world.blue/icon.png b/examples/applicationmanager/intents/apps/hello-world.blue/icon.png Binary files differnew file mode 100644 index 00000000..be6ffc57 --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.blue/icon.png diff --git a/examples/applicationmanager/intents/apps/hello-world.blue/info.yaml b/examples/applicationmanager/intents/apps/hello-world.blue/info.yaml new file mode 100644 index 00000000..876f2ed9 --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.blue/info.yaml @@ -0,0 +1,15 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'hello-world.blue' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Hello Blue' + +intents: +- id: io.qt.blue + requiredCapabilities: [ "foo" ] +- id: io.qt.blue.private + visibility: private diff --git a/examples/applicationmanager/intents/apps/hello-world.blue/main.qml b/examples/applicationmanager/intents/apps/hello-world.blue/main.qml new file mode 100644 index 00000000..81ea5b22 --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.blue/main.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + color: "blue" + + Text { + anchors.centerIn: parent + text: "Hello World!" + } + + IntentHandler { + intentIds: [ "io.qt.blue" ] + onReceivedRequest: { + request.sendReply({ "this": "is a result", "nested": { "a": 1, "b": 2 } }) + } + } +} diff --git a/examples/applicationmanager/intents/apps/hello-world.green/icon.png b/examples/applicationmanager/intents/apps/hello-world.green/icon.png Binary files differnew file mode 100644 index 00000000..b149340c --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.green/icon.png diff --git a/examples/applicationmanager/intents/apps/hello-world.green/info.yaml b/examples/applicationmanager/intents/apps/hello-world.green/info.yaml new file mode 100644 index 00000000..4455d4b5 --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.green/info.yaml @@ -0,0 +1,13 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'hello-world.green' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Hello Green' + +intents: + - id: io.qt.green + parameterMatch: { "url": "^image/.*$" } diff --git a/examples/applicationmanager/intents/apps/hello-world.green/main.qml b/examples/applicationmanager/intents/apps/hello-world.green/main.qml new file mode 100644 index 00000000..2c0dea9e --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.green/main.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + color: "Green" + + Text { + anchors.centerIn: parent + text: "Hello World!" + } +} diff --git a/examples/applicationmanager/intents/apps/hello-world.red/icon.png b/examples/applicationmanager/intents/apps/hello-world.red/icon.png Binary files differnew file mode 100644 index 00000000..04ca44dd --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.red/icon.png diff --git a/examples/applicationmanager/intents/apps/hello-world.red/info.yaml b/examples/applicationmanager/intents/apps/hello-world.red/info.yaml new file mode 100644 index 00000000..2663f608 --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.red/info.yaml @@ -0,0 +1,14 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'hello-world.red' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Hello Red' + +capabilities: 'foo' + +intents: +- id: io.qt.red diff --git a/examples/applicationmanager/intents/apps/hello-world.red/main.qml b/examples/applicationmanager/intents/apps/hello-world.red/main.qml new file mode 100644 index 00000000..b1cce59b --- /dev/null +++ b/examples/applicationmanager/intents/apps/hello-world.red/main.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + color: "red" + + Text { + anchors.fill: parent + text: "Hello World!" + + MouseArea { + anchors.fill: parent + onClicked: { + var request = ApplicationInterface.createIntentRequest("io.qt.blue", { "hello": "world", "number": 42 }) + request.onFinished.connect(function() { + console.warn("Request finished " + request.requestId + + (request.succeeded ? (" Result: " + JSON.stringify(request.result)) + : (" Error: " + request.errorString))) + }) + } + } + } +} diff --git a/examples/applicationmanager/intents/intents.pro b/examples/applicationmanager/intents/intents.pro new file mode 100644 index 00000000..d519e1a0 --- /dev/null +++ b/examples/applicationmanager/intents/intents.pro @@ -0,0 +1,26 @@ +TEMPLATE = app +CONFIG += am-systemui + +OTHER_FILES += \ + *.qml \ + apps/hello-world.blue/*.yaml \ + apps/hello-world.blue/*.qml \ + apps/hello-world.blue/*.png \ + apps/hello-world.green/*.yaml \ + apps/hello-world.green/*.qml \ + apps/hello-world.green/*.png \ + apps/hello-world.red/*.yaml \ + apps/hello-world.red/*.qml \ + apps/hello-world.red/*.png \ + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/intents +INSTALLS += target + +AM_COPY_DIRECTORIES += apps +AM_COPY_FILES += system-ui.qml + +AM_DEFAULT_ARGS=--builtin-apps-manifest-dir $$target.path/apps $$target.path/system-ui.qml + +example_sources.path = $$target.path +example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES +INSTALLS += example_sources diff --git a/examples/applicationmanager/intents/system-ui.qml b/examples/applicationmanager/intents/system-ui.qml new file mode 100644 index 00000000..9bca06a3 --- /dev/null +++ b/examples/applicationmanager/intents/system-ui.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +Item { + width: 800 + height: 600 + + // Show application names and icons + Column { + spacing: 20 + Repeater { + model: ApplicationManager + Column { + Image { + source: model.icon + MouseArea { + anchors.fill: parent + onClicked: model.isRunning ? application.stop() : application.start() + } + } + Text { + font.pixelSize: 20 + text: model.name + } + } + } + } + + // Show windows + Column { + anchors.right: parent.right + Repeater { + model: WindowManager + WindowItem { + width: 600 + height: 200 + window: model.window + } + } + } +} diff --git a/src/application-lib/applicationinfo.cpp b/src/application-lib/applicationinfo.cpp index 1ef41d79..90897c6d 100644 --- a/src/application-lib/applicationinfo.cpp +++ b/src/application-lib/applicationinfo.cpp @@ -330,6 +330,11 @@ QVariantMap ApplicationInfo::openGLConfiguration() const return m_openGLConfiguration; } +QVariantList ApplicationInfo::intents() const +{ + return m_intents; +} + void ApplicationInfo::setBuiltIn(bool builtIn) { m_builtIn = builtIn; diff --git a/src/application-lib/applicationinfo.h b/src/application-lib/applicationinfo.h index f4980c71..6f70f73c 100644 --- a/src/application-lib/applicationinfo.h +++ b/src/application-lib/applicationinfo.h @@ -125,6 +125,7 @@ public: QStringList categories() const; QString version() const; QVariantMap openGLConfiguration() const; + QVariantList intents() const; bool supportsApplicationInterface() const; void setSupportsApplicationInterface(bool supportsAppInterface); @@ -157,6 +158,7 @@ private: QString m_version; QVariantMap m_openGLConfiguration; + QVariantList m_intents; // added by installer QScopedPointer<InstallationReport> m_installationReport; diff --git a/src/application-lib/applicationinterface.h b/src/application-lib/applicationinterface.h index 0445a51a..8230a0a4 100644 --- a/src/application-lib/applicationinterface.h +++ b/src/application-lib/applicationinterface.h @@ -69,6 +69,7 @@ public: #ifdef Q_QDOC Q_INVOKABLE Notification *createNotification(); + Q_INVOKABLE IntentRequest *createIntentRequest(); Q_INVOKABLE virtual void acknowledgeQuit() const; #endif Q_SCRIPTABLE virtual void finishedInitialization() = 0; diff --git a/src/application-lib/yamlapplicationscanner.cpp b/src/application-lib/yamlapplicationscanner.cpp index 3d0a3ae9..68e004e1 100644 --- a/src/application-lib/yamlapplicationscanner.cpp +++ b/src/application-lib/yamlapplicationscanner.cpp @@ -184,6 +184,8 @@ AbstractApplicationInfo *YamlApplicationScanner::scanInternal(const QString &fil if (!validKeys.contains(it.key())) throw Exception(Error::Parse, "the 'opengl' object contains the unsupported key '%1'").arg(it.key()); } + } else if (field == "intents") { + appInfo->m_intents = v.toList(); } else { unknownField = true; } diff --git a/src/common-lib/error.h b/src/common-lib/error.h index 3018f195..e6130b61 100644 --- a/src/common-lib/error.h +++ b/src/common-lib/error.h @@ -69,6 +69,8 @@ enum class Error { MediumNotAvailable = 50, WrongMedium = 51, + + Intents = 60 }; QT_END_NAMESPACE_AM diff --git a/src/common-lib/logging.cpp b/src/common-lib/logging.cpp index 6788f170..dd8207f6 100644 --- a/src/common-lib/logging.cpp +++ b/src/common-lib/logging.cpp @@ -83,6 +83,7 @@ QT_BEGIN_NAMESPACE_AM \li \c am.qml.ipc - QML IPC \li \c am.notify - Notification sub-system \li \c am.deployment - Deployment hints +\li \c am.intent - Intent sub-system \li \c general - General messages not part of any ApplicationManager sub-system \endlist //! [am-logging-categories] @@ -96,8 +97,9 @@ QDLT_LOGGING_CATEGORY(LogWaylandDebug, "am.wayland.debug", "WAYL", "Wayland prot QDLT_LOGGING_CATEGORY(LogQml, "am.qml", "QML", "QML messages") QDLT_LOGGING_CATEGORY(LogQmlRuntime, "am.runtime.qml", "QMRT", "QML runtime") QDLT_LOGGING_CATEGORY(LogQmlIpc, "am.qml.ipc", "QMIP", "QML IPC") -QDLT_LOGGING_CATEGORY(LogNotifications, "am.notify", "NTFY", "Notification sub-system") +QDLT_LOGGING_CATEGORY(LogNotifications, "am.notify", "NTFY", "Notifications sub-system") QDLT_LOGGING_CATEGORY(LogDeployment, "am.deployment", "DPLM", "Deployment hints") +QDLT_LOGGING_CATEGORY(LogIntents, "am.intent", "INTN", "Intents sub-system") QDLT_LOGGING_CATEGORY(LogGeneral, "general", "GEN", "General messages not part of any ApplicationManager sub-system") QDLT_FALLBACK_CATEGORY(LogGeneral) diff --git a/src/common-lib/logging.h b/src/common-lib/logging.h index 2acbb100..d0a0fc3f 100644 --- a/src/common-lib/logging.h +++ b/src/common-lib/logging.h @@ -55,6 +55,7 @@ Q_DECLARE_LOGGING_CATEGORY(LogNotifications) Q_DECLARE_LOGGING_CATEGORY(LogQmlRuntime) Q_DECLARE_LOGGING_CATEGORY(LogQmlIpc) Q_DECLARE_LOGGING_CATEGORY(LogDeployment) +Q_DECLARE_LOGGING_CATEGORY(LogIntents) class Logging { diff --git a/src/dbus-lib/dbus-lib.pro b/src/dbus-lib/dbus-lib.pro index 90cb1eda..cc39bf5b 100644 --- a/src/dbus-lib/dbus-lib.pro +++ b/src/dbus-lib/dbus-lib.pro @@ -48,6 +48,7 @@ OTHER_FILES = \ io.qt.applicationinstaller.xml \ io.qt.applicationmanager.applicationinterface.xml \ io.qt.applicationmanager.runtimeinterface.xml \ + io.qt.applicationmanager.intentinterface.xml \ io.qt.applicationmanager.xml \ io.qt.windowmanager.xml \ org.freedesktop.notifications.xml \ diff --git a/src/dbus-lib/io.qt.applicationmanager.intentinterface.xml b/src/dbus-lib/io.qt.applicationmanager.intentinterface.xml new file mode 100644 index 00000000..8b452eb0 --- /dev/null +++ b/src/dbus-lib/io.qt.applicationmanager.intentinterface.xml @@ -0,0 +1,31 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="io.qt.ApplicationManager.IntentInterface"> + <method name="requestToSystem"> + <arg type="s" direction="out"/> + <arg name="id" type="s" direction="in"/> + <arg name="applicationId" type="s" direction="in"/> + <arg name="parameters" type="a{sv}" direction="in"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/> + </method> + <signal name="replyFromSystem"> + <arg name="requestId" type="s" direction="out"/> + <arg name="error" type="b" direction="out"/> + <arg name="result" type="a{sv}" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out2" value="QVariantMap"/> + </signal> + <signal name="requestToApplication"> + <arg name="requestId" type="s" direction="out"/> + <arg name="id" type="s" direction="out"/> + <arg name="applicationId" type="s" direction="out"/> + <arg name="parameters" type="a{sv}" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out3" value="QVariantMap"/> + </signal> + <method name="replyFromApplication"> + <arg name="requestId" type="s" direction="in"/> + <arg name="error" type="b" direction="in"/> + <arg name="result" type="a{sv}" direction="in"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/> + </method> + </interface> +</node> diff --git a/src/intent-client-lib/intent-client-lib.pro b/src/intent-client-lib/intent-client-lib.pro new file mode 100644 index 00000000..e1dcabb4 --- /dev/null +++ b/src/intent-client-lib/intent-client-lib.pro @@ -0,0 +1,25 @@ +TEMPLATE = lib +TARGET = QtAppManIntentClient +MODULE = appman_intent_client + +load(am-config) + +QT = core network qml +QT_FOR_PRIVATE *= \ + appman_common-private \ + +CONFIG *= static internal_module + +HEADERS += \ + intenthandler.h \ + intentclient.h \ + intentclientrequest.h \ + intentclientsysteminterface.h + +SOURCES += \ + intenthandler.cpp \ + intentclient.cpp \ + intentclientrequest.cpp \ + intentclientsysteminterface.cpp + +load(qt_module) diff --git a/src/intent-client-lib/intentclient.cpp b/src/intent-client-lib/intentclient.cpp new file mode 100644 index 00000000..21a33f06 --- /dev/null +++ b/src/intent-client-lib/intentclient.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** 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 <QScopedPointer> +#include <qqml.h> +#include <QQmlInfo> + +#include "intentclient.h" +#include "intentclientsysteminterface.h" +#include "intentclientrequest.h" +#include "intenthandler.h" +#include "logging.h" + +#include <exception> + +QT_BEGIN_NAMESPACE_AM + +IntentClient *IntentClient::s_instance = nullptr; + +IntentClient *IntentClient::createInstance(IntentClientSystemInterface *systemInterface) +{ + if (Q_UNLIKELY(s_instance)) + qFatal("IntentClient::createInstance() was called a second time."); + if (Q_UNLIKELY(!systemInterface)) + qFatal("IntentClient::createInstance() was called without a systemInterface."); + + QScopedPointer<IntentClient> ic(new IntentClient(systemInterface)); + try { + systemInterface->initialize(ic.data()); + } catch (const std::exception &exc) { + qCWarning(LogIntents) << "Failed to initialize IntentClient:" << exc.what(); + return nullptr; + } + + qmlRegisterUncreatableType<IntentClientRequest>("QtApplicationManager", 1, 0, "IntentRequest", + qSL("Cannot create objects of type IntentRequest")); + qmlRegisterType<IntentHandler>("QtApplicationManager", 1, 0, "IntentHandler"); + + return s_instance = ic.take(); +} + +IntentClient *IntentClient::instance() +{ + if (!s_instance) + qFatal("IntentClient::instance() was called before createInstance()."); + return s_instance; +} + +IntentClient::IntentClient(IntentClientSystemInterface *systemInterface, QObject *parent) + : QObject(parent) + , m_systemInterface(systemInterface) +{ + m_systemInterface->setParent(this); +} + +IntentClient::~IntentClient() +{ + s_instance = nullptr; +} + +void IntentClient::registerHandler(IntentHandler *handler) +{ + const QStringList intentIds = handler->intentIds(); + for (auto intentId : intentIds) { + if (m_handlers.contains(intentId)) { + qmlWarning(handler) << "Double registration for intent" << intentId << "detected. " + "Only the handler that registered first will be active."; + } else { + m_handlers.insert(intentId, handler); + } + } +} + +void IntentClient::unregisterHandler(IntentHandler *handler) +{ + const QStringList intentIds = handler->intentIds(); + for (auto intentId : intentIds) + m_handlers.remove(intentId); +} + +IntentClientRequest *IntentClient::requestToSystem(const QString &requestingApplicationId, + const QString &intentId, const QString &applicationId, + const QVariantMap ¶meters) +{ + IntentClientRequest *ir = new IntentClientRequest(IntentClientRequest::Direction::ToSystem, + requestingApplicationId, QUuid(), + intentId, applicationId, parameters); + + qCDebug(LogIntents) << "Application" << requestingApplicationId << "created an intent request for" + << intentId << "(application:" << applicationId << ")"; + m_systemInterface->requestToSystem(ir); + return ir; +} + +void IntentClient::requestToSystemFinished(IntentClientRequest *icr, const QUuid &newRequestId, bool error, const QString &errorMessage) +{ + if (error) { + icr->setErrorMessage(errorMessage); + icr->deleteLater(); + } else if (newRequestId.isNull()) { + icr->setErrorMessage(qL1S("No matching Intent found in the system")); + icr->deleteLater(); + } else { + icr->setRequestId(newRequestId); + m_waiting << icr; + } +} + +void IntentClient::replyFromSystem(const QString &requestId, bool error, const QVariantMap &result) +{ + IntentClientRequest *icr = nullptr; + auto it = std::find_if(m_waiting.begin(), m_waiting.end(), + [requestId](IntentClientRequest *ir) -> bool { + return (ir->id() == requestId); +}); + if (it == m_waiting.cend()) { + qCWarning(LogIntents) << "IntentClient received an unexpected intent reply for request" + << requestId << " succeeded:" << error << "error:" + << result.value(qL1S("errorMessage")).toString() << "result:" << result; + return; + } + icr = *it; + m_waiting.erase(it); + + if (error) + icr->setErrorMessage(result.value(qSL("errorMessage")).toString()); + else + icr->setResult(result); + icr->deleteLater(); + + qCDebug(LogIntents) << "Application" << icr->requestingApplicationId() << "received an intent reply for" + << icr->intentId() << " succeeded:" << icr->succeeded() << "error:" + << icr->errorMessage() << "result:" << icr->result(); +} + +void IntentClient::requestToApplication(const QString &requestId, const QString &intentId, + const QString &applicationId, const QVariantMap ¶meters) +{ + //TODO: we should sanity check the applicationId + qCDebug(LogIntents) << "Incoming intent request" << requestId << " to application" << applicationId + << "for intent" << intentId << "parameters" << parameters; + + IntentClientRequest *icr = new IntentClientRequest(IntentClientRequest::Direction::ToApplication, + QString(), requestId, intentId, applicationId, + parameters); + + IntentHandler *handler = m_handlers.value(intentId); + if (handler) { + emit handler->receivedRequest(icr); + } else { + qCDebug(LogIntents) << "No Intent handler registered for intent" << intentId; + errorReplyFromApplication(icr, qSL("No matching IntentHandler found.")); + } +} + +void IntentClient::replyFromApplication(IntentClientRequest *icr, const QVariantMap &result) +{ + if (!icr || icr->m_direction != IntentClientRequest::Direction::ToApplication) + return; + icr->m_succeeded = true; + icr->m_result = result; + + m_systemInterface->replyFromApplication(icr); + icr->deleteLater(); +} + +void IntentClient::errorReplyFromApplication(IntentClientRequest *icr, const QString &errorMessage) +{ + if (!icr || icr->m_direction != IntentClientRequest::Direction::ToApplication) + return; + icr->m_succeeded = false; + icr->m_result = QVariantMap{ { qSL("errorMessage"), errorMessage } }; + + m_systemInterface->replyFromApplication(icr); + icr->deleteLater(); +} + +QT_END_NAMESPACE_AM + diff --git a/src/intent-client-lib/intentclient.h b/src/intent-client-lib/intentclient.h new file mode 100644 index 00000000..730647e1 --- /dev/null +++ b/src/intent-client-lib/intentclient.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 <QList> +#include <QMap> + +#include <QtAppManCommon/global.h> + +QT_BEGIN_NAMESPACE_AM + +class IntentHandler; +class IntentClientRequest; +class IntentClientSystemInterface; + +/* internal interface used by IntentRequest and IntentHandler */ + +class IntentClient : public QObject +{ + Q_OBJECT + +public: + ~IntentClient() override; + static IntentClient *createInstance(IntentClientSystemInterface *systemInterface); + static IntentClient *instance(); + + IntentClientRequest *requestToSystem(const QString &requestingApplicationId, const QString &intentId, const QString &applicationId, + const QVariantMap ¶meters); + void replyFromApplication(IntentClientRequest *icr, const QVariantMap &result); + void errorReplyFromApplication(IntentClientRequest *icr, const QString &errorMessage); + + void registerHandler(IntentHandler *handler); + void unregisterHandler(IntentHandler *handler); + +private: + void requestToSystemFinished(IntentClientRequest *icr, const QUuid &newRequestId, + bool error, const QString &errorMessage); + void replyFromSystem(const QString &requestId, bool error, const QVariantMap &result); + + void requestToApplication(const QString &requestId, const QString &intentId, + const QString &applicationId, const QVariantMap ¶meters); + +private: + IntentClient(IntentClientSystemInterface *systemInterface, QObject *parent = nullptr); + Q_DISABLE_COPY(IntentClient) + static IntentClient *s_instance; + + QList<IntentClientRequest *> m_waiting; + QMap<QString, IntentHandler *> m_handlers; + + IntentClientSystemInterface *m_systemInterface; + friend class IntentClientSystemInterface; +}; + +QT_END_NAMESPACE_AM diff --git a/src/intent-client-lib/intentclientrequest.cpp b/src/intent-client-lib/intentclientrequest.cpp new file mode 100644 index 00000000..f4be4a90 --- /dev/null +++ b/src/intent-client-lib/intentclientrequest.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** 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 "intentclientrequest.h" +#include "intentclient.h" + +#include <QQmlEngine> +#include <QThread> + +QT_BEGIN_NAMESPACE_AM + +IntentClientRequest *IntentClientRequest::create(const QString &requestingApplicationId, + const QString &intentId, const QVariantMap ¶meters) +{ + return create(requestingApplicationId, intentId, QString(), parameters); +} + +IntentClientRequest *IntentClientRequest::create(const QString &requestingApplicationId, + const QString &intentId, const QString &applicationId, + const QVariantMap ¶meters) +{ + //TODO: check that parameters only contains basic datatypes. convertFromJSVariant() does most of + // this already, but doesn't bail out on unconvertible types (yet) + + if (intentId.isEmpty()) + return nullptr; + return IntentClient::instance()->requestToSystem(requestingApplicationId, intentId, applicationId, parameters); +} + +QUuid IntentClientRequest::id() const +{ + return m_id; +} + +IntentClientRequest::Direction IntentClientRequest::direction() const +{ + return m_direction; +} + +QString IntentClientRequest::requestId() const +{ + return id().toString(); +} + +QString IntentClientRequest::intentId() const +{ + return m_intentId; +} + +QString IntentClientRequest::applicationId() const +{ + return m_applicationId; +} + +QString IntentClientRequest::requestingApplicationId() const +{ + return m_requestingApplicationId; +} + +QVariantMap IntentClientRequest::parameters() const +{ + return m_parameters; +} + +bool IntentClientRequest::succeeded() const +{ + return m_succeeded; +} + +const QVariantMap IntentClientRequest::result() const +{ + return m_result; +} + +QString IntentClientRequest::errorMessage() const +{ + return m_errorMessage; +} + +void IntentClientRequest::sendReply(const QVariantMap &result) +{ + //TODO: check that result only contains basic datatypes. convertFromJSVariant() does most of + // this already, but doesn't bail out on unconvertible types (yet) + + if (m_direction != Direction::ToApplication) + return; + IntentClient *ic = IntentClient::instance(); + + if (QThread::currentThread() != ic->thread()) { + ic->metaObject()->invokeMethod(ic, [this, ic, result]() + { ic->replyFromApplication(this, result); }, + Qt::QueuedConnection); + } else { + ic->replyFromApplication(this, result); + } +} + +void IntentClientRequest::sendErrorReply(const QString &errorMessage) +{ + if (m_direction != Direction::ToApplication) + return; + IntentClient *ic = IntentClient::instance(); + + if (QThread::currentThread() != ic->thread()) { + ic->metaObject()->invokeMethod(ic, [this, ic, errorMessage]() + { ic->errorReplyFromApplication(this, errorMessage); }, + Qt::QueuedConnection); + } else { + ic->errorReplyFromApplication(this, errorMessage); + } +} + +IntentClientRequest::IntentClientRequest(Direction direction, const QString &requestingApplicationId, + const QUuid &id, const QString &intentId, + const QString &applicationId, const QVariantMap ¶meters) + : QObject() + , m_direction(direction) + , m_id(id) + , m_intentId(intentId) + , m_requestingApplicationId(requestingApplicationId) + , m_applicationId(applicationId) + , m_parameters(parameters) +{ } + +void IntentClientRequest::setRequestId(const QUuid &requestId) +{ + if (m_id != requestId) { + m_id = requestId; + emit requestIdChanged(); + } +} + +void IntentClientRequest::setResult(const QVariantMap &result) +{ + if (m_result != result) { + m_result = result; + m_succeeded = true; + emit finished(); + } +} + +void IntentClientRequest::setErrorMessage(const QString &errorMessage) +{ + if (m_errorMessage != errorMessage) { + m_errorMessage = errorMessage; + m_succeeded = false; + emit finished(); + } +} + +QT_END_NAMESPACE_AM diff --git a/src/intent-client-lib/intentclientrequest.h b/src/intent-client-lib/intentclientrequest.h new file mode 100644 index 00000000..444cf39c --- /dev/null +++ b/src/intent-client-lib/intentclientrequest.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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 <QDir> +#include <QUuid> +#include <QString> +#include <QVariantMap> +#include <QtAppManCommon/global.h> + +QT_BEGIN_NAMESPACE_AM + +class IntentClient; + +class IntentClientRequest : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString requestId READ requestId NOTIFY requestIdChanged) + Q_PROPERTY(Direction direction READ direction CONSTANT) + Q_PROPERTY(QString intentId READ intentId CONSTANT) + Q_PROPERTY(QString applicationId READ applicationId CONSTANT) + Q_PROPERTY(QVariantMap parameters READ parameters CONSTANT) + Q_PROPERTY(bool succeeded READ succeeded NOTIFY finished) + Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY finished) + Q_PROPERTY(QVariantMap result READ result NOTIFY finished) + +public: + enum class Direction { ToSystem, ToApplication }; + Q_ENUM(Direction) + + static IntentClientRequest *create(const QString &requestingApplicationId, const QString &intentId, const QVariantMap ¶meters); + static IntentClientRequest *create(const QString &requestingApplicationId, const QString &intentId, const QString &applicationId, const QVariantMap ¶meters); + + QUuid id() const; + Direction direction() const; + QString requestId() const; + QString intentId() const; + QString applicationId() const; + QString requestingApplicationId() const; + QVariantMap parameters() const; + + const QVariantMap result() const; + bool succeeded() const; + QString errorMessage() const; + + Q_INVOKABLE void sendReply(const QVariantMap &result); + Q_INVOKABLE void sendErrorReply(const QString &errorMessage); + +signals: + void requestIdChanged(); + void finished(); + +private: + IntentClientRequest(Direction direction, const QString &requestingApplicationId, const QUuid &id, + const QString &intentId, const QString &applicationId, const QVariantMap ¶meters); + + void setRequestId(const QUuid &requestId); + void setResult(const QVariantMap &result); + void setErrorMessage(const QString &errorMessage); + + Direction m_direction; + QUuid m_id; + QString m_intentId; + QString m_requestingApplicationId; + QString m_applicationId; + QVariantMap m_parameters; + QVariantMap m_result; + QString m_errorMessage; + bool m_requestSent = false; + bool m_replyReceived = false; + bool m_succeeded = false; + + Q_DISABLE_COPY(IntentClientRequest) + + friend class IntentClient; +}; + +QT_END_NAMESPACE_AM + +Q_DECLARE_METATYPE(QtAM::IntentClientRequest *) diff --git a/src/intent-client-lib/intentclientsysteminterface.cpp b/src/intent-client-lib/intentclientsysteminterface.cpp new file mode 100644 index 00000000..ee2329d6 --- /dev/null +++ b/src/intent-client-lib/intentclientsysteminterface.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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 "intentclientsysteminterface.h" +#include "intentclient.h" +#include "intentclientrequest.h" + +QT_BEGIN_NAMESPACE_AM + +void IntentClientSystemInterface::initialize(IntentClient *intentClient) +{ + m_ic = intentClient; + connect(this, &IntentClientSystemInterface::requestToSystemFinished, + m_ic, &IntentClient::requestToSystemFinished); + connect(this, &IntentClientSystemInterface::replyFromSystem, + m_ic, &IntentClient::replyFromSystem); + connect(this, &IntentClientSystemInterface::requestToApplication, + m_ic, &IntentClient::requestToApplication); +} + +QT_END_NAMESPACE_AM diff --git a/src/intent-client-lib/intentclientsysteminterface.h b/src/intent-client-lib/intentclientsysteminterface.h new file mode 100644 index 00000000..a140c190 --- /dev/null +++ b/src/intent-client-lib/intentclientsysteminterface.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 <QUuid> +#include <QVariantMap> +#include <QtAppManCommon/global.h> + + +QT_BEGIN_NAMESPACE_AM + +class IntentClient; +class IntentClientRequest; + +class IntentClientSystemInterface : public QObject +{ + Q_OBJECT + +public: + virtual ~IntentClientSystemInterface() = default; + + virtual void initialize(IntentClient *intentClient) Q_DECL_NOEXCEPT_EXPR(false); + + virtual void requestToSystem(IntentClientRequest *icr) = 0; + virtual void replyFromApplication(IntentClientRequest *icr) = 0; + +signals: + void requestToSystemFinished(IntentClientRequest *icr, const QUuid &newRequestId, + bool error, const QString &errorMessage); + void replyFromSystem(const QString &requestId, bool error, const QVariantMap &result); + + void requestToApplication(const QString &requestId, const QString &intentId, + const QString &applicationId, const QVariantMap ¶meters); + +protected: + IntentClient *m_ic = nullptr; +}; + +QT_END_NAMESPACE_AM diff --git a/src/intent-client-lib/intenthandler.cpp b/src/intent-client-lib/intenthandler.cpp new file mode 100644 index 00000000..a11f3471 --- /dev/null +++ b/src/intent-client-lib/intenthandler.cpp @@ -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 +** +****************************************************************************/ + +#include "intenthandler.h" +#include "intentclient.h" + +QT_BEGIN_NAMESPACE_AM + +IntentHandler::IntentHandler(QObject *parent) + : QObject(parent) +{ } + +IntentHandler::IntentHandler(const QString &intentId, QObject *parent) + : QObject(parent) + , m_intentIds(intentId) +{ } + +IntentHandler::IntentHandler(const QStringList &intentIds, QObject *parent) + : QObject(parent) + , m_intentIds(intentIds) + +{ } + +IntentHandler::~IntentHandler() +{ + if (auto ie = IntentClient::instance()) + ie->unregisterHandler(this); +} + +QStringList IntentHandler::intentIds() const +{ + return m_intentIds; +} + +void IntentHandler::setIntentIds(const QStringList &intentIds) +{ + if (intentIds != m_intentIds) { + m_intentIds = intentIds; + emit intentIdsChanged(m_intentIds); + } +} + +void IntentHandler::componentComplete() +{ + IntentClient::instance()->registerHandler(this); +} + +void IntentHandler::classBegin() +{ } + +QT_END_NAMESPACE_AM diff --git a/src/intent-client-lib/intenthandler.h b/src/intent-client-lib/intenthandler.h new file mode 100644 index 00000000..3a57a3de --- /dev/null +++ b/src/intent-client-lib/intenthandler.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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 <QQmlParserStatus> +#include <QUuid> +#include <QString> +#include <QStringList> +#include <QVariantMap> +#include <QtAppManCommon/global.h> + +QT_BEGIN_NAMESPACE_AM + +class IntentClientRequest; + +class IntentHandler : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QStringList intentIds READ intentIds WRITE setIntentIds NOTIFY intentIdsChanged) + +public: + IntentHandler(QObject *parent = nullptr); + IntentHandler(const QString &intentId, QObject *parent = nullptr); + IntentHandler(const QStringList &intentIds, QObject *parent = nullptr); + ~IntentHandler() override; + + QStringList intentIds() const; + void setIntentIds(const QStringList &intentId); + +signals: + void intentIdsChanged(const QStringList &intentId); + + void receivedRequest(QtAM::IntentClientRequest *request); + +protected: + void componentComplete() override; + void classBegin() override; + +private: + Q_DISABLE_COPY(IntentHandler) + + QStringList m_intentIds; +}; + +QT_END_NAMESPACE_AM + +Q_DECLARE_METATYPE(QtAM::IntentHandler *) 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 ¶meters) const +{ + QMapIterator<QString, QVariant> rit(m_parameterMatch); + while (rit.hasNext()) { + rit.next(); + const QString ¶mName = 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 ¶meterMatch) + : 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 ¶meters) const; + +private: + Intent(const QString &id, const QString &applicationId, const QString &backgroundHandlerId, + const QStringList &capabilities, Intent::Visibility visibility, + const QVariantMap ¶meterMatch = 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 ¶meterMatch) +{ + 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 ¶meterMatch) +{ + 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 ¶meters) 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 ¶meters) 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 ¶meters) 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 ¶meters) +{ + 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 ¶meters = QVariantMap{}) const; + Q_INVOKABLE QVector<const Intent *> filterByApplicationId(const QString &applicationId, + const QVariantMap ¶meters = QVariantMap{}) const; + Q_INVOKABLE const Intent *find(const QString &intentId, const QString &applicationId, + const QVariantMap ¶meters = 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 ¶meterMatch = QVariantMap()); + + const Intent *addIntent(const QString &id, const QString &applicationId, + const QString &backgroundHandlerId, + const QStringList &capabilities, Intent::Visibility visibility, + const QVariantMap ¶meterMatch = 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 ¶meters); + + +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 ¶meters); + + 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 ¶meters) + : 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 ¶meters); + + 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 ¶meters) +{ + // 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 ¶meters); + 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 diff --git a/src/launcher-lib/intentclientdbusimplementation.cpp b/src/launcher-lib/intentclientdbusimplementation.cpp new file mode 100644 index 00000000..39141c6c --- /dev/null +++ b/src/launcher-lib/intentclientdbusimplementation.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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 <QDBusConnection> +#include <QDBusPendingReply> +#include <QDBusPendingCallWatcher> +#include "dbus-utilities.h" +#include "intentclient.h" +#include "intentclientrequest.h" +#include "intentclientdbusimplementation.h" + +#include "intentinterface_interface.h" + +QT_BEGIN_NAMESPACE_AM + +IntentClientDBusImplementation::IntentClientDBusImplementation(const QString &dbusName) + : IntentClientSystemInterface() + , m_dbusName(dbusName) +{ } + +void IntentClientDBusImplementation::initialize(IntentClient *intentClient) Q_DECL_NOEXCEPT_EXPR(false) +{ + IntentClientSystemInterface::initialize(intentClient); + + m_dbusInterface = new IoQtApplicationManagerIntentInterfaceInterface( + QString(), qSL("/IntentManager"), QDBusConnection(m_dbusName), intentClient); + + if (!m_dbusInterface->isValid()) + throw std::logic_error("Could not connect to the /IntentManager object on the P2P D-Bus"); + + connect(m_dbusInterface, &IoQtApplicationManagerIntentInterfaceInterface::replyFromSystem, + intentClient, [this](const QString &requestId, bool error, const QVariantMap &result) { + emit replyFromSystem(requestId, error, convertFromDBusVariant(result).toMap()); + }); + + connect(m_dbusInterface, &IoQtApplicationManagerIntentInterfaceInterface::requestToApplication, + intentClient, [this](const QString &requestId, const QString &id, + const QString &applicationId, const QVariantMap ¶meters) { + emit requestToApplication(requestId, id, applicationId, convertFromDBusVariant(parameters).toMap()); + }); +} + +void IntentClientDBusImplementation::requestToSystem(IntentClientRequest *icr) +{ + QDBusPendingReply<QString> reply = + m_dbusInterface->requestToSystem(icr->intentId(), icr->applicationId(), + convertFromJSVariant(icr->parameters()).toMap()); + + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, icr); + connect(watcher, &QDBusPendingCallWatcher::finished, icr, [this, watcher, icr]() { + watcher->deleteLater(); + + QDBusPendingReply<QString> reply = *watcher; + emit requestToSystemFinished(icr, QUuid::fromString(reply.argumentAt<0>()), + reply.isError(), reply.error().message()); + }); +} + +void IntentClientDBusImplementation::replyFromApplication(IntentClientRequest *icr) +{ + m_dbusInterface->replyFromApplication(icr->id().toString(), !icr->succeeded(), + convertFromJSVariant(icr->result()).toMap()); + + //TODO: should we wait for the call completion - how/why would we report a possible failure? +} + +QT_END_NAMESPACE_AM diff --git a/src/launcher-lib/intentclientdbusimplementation.h b/src/launcher-lib/intentclientdbusimplementation.h new file mode 100644 index 00000000..529d937f --- /dev/null +++ b/src/launcher-lib/intentclientdbusimplementation.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** 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 <QtAppManIntentClient/intentclientsysteminterface.h> + +class IoQtApplicationManagerIntentInterfaceInterface; + +QT_BEGIN_NAMESPACE_AM + +class IntentClientDBusImplementation : public IntentClientSystemInterface +{ +public: + IntentClientDBusImplementation(const QString &dbusName); + + void initialize(IntentClient *intentClient) Q_DECL_NOEXCEPT_EXPR(false) override; + + void requestToSystem(IntentClientRequest *icr) override; + void replyFromApplication(IntentClientRequest *icr) override; + +private: + IoQtApplicationManagerIntentInterfaceInterface *m_dbusInterface; + QString m_dbusName; +}; + +QT_END_NAMESPACE_AM diff --git a/src/launcher-lib/launcher-lib.pro b/src/launcher-lib/launcher-lib.pro index 15421752..f0f8dcb8 100644 --- a/src/launcher-lib/launcher-lib.pro +++ b/src/launcher-lib/launcher-lib.pro @@ -12,15 +12,19 @@ QT_FOR_PRIVATE *= \ appman_shared_main-private \ appman_application-private \ appman_notification-private \ + appman_intent_client-private \ CONFIG *= static internal_module +DBUS_INTERFACES += ../dbus-lib/io.qt.applicationmanager.intentinterface.xml + SOURCES += \ qmlapplicationinterface.cpp \ ipcwrapperobject.cpp \ qmlapplicationinterfaceextension.cpp \ qmlnotification.cpp \ launchermain.cpp \ + intentclientdbusimplementation.cpp !headless:SOURCES += \ applicationmanagerwindow.cpp \ @@ -41,6 +45,7 @@ HEADERS += \ qmlapplicationinterfaceextension.h \ qmlnotification.h \ launchermain.h \ + intentclientdbusimplementation.h !headless:HEADERS += \ applicationmanagerwindow_p.h diff --git a/src/launcher-lib/qmlapplicationinterface.cpp b/src/launcher-lib/qmlapplicationinterface.cpp index 428708c6..a48a5e3b 100644 --- a/src/launcher-lib/qmlapplicationinterface.cpp +++ b/src/launcher-lib/qmlapplicationinterface.cpp @@ -43,6 +43,7 @@ #include <QDBusInterface> #include <QDBusMessage> #include <QDBusReply> +#include <QQmlEngine> #include <QDebug> #include <QPointer> #include <QCoreApplication> @@ -55,6 +56,9 @@ #include "notification.h" #include "ipcwrapperobject.h" #include "utilities.h" +#include "intentclient.h" +#include "intentclientrequest.h" +#include "intentclientdbusimplementation.h" QT_BEGIN_NAMESPACE_AM @@ -144,6 +148,12 @@ bool QmlApplicationInterface::initialize() QmlApplicationInterfaceExtension::initialize(m_connection); + auto intentClientDBusInterface = new IntentClientDBusImplementation(m_connection.name()); + if (!IntentClient::createInstance(intentClientDBusInterface)) { + qCritical("ERROR: could not connect to the application manager's IntentInterface on the P2P D-Bus"); + return false; + } + if (ok) finishedInitialization(); return ok; @@ -198,6 +208,18 @@ QVariantMap QmlApplicationInterface::applicationProperties() const return m_applicationProperties; } +IntentClientRequest *QmlApplicationInterface::createIntentRequest(const QString &intentId, const QVariantMap ¶meters) +{ + return createIntentRequest(intentId, QString(), parameters); +} + +IntentClientRequest *QmlApplicationInterface::createIntentRequest(const QString &intentId, const QString &applicationId, const QVariantMap ¶meters) +{ + auto req = IntentClientRequest::create(this->applicationId(), intentId, applicationId, parameters); + QQmlEngine::setObjectOwnership(req, QQmlEngine::CppOwnership); + return req; +} + uint QmlApplicationInterface::notificationShow(QmlNotification *n) { if (n && m_notifyIf && m_notifyIf->isValid()) { diff --git a/src/launcher-lib/qmlapplicationinterface.h b/src/launcher-lib/qmlapplicationinterface.h index 2a4b923c..bd7d7738 100644 --- a/src/launcher-lib/qmlapplicationinterface.h +++ b/src/launcher-lib/qmlapplicationinterface.h @@ -55,6 +55,7 @@ QT_BEGIN_NAMESPACE_AM class QmlNotification; class Notification; +class IntentClientRequest; class Controller; class QmlApplicationInterfaceExtension; @@ -76,6 +77,8 @@ public: QVariantMap systemProperties() const override; QVariantMap applicationProperties() const override; Q_INVOKABLE QT_PREPEND_NAMESPACE_AM(Notification *) createNotification(); + Q_INVOKABLE QT_PREPEND_NAMESPACE_AM(IntentClientRequest *) createIntentRequest(const QString &intentId, const QVariantMap ¶meters); + Q_INVOKABLE QT_PREPEND_NAMESPACE_AM(IntentClientRequest *) createIntentRequest(const QString &intentId, const QString &applicationId, const QVariantMap ¶meters); Q_INVOKABLE void acknowledgeQuit() const; Q_INVOKABLE void finishedInitialization() override; diff --git a/src/launchers/qml/main.cpp b/src/launchers/qml/main.cpp index 993da379..7ed18027 100644 --- a/src/launchers/qml/main.cpp +++ b/src/launchers/qml/main.cpp @@ -279,7 +279,8 @@ Controller::Controller(LauncherMain *a, bool quickLaunched, const QString &direc connect(m_applicationInterface, &QmlApplicationInterface::startApplication, this, &Controller::startApplication); if (!m_applicationInterface->initialize()) - throw Exception("Could not connect to the application manager's interface on the peer D-Bus"); + throw Exception("Could not connect to the application manager's ApplicationInterface on the peer D-Bus"); + } else { QTimer::singleShot(0, [this, directLoad]() { QFileInfo fi(directLoad); diff --git a/src/main-lib/main-lib.pro b/src/main-lib/main-lib.pro index 0211e242..d8087850 100644 --- a/src/main-lib/main-lib.pro +++ b/src/main-lib/main-lib.pro @@ -18,6 +18,7 @@ QT *= \ appman_notification-private \ appman_monitor-private \ appman_shared_main-private \ + appman_intent_server-private \ !headless:QT *= appman_window-private !disable-external-dbus-interfaces:qtHaveModule(dbus):QT *= dbus appman_dbus-private diff --git a/src/main-lib/main.cpp b/src/main-lib/main.cpp index 76833b7c..977ab980 100644 --- a/src/main-lib/main.cpp +++ b/src/main-lib/main.cpp @@ -115,6 +115,8 @@ #include "qml-utilities.h" #include "dbus-utilities.h" #include "package.h" +#include "intentserver.h" +#include "intentaminterface.h" #if !defined(AM_HEADLESS) # include "windowmanager.h" @@ -168,6 +170,7 @@ Main::~Main() delete m_engine; + delete m_intentServer; delete m_notificationManager; # if !defined(AM_HEADLESS) delete m_windowManager; @@ -456,6 +459,72 @@ void Main::loadApplicationDatabase(const QString &databasePath, bool recreateDat StartupTimer::instance()->checkpoint("after application database loading"); } +void Main::setupIntents() Q_DECL_NOEXCEPT_EXPR(false) +{ + qCDebug(LogSystem) << "Registering intents:"; + + const auto apps = m_applicationManager->applications(); + for (const AbstractApplication *app : apps) { + if (app->isAlias()) + continue; + const ApplicationInfo *info = app->nonAliasedInfo(); + QSet<QString> intentIds; + + const auto intents = info->intents(); + + if (!intents.isEmpty()) + m_intentServer->addApplication(app->id()); + + for (const auto &intent : intents) { + /* example: + id: io.qt.shareImage + handledBy: main + visibility: public*|private + requiredCapabilities: [ a, b ] + parameterMatch: + mimeType: "^image/.*\.png$" + */ + const QVariantMap map = intent.toMap(); + const QString id = map[qSL("id")].toString(); + Intent::Visibility visibility = Intent::Public; + const QString visibilityStr = map[qSL("visibility")].toString(); + QString handledBy = map[qSL("handledBy")].toString(); + const QStringList capabilities = map[qSL("requiredCapabilities")].toStringList(); + const QVariantMap parameterMatch = map[qSL("parameterMatch")].toMap(); // do we really need that? + + if (id.isEmpty()) + throw Exception(Error::Intents, "intents need to have an id (app %1)").arg(app->id()); + if (intentIds.contains(id)) + throw Exception(Error::Intents, "found two intent handlers for %2 (app %1)").arg(app->id()).arg(id); + intentIds << id; + + if (visibilityStr == qL1S("private")) + visibility = Intent::Private; + else if (visibilityStr == qL1S("hidden")) + visibility = Intent::Hidden; + else if (!visibilityStr.isEmpty() && (visibilityStr != qL1S("public"))) { + throw Exception(Error::Intents, "intent visibilty %3 is invalid (intent %2, app %1)") + .arg(app->id()).arg(id).arg(visibilityStr); + } + + if (handledBy == qL1S("main")) + handledBy.clear(); + // we do not support bg services yet + if (!handledBy.isEmpty()) { + throw Exception(Error::Intents, "service background handlers for intent are not supported yet (intent %2, app %1)") + .arg(app->id()).arg(id).arg(visibilityStr); + } + + qCDebug(LogSystem).nospace().noquote() << " * " << id << " [app: " << app->id() << "]"; + + if (!m_intentServer->addIntent(id, app->id(), handledBy, capabilities, + visibility, parameterMatch)) { + throw Exception(Error::Intents, "could not add intent %2 for app %1").arg(app->id()).arg(id); + } + } + } +} + void Main::setupSingletons(const QList<QPair<QString, QString>> &containerSelectionConfiguration, int quickLaunchRuntimesPerContainer, qreal quickLaunchIdleLoad, const QString &singleApp) Q_DECL_NOEXCEPT_EXPR(false) @@ -490,6 +559,10 @@ void Main::setupSingletons(const QList<QPair<QString, QString>> &containerSelect StartupTimer::instance()->checkpoint("after ApplicationManager instantiation"); + m_intentServer = IntentAMImplementation::createIntentServerAndClientInstance(); + setupIntents(); + StartupTimer::instance()->checkpoint("after IntentManager instantiation"); + m_notificationManager = NotificationManager::createInstance(); StartupTimer::instance()->checkpoint("after NotificationManager instantiation"); diff --git a/src/main-lib/main.h b/src/main-lib/main.h index f324ed4c..1935f904 100644 --- a/src/main-lib/main.h +++ b/src/main-lib/main.h @@ -77,6 +77,7 @@ class ApplicationDatabase; class ApplicationManager; class ApplicationInstaller; class NotificationManager; +class IntentServer; class WindowManager; class QuickLauncher; class SystemMonitor; @@ -116,6 +117,7 @@ protected: void setupInstallationLocations(const QVariantList &installationLocations); void loadApplicationDatabase(const QString &databasePath, bool recreateDatabase, const QString &singleApp) Q_DECL_NOEXCEPT_EXPR(false); + void setupIntents() Q_DECL_NOEXCEPT_EXPR(false); void setupSingletons(const QList<QPair<QString, QString>> &containerSelectionConfiguration, int quickLaunchRuntimesPerContainer, qreal quickLaunchIdleLoad, const QString &singleApp) Q_DECL_NOEXCEPT_EXPR(false); @@ -166,6 +168,7 @@ private: ApplicationIPCManager *m_applicationIPCManager = nullptr; ApplicationInstaller *m_applicationInstaller = nullptr; NotificationManager *m_notificationManager = nullptr; + IntentServer *m_intentServer = nullptr; WindowManager *m_windowManager = nullptr; QuickLauncher *m_quickLauncher = nullptr; QVector<StartupInterface *> m_startupPlugins; diff --git a/src/manager-lib/intentaminterface.cpp b/src/manager-lib/intentaminterface.cpp new file mode 100644 index 00000000..66410c6d --- /dev/null +++ b/src/manager-lib/intentaminterface.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** +** +** 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 +** +****************************************************************************/ + +#if defined(AM_MULTI_PROCESS) +# include <QDBusMessage> +# include <QDBusConnection> +# include <QDBusPendingCallWatcher> +# include <QDBusPendingReply> + +# include "io.qt.applicationmanager.intentinterface_adaptor.h" +# include "dbus-utilities.h" +# include "nativeruntime.h" +#endif +#include <QDebug> +#include <QTimer> + +#include "logging.h" +#include "runtimefactory.h" +#include "intentserver.h" +#include "intentclient.h" +#include "intentserverrequest.h" +#include "intentclientrequest.h" +#include "intentaminterface.h" +#include "qmlinprocessruntime.h" +#include "application.h" +#include "applicationmanager.h" + +QT_BEGIN_NAMESPACE_AM + +////////////////////////////////////////////////////////////////////////// +// vvv IntentAMImplementation vvv + + +IntentServer *IntentAMImplementation::createIntentServerAndClientInstance() +{ + auto intentServerAMInterface = new IntentServerAMImplementation; + auto intentClientAMInterface = new IntentClientAMImplementation(intentServerAMInterface); + auto intentServer = IntentServer::createInstance(intentServerAMInterface); + auto intentClient = IntentClient::createInstance(intentClientAMInterface); + + // this way, deleting the server (the return value of this factory function) will get rid + // of both client and server as well as both their AM interfaces + intentClient->setParent(intentServer); + return intentServer; +} + + +// ^^^ IntentAMImplementation ^^^ +////////////////////////////////////////////////////////////////////////// +// vvv IntentServerAMImplementation vvv + + +void IntentServerAMImplementation::setIntentClientSystemInterface(IntentClientSystemInterface *iface) +{ + m_icsi = iface; +} + +IntentClientSystemInterface *IntentServerAMImplementation::intentClientSystemInterface() const +{ + return m_icsi; +} + +void IntentServerAMImplementation::initialize(IntentServer *intentManager) +{ + IntentServerSystemInterface::initialize(intentManager); + + // The IntentManager itself doesn't know about the p2p D-Bus or the AM itself, so we need to + // wire it up to both interfaces from the outside + connect(&ApplicationManager::instance()->internalSignals, &ApplicationManagerInternalSignals::newRuntimeCreated, + intentServer(), [this](AbstractRuntime *runtime) { +#if defined(AM_MULTI_PROCESS) + if (NativeRuntime *nativeRuntime = qobject_cast<NativeRuntime *>(runtime)) { + connect(nativeRuntime, &NativeRuntime::applicationConnectedToPeerDBus, + intentServer(), [this](const QDBusConnection &connection, Application *application) { + qCDebug(LogIntents) << "IntentManager: applicationConnectedToPeerDBus" + << (application ? application->id() : qSL("<launcher>")); + + IntentServerDBusIpcConnection::create(connection, application, this); + }); + + connect(nativeRuntime, &NativeRuntime::applicationReadyOnPeerDBus, + intentServer(), [](const QDBusConnection &connection, Application *application) { + auto peer = IntentServerDBusIpcConnection::find(connection); + + if (!peer) { + qCWarning(LogIntents) << "IntentManager: applicationReadyOnPeerDBus() was emitted, " + "but no previous applicationConnectedToPeerDBus() was seen"; + return; + } + peer->setReady(application); + }); + + connect(nativeRuntime, &NativeRuntime::applicationDisconnectedFromPeerDBus, + intentServer(), [](const QDBusConnection &connection, Application *) { + delete IntentServerDBusIpcConnection::find(connection); + }); + } else +#endif // defined(AM_MULTI_PROCESS) + if (QmlInProcessRuntime *qmlRuntime = qobject_cast<QmlInProcessRuntime *>(runtime)) { + connect(qmlRuntime, &QmlInProcessRuntime::stateChanged, + intentServer(), [this, qmlRuntime](Am::RunState newState) { + if (newState == Am::Running) + IntentServerInProcessIpcConnection::create(qmlRuntime->application(), this); + else if (newState == Am::NotRunning) + delete IntentServerIpcConnection::find(qmlRuntime->application()->id()); + }); + } + }); +} + +bool IntentServerAMImplementation::checkApplicationCapabilities(const QString &applicationId, + const QStringList &requiredCapabilities) +{ + const auto app = ApplicationManager::instance()->application(applicationId); + if (!app) + return false; + + auto capabilities = app->capabilities(); + for (auto cap : requiredCapabilities) { + if (!capabilities.contains(cap)) + return false; + } + return true; +} + +IntentServerSystemInterface::IpcConnection *IntentServerAMImplementation::findClientIpc(const QString &appId) +{ + const auto app = ApplicationManager::instance()->application(appId); + if (!app) + return nullptr; + auto peer = IntentServerIpcConnection::find(appId); + return (peer && peer->isReady()) ? reinterpret_cast<IpcConnection *>(peer) : nullptr; +} + +void IntentServerAMImplementation::startApplication(const QString &appId) +{ + ApplicationManager::instance()->startApplication(appId); +} + +void IntentServerAMImplementation::requestToApplication(IntentServerSystemInterface::IpcConnection *clientIPC, + IntentServerRequest *irs) +{ + reinterpret_cast<IntentServerIpcConnection *>(clientIPC)->requestToApplication(irs); +} + +void IntentServerAMImplementation::replyFromSystem(IntentServerSystemInterface::IpcConnection *clientIPC, + IntentServerRequest *irs) +{ + reinterpret_cast<IntentServerIpcConnection *>(clientIPC)->replyFromSystem(irs); +} + + +// ^^^ IntentServerAMImplementation ^^^ +////////////////////////////////////////////////////////////////////////// +// vvv IntentClientAMImplementation vvv + + +IntentClientAMImplementation::IntentClientAMImplementation(IntentServerAMImplementation *serverInterface) + : IntentClientSystemInterface() + , m_issi(serverInterface) +{ + serverInterface->setIntentClientSystemInterface(this); +} + +void IntentClientAMImplementation::initialize(IntentClient *intentClient) Q_DECL_NOEXCEPT_EXPR(false) +{ + IntentClientSystemInterface::initialize(intentClient); +} + +void IntentClientAMImplementation::requestToSystem(IntentClientRequest *icr) +{ + IntentServerRequest *isr = m_issi->requestToSystem(icr->requestingApplicationId(), icr->intentId(), + icr->applicationId(), icr->parameters()); + + QUuid requestId = isr ? isr->id() : QUuid(); + + QTimer::singleShot(0, m_ic, [icr, requestId, this]() { + emit requestToSystemFinished(icr, requestId, requestId.isNull(), + requestId.isNull() ? qSL("Failed") : QString()); + }); +} + +void IntentClientAMImplementation::replyFromApplication(IntentClientRequest *icr) +{ + emit m_issi->replyFromApplication(icr->applicationId(), icr->requestId(), !icr->succeeded(), + icr->result()); +} + + +// ^^^ IntentClientAMImplementation ^^^ +////////////////////////////////////////////////////////////////////////// +// vvv IntentServerIpcConnection vvv + + +QList<IntentServerIpcConnection *> IntentServerIpcConnection::s_allPeers; + +IntentServerIpcConnection::IntentServerIpcConnection(bool inProcess, Application *application, + IntentServerAMImplementation *iface) + : QObject() + , m_application(application) + , m_interface(iface) + , m_inprocess(inProcess) +{ + connect(this, &IntentServerIpcConnection::applicationIsReady, + m_interface, &IntentServerSystemInterface::applicationWasStarted); +} + +IntentServerIpcConnection::~IntentServerIpcConnection() +{ } + +bool IntentServerIpcConnection::isReady() const +{ + return m_ready; +} + +void IntentServerIpcConnection::setReady(Application *application) +{ + if (m_ready) + return; + m_application = application; + m_ready = true; + emit applicationIsReady(application->id()); +} + +IntentServerIpcConnection *IntentServerIpcConnection::find(const QString &appId) +{ + for (auto peer : qAsConst(s_allPeers)) { + if (peer->m_application->id() == appId) + return peer; + } + return nullptr; +} + + +Application *IntentServerIpcConnection::application() const +{ + return m_application; +} + +bool IntentServerIpcConnection::isInProcess() const +{ + return m_inprocess; +} + + +// ^^^ IntentServerIpcConnection ^^^ +////////////////////////////////////////////////////////////////////////// +// vvv IntentServerInProcessIpcConnection vvv + + +IntentServerInProcessIpcConnection::IntentServerInProcessIpcConnection(Application *application, + IntentServerAMImplementation *iface) + : IntentServerIpcConnection(true /*inProcess*/, application, iface) +{ } + +IntentServerInProcessIpcConnection::~IntentServerInProcessIpcConnection() +{ } + +IntentServerInProcessIpcConnection *IntentServerInProcessIpcConnection::create(Application *application, + IntentServerAMImplementation *iface) +{ + auto peer = new IntentServerInProcessIpcConnection(application, iface); + QTimer::singleShot(0, peer, [peer, application]() { peer->setReady(application); }); + s_allPeers << peer; + return peer; +} + +void IntentServerInProcessIpcConnection::requestToApplication(IntentServerRequest *irs) +{ + // we need decouple the server/client interface at this point to have a consistent + // behavior in single- and multi-process mode + QTimer::singleShot(0, this, [this, irs]() { + auto clientInterface = m_interface->intentClientSystemInterface(); + emit clientInterface->requestToApplication(irs->id().toString(), irs->intent()->id(), + irs->intent()->applicationId(), irs->parameters()); + }); +} + +void IntentServerInProcessIpcConnection::replyFromSystem(IntentServerRequest *irs) +{ + // we need decouple the server/client interface at this point to have a consistent + // behavior in single- and multi-process mode + QTimer::singleShot(0, this, [this, irs]() { + auto clientInterface = m_interface->intentClientSystemInterface(); + emit clientInterface->replyFromSystem(irs->id().toString(), !irs->hasSucceeded(), irs->result()); + }); +} + + +// ^^^ IntentServerInProcessIpcConnection ^^^ +////////////////////////////////////////////////////////////////////////// +// vvv IntentServerDBusIpcConnection vvv + +#if defined(AM_MULTI_PROCESS) + +IntentServerDBusIpcConnection::IntentServerDBusIpcConnection(QDBusConnection connection, + Application *application, + IntentServerAMImplementation *iface) + : IntentServerIpcConnection(false /*!inProcess*/, application, iface) +{ + m_connectionName = connection.name(); + m_adaptor = new IntentInterfaceAdaptor(this); + connection.registerObject(qSL("/IntentManager"), this, QDBusConnection::ExportAdaptors); +} + +IntentServerDBusIpcConnection::~IntentServerDBusIpcConnection() +{ + QDBusConnection(m_connectionName).unregisterObject(qSL("/IntentManager")); + s_allPeers.removeOne(this); +} + +IntentServerDBusIpcConnection *IntentServerDBusIpcConnection::create(QDBusConnection connection, + Application *application, + IntentServerAMImplementation *iface) +{ + auto peer = new IntentServerDBusIpcConnection(connection, application, iface); + s_allPeers << peer; + return peer; +} + +IntentServerDBusIpcConnection *IntentServerDBusIpcConnection::find(QDBusConnection connection) +{ + QString connectionName = connection.name(); + + for (auto peer : qAsConst(s_allPeers)) { + if (peer->isInProcess()) + continue; + auto dbusPeer = static_cast<IntentServerDBusIpcConnection *>(peer); + if (dbusPeer->m_connectionName == connectionName) + return dbusPeer; + } + return nullptr; +} + +void IntentServerDBusIpcConnection::requestToApplication(IntentServerRequest *irs) +{ + emit m_adaptor->requestToApplication(irs->id().toString(), irs->intent()->id(), + irs->intent()->applicationId(), + convertFromJSVariant(irs->parameters()).toMap()); +} + +void IntentServerDBusIpcConnection::replyFromSystem(IntentServerRequest *irs) +{ + emit m_adaptor->replyFromSystem(irs->id().toString(), !irs->hasSucceeded(), + convertFromJSVariant(irs->result()).toMap()); +} + +QString IntentServerDBusIpcConnection::requestToSystem(const QString &intentId, + const QString &applicationId, + const QVariantMap ¶meters) +{ + auto requestingApplicationId = application() ? application()->id() : QString(); + auto irs = m_interface->requestToSystem(requestingApplicationId, intentId, applicationId, + convertFromDBusVariant(parameters).toMap()); + return irs ? irs->id().toString() : QString(); +} + +void IntentServerDBusIpcConnection::replyFromApplication(const QString &requestId, bool error, + const QVariantMap &result) +{ + emit m_interface->replyFromApplication(application()->id(), requestId, error, + convertFromDBusVariant(result).toMap()); +} + +#endif // defined(AM_MULTI_PROCESS) + +QT_END_NAMESPACE_AM + + +// ^^^ IntentServerDBusIpcConnection ^^^ +////////////////////////////////////////////////////////////////////////// +// vvv IntentInterfaceAdaptor vvv + +#if defined(AM_MULTI_PROCESS) + +IntentInterfaceAdaptor::IntentInterfaceAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ } + +IntentInterfaceAdaptor::~IntentInterfaceAdaptor() +{ } + +void IntentInterfaceAdaptor::replyFromApplication(const QString &requestId, bool error, + const QVariantMap &result) +{ + auto peer = static_cast<QtAM::IntentServerDBusIpcConnection *>(parent()); + peer->replyFromApplication(requestId, error, result); +} + +QString IntentInterfaceAdaptor::requestToSystem(const QString &intentId, const QString &applicationId, + const QVariantMap ¶meters) +{ + auto peer = static_cast<QtAM::IntentServerDBusIpcConnection *>(parent()); + return peer->requestToSystem(intentId, applicationId, parameters); +} + +#endif // defined(AM_MULTI_PROCESS) + +// ^^^ IntentInterfaceAdaptor ^^^ +////////////////////////////////////////////////////////////////////////// diff --git a/src/manager-lib/intentaminterface.h b/src/manager-lib/intentaminterface.h new file mode 100644 index 00000000..fd590670 --- /dev/null +++ b/src/manager-lib/intentaminterface.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** 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 <QVector> +#include <QString> +#include <QVariantMap> +#include <QList> +#if defined(AM_MULTI_PROCESS) +# include <QDBusConnection> +# include <QDBusContext> +#endif +#include <QtAppManCommon/global.h> +#include <QtAppManIntentServer/intentserversysteminterface.h> +#include <QtAppManIntentClient/intentclientsysteminterface.h> + +class IntentInterfaceAdaptor; + +QT_BEGIN_NAMESPACE_AM + +class Application; +class IntentServerRequest; + +namespace IntentAMImplementation { +IntentServer *createIntentServerAndClientInstance(); +} + +// the server side +class IntentServerAMImplementation : public IntentServerSystemInterface +{ + Q_OBJECT + +public: + void setIntentClientSystemInterface(IntentClientSystemInterface *iface); + IntentClientSystemInterface *intentClientSystemInterface() const; + + void initialize(IntentServer *intentManager) override; + + bool checkApplicationCapabilities(const QString &applicationId, + const QStringList &requiredCapabilities) override; + + IpcConnection *findClientIpc(const QString &appId) override; + + void startApplication(const QString &appId) override; + void requestToApplication(IpcConnection *clientIPC, IntentServerRequest *irs) override; + void replyFromSystem(IpcConnection *clientIPC, IntentServerRequest *irs) override; + +private: + IntentClientSystemInterface *m_icsi = nullptr; +}; + +// the in-process client side +class IntentClientAMImplementation : public IntentClientSystemInterface +{ +public: + IntentClientAMImplementation(IntentServerAMImplementation *serverInterface); + + void initialize(IntentClient *intentClient) Q_DECL_NOEXCEPT_EXPR(false) override; + + void requestToSystem(IntentClientRequest *icr) override; + void replyFromApplication(IntentClientRequest *icr) override; + +private: + IntentServerSystemInterface *m_issi; +}; + +// each instance represents one server->client connection +class IntentServerIpcConnection : public QObject +{ + Q_OBJECT + +public: + ~IntentServerIpcConnection() override; + + static IntentServerIpcConnection *find(const QString &appId); + + Application *application() const; + bool isInProcess() const; + + bool isReady() const; + void setReady(Application *application); + + virtual void replyFromSystem(IntentServerRequest *irs) = 0; + virtual void requestToApplication(IntentServerRequest *irs) = 0; + +signals: + void applicationIsReady(const QString &applicationId); + +protected: + IntentServerIpcConnection(bool inProcess, Application *application, IntentServerAMImplementation *iface); + + Application *m_application; + IntentServerAMImplementation *m_interface; + bool m_inprocess = true; + bool m_ready = false; + + static QList<IntentServerIpcConnection *> s_allPeers; +}; + +// ... derived for in-process clients +class IntentServerInProcessIpcConnection : public IntentServerIpcConnection +{ + Q_OBJECT + +public: + static IntentServerInProcessIpcConnection *create(Application *application, IntentServerAMImplementation *iface); + + ~IntentServerInProcessIpcConnection() override; + + void replyFromSystem(IntentServerRequest *irs) override; + void requestToApplication(IntentServerRequest *irs) override; + +private: + IntentServerInProcessIpcConnection(Application *application, IntentServerAMImplementation *iface); +}; + +#if defined(AM_MULTI_PROCESS) + +// ... derived for P2P DBus clients +class IntentServerDBusIpcConnection : public IntentServerIpcConnection, public QDBusContext +{ + Q_OBJECT + +public: + static IntentServerDBusIpcConnection *create(QDBusConnection connection, Application *application, + IntentServerAMImplementation *iface); + static IntentServerDBusIpcConnection *find(QDBusConnection connection); + + ~IntentServerDBusIpcConnection() override; + + QString requestToSystem(const QString &intentId, const QString &applicationId, const QVariantMap ¶meters); + void replyFromSystem(IntentServerRequest *irs) override; + void requestToApplication(IntentServerRequest *irs) override; + void replyFromApplication(const QString &requestId, bool error, const QVariantMap &result); + + +private: + IntentServerDBusIpcConnection(QDBusConnection connection, Application *application, + IntentServerAMImplementation *iface); + + QString m_connectionName; + ::IntentInterfaceAdaptor *m_adaptor = nullptr; +}; + +#endif // defined(AM_MULTI_PROCESS) + +QT_END_NAMESPACE_AM diff --git a/src/manager-lib/manager-lib.pro b/src/manager-lib/manager-lib.pro index 1eeb00ed..ccc7ab7b 100644 --- a/src/manager-lib/manager-lib.pro +++ b/src/manager-lib/manager-lib.pro @@ -6,17 +6,33 @@ load(am-config) QT = core network qml !headless:QT *= gui gui-private quick qml-private quick-private -qtHaveModule(dbus):QT *= dbus QT_FOR_PRIVATE *= \ appman_common-private \ appman_application-private \ appman_notification-private \ appman_plugininterfaces-private \ + appman_intent_server-private \ + appman_intent_client-private \ CONFIG *= static internal_module multi-process { - LIBS += -ldl + QT *= dbus + LIBS *= -ldl + + HEADERS += \ + nativeruntime.h \ + nativeruntime_p.h \ + processcontainer.h \ + + SOURCES += \ + nativeruntime.cpp \ + processcontainer.cpp \ + + CONFIG = dbus-adaptors-xml $$CONFIG + + ADAPTORS_XML = \ + ../dbus-lib/io.qt.applicationmanager.intentinterface.xml } HEADERS += \ @@ -38,6 +54,7 @@ HEADERS += \ systemreader.h \ debugwrapper.h \ amnamespace.h \ + intentaminterface.h linux:HEADERS += \ sysfsreader.h \ @@ -46,11 +63,6 @@ linux:HEADERS += \ fakeapplicationmanagerwindow.h \ inprocesssurfaceitem.h -multi-process:HEADERS += \ - nativeruntime.h \ - nativeruntime_p.h \ - processcontainer.h \ - qtHaveModule(qml):HEADERS += \ qmlinprocessruntime.h \ qmlinprocessapplicationinterface.h \ @@ -71,6 +83,7 @@ SOURCES += \ applicationipcinterface.cpp \ systemreader.cpp \ debugwrapper.cpp \ + intentaminterface.cpp linux:SOURCES += \ sysfsreader.cpp \ @@ -79,10 +92,6 @@ linux:SOURCES += \ fakeapplicationmanagerwindow.cpp \ inprocesssurfaceitem.cpp -multi-process:SOURCES += \ - nativeruntime.cpp \ - processcontainer.cpp \ - qtHaveModule(qml):SOURCES += \ qmlinprocessruntime.cpp \ qmlinprocessapplicationinterface.cpp \ diff --git a/src/manager-lib/qmlinprocessapplicationinterface.cpp b/src/manager-lib/qmlinprocessapplicationinterface.cpp index cd9a4d93..38bf8b23 100644 --- a/src/manager-lib/qmlinprocessapplicationinterface.cpp +++ b/src/manager-lib/qmlinprocessapplicationinterface.cpp @@ -50,6 +50,7 @@ #include "notificationmanager.h" #include "applicationipcmanager.h" #include "applicationipcinterface.h" +#include "intentclientrequest.h" QT_BEGIN_NAMESPACE_AM @@ -112,6 +113,21 @@ QVariantMap QmlInProcessApplicationInterface::applicationProperties() const return QVariantMap(); } +IntentClientRequest *QmlInProcessApplicationInterface::createIntentRequest(const QString &intentId, + const QVariantMap ¶meters) +{ + return createIntentRequest(intentId, QString(), parameters); +} + +IntentClientRequest *QmlInProcessApplicationInterface::createIntentRequest(const QString &intentId, + const QString &applicationId, + const QVariantMap ¶meters) +{ + auto req = IntentClientRequest::create(this->applicationId(), intentId, applicationId, parameters); + QQmlEngine::setObjectOwnership(req, QQmlEngine::CppOwnership); + return req; +} + void QmlInProcessApplicationInterface::acknowledgeQuit() { emit quitAcknowledged(); diff --git a/src/manager-lib/qmlinprocessapplicationinterface.h b/src/manager-lib/qmlinprocessapplicationinterface.h index 5f5b0c7d..72154e4b 100644 --- a/src/manager-lib/qmlinprocessapplicationinterface.h +++ b/src/manager-lib/qmlinprocessapplicationinterface.h @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE_AM class QmlInProcessRuntime; +class IntentClientRequest; class QmlInProcessNotification : public Notification // clazy:exclude=missing-qobject-macro { @@ -90,6 +91,8 @@ public: QVariantMap applicationProperties() const override; Q_INVOKABLE QT_PREPEND_NAMESPACE_AM(Notification *) createNotification(); + Q_INVOKABLE QT_PREPEND_NAMESPACE_AM(IntentClientRequest *) createIntentRequest(const QString &intentId, const QVariantMap ¶meters); + Q_INVOKABLE QT_PREPEND_NAMESPACE_AM(IntentClientRequest *) createIntentRequest(const QString &intentId, const QString &applicationId, const QVariantMap ¶meters); Q_INVOKABLE void acknowledgeQuit(); void finishedInitialization() override; diff --git a/src/src.pro b/src/src.pro index 6cef2941..67c1008d 100644 --- a/src/src.pro +++ b/src/src.pro @@ -20,7 +20,7 @@ package_lib.subdir = package-lib package_lib.depends = crypto_lib application_lib manager_lib.subdir = manager-lib -manager_lib.depends = application_lib notification_lib plugin_interfaces +manager_lib.depends = application_lib notification_lib intent_server_lib intent_client_lib plugin_interfaces installer_lib.subdir = installer-lib installer_lib.depends = package_lib manager_lib @@ -34,8 +34,14 @@ monitor_lib.depends = manager_lib window_lib shared_main_lib.subdir = shared-main-lib shared_main_lib.depends = common_lib +intent_server_lib.subdir = intent-server-lib +intent_server_lib.depends = common_lib + +intent_client_lib.subdir = intent-client-lib +intent_client_lib.depends = common_lib + launcher_lib.subdir = launcher-lib -launcher_lib.depends = application_lib notification_lib shared_main_lib +launcher_lib.depends = application_lib notification_lib shared_main_lib intent_client_lib main_lib.subdir = main-lib main_lib.depends = shared_main_lib manager_lib installer_lib window_lib monitor_lib @@ -85,6 +91,8 @@ SUBDIRS = \ window_lib \ monitor_lib \ shared_main_lib \ + intent_server_lib \ + intent_client_lib \ main_lib \ tools_appman \ # Although the testrunner is in tools we don't want to build it with tools-only diff --git a/sync.profile b/sync.profile index 16369812..142d3f44 100644 --- a/sync.profile +++ b/sync.profile @@ -13,6 +13,8 @@ "QtAppManPluginInterfaces" => "$basedir/src/plugin-interfaces", "QtAppManMonitor" => "$basedir/src/monitor-lib", "QtAppManDBus" => "$basedir/src/dbus-lib", + "QtAppManIntentServer" => "$basedir/src/intent-server-lib", + "QtAppManIntentClient" => "$basedir/src/intent-client-lib", ); %moduleheaders = ( # restrict the module headers to those found in relative path ); |