summaryrefslogtreecommitdiffstats
path: root/applauncher
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2022-09-10 12:31:13 -0400
committerScott Murray <scott.murray@konsulko.com>2022-09-10 12:42:02 -0400
commit560d902f4d2bf4ba3bb2edba6436080ee7d5a5ac (patch)
tree026c81383374b0b7ffbb867ab392ec7789f8ff45 /applauncher
parenta2d991a54f77017ced1558d289bcb83d73fe2a35 (diff)
Add applaunchd gRPC API wrapper
Changes: - Add applaunchd gRPC API wrapper in applauncher directory, clients can include AppLauncherClient.h to use it. - To facilitate generating protobuf and gRPC code with protoc, switch from CMake to meson for building. While the code generation can be done in CMake, it is a lot more straightforward with meson, and if use of this library continues meson will be easier to maintain. Known issues: - The behavior of the client implementation here with respect to the server side (i.e. applaunchd) going away is currently robust, but could stand improvement with some further investigation. As the code stands, starting applications works when applaunchd becomes available again, but the streaming status RPC that is tied to window activation in the homescreen does not reconnect, and there seem to be some things that need to be resolved with respect to Qt object connection expiry to do so. If the Qt demos continue to be used in a significant fashion, this may be worth picking up. Bug-AGL: SPEC-4559 Signed-off-by: Scott Murray <scott.murray@konsulko.com> Change-Id: I5cb480d4ce4d1cb944ebfd4114fb305f09f28ea3
Diffstat (limited to 'applauncher')
-rw-r--r--applauncher/AppLauncherClient.cpp41
-rw-r--r--applauncher/AppLauncherClient.h36
-rw-r--r--applauncher/AppLauncherGrpcClient.cpp113
-rw-r--r--applauncher/AppLauncherGrpcClient.h55
-rw-r--r--applauncher/meson.build56
-rw-r--r--applauncher/protos/applauncher.proto50
6 files changed, 351 insertions, 0 deletions
diff --git a/applauncher/AppLauncherClient.cpp b/applauncher/AppLauncherClient.cpp
new file mode 100644
index 0000000..bbbcef3
--- /dev/null
+++ b/applauncher/AppLauncherClient.cpp
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (C) 2022 Konsulko Group
+ */
+
+#include <QDebug>
+
+#include "AppLauncherClient.h"
+#include "AppLauncherGrpcClient.h"
+
+AppLauncherClient::AppLauncherClient(QObject *parent) : QObject(parent)
+{
+ m_launcher = new AppLauncherGrpcClient(this);
+}
+
+AppLauncherClient::~AppLauncherClient()
+{
+ delete m_launcher;
+}
+
+bool AppLauncherClient::startApplication(const QString &id)
+{
+ if (m_launcher)
+ return m_launcher->StartApplication(id);
+
+ return false;
+}
+
+bool AppLauncherClient::listApplications(QList<QMap<QString, QString>> &list)
+{
+ if (!m_launcher) {
+ return false;
+ }
+
+ return m_launcher->ListApplications(list);
+}
+
+void AppLauncherClient::sendStatusEvent(const QString &id, const QString &status)
+{
+ emit appStatusEvent(id, status);
+}
diff --git a/applauncher/AppLauncherClient.h b/applauncher/AppLauncherClient.h
new file mode 100644
index 0000000..91723ab
--- /dev/null
+++ b/applauncher/AppLauncherClient.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (C) 2022 Konsulko Group
+ */
+
+#ifndef APPLAUNCHER_CLIENT_H
+#define APPLAUNCHER_CLIENT_H
+
+#include <QObject>
+#include <QList>
+#include <QMap>
+
+class AppLauncherGrpcClient;
+
+class AppLauncherClient : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit AppLauncherClient(QObject *parent = Q_NULLPTR);
+ virtual ~AppLauncherClient();
+
+ Q_INVOKABLE bool startApplication(const QString &id);
+ Q_INVOKABLE bool listApplications(QList<QMap<QString, QString>> &list);
+
+public slots:
+ void sendStatusEvent(const QString &id, const QString &status);
+
+signals:
+ void appStatusEvent(const QString &id, const QString &status);
+
+private:
+ AppLauncherGrpcClient *m_launcher;
+};
+
+#endif // APPLAUNCHER_CLIENT_H
diff --git a/applauncher/AppLauncherGrpcClient.cpp b/applauncher/AppLauncherGrpcClient.cpp
new file mode 100644
index 0000000..a25b148
--- /dev/null
+++ b/applauncher/AppLauncherGrpcClient.cpp
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (C) 2022 Konsulko Group
+ */
+
+#include <QDebug>
+#include "AppLauncherGrpcClient.h"
+#include "AppLauncherClient.h"
+
+using grpc::Channel;
+using grpc::ClientContext;
+using grpc::ClientReader;
+using grpc::Status;
+
+using automotivegradelinux::AppLauncher;
+using automotivegradelinux::StartRequest;
+using automotivegradelinux::StartResponse;
+using automotivegradelinux::ListRequest;
+using automotivegradelinux::ListResponse;
+using automotivegradelinux::AppInfo;
+using automotivegradelinux::StatusRequest;
+using automotivegradelinux::StatusResponse;
+using automotivegradelinux::AppStatus;
+
+
+void AppStatusEventReader::GetStatusEvents()
+{
+ ClientContext context;
+ StatusRequest request;
+ StatusResponse response;
+
+ std::unique_ptr<ClientReader<StatusResponse> > reader(stub_->GetStatusEvents(&context, request));
+ while (reader->Read(&response)) {
+ if (response.has_app()) {
+ AppStatus app_status = response.app();
+ emit statusUpdate(QString::fromStdString(app_status.id()),
+ QString::fromStdString(app_status.status()));
+ }
+ }
+ Status status = reader->Finish();
+ if (!status.ok()) {
+ qWarning() << "GetStatusEvents RPC failed";
+ }
+
+ emit finished();
+}
+
+AppLauncherGrpcClient::AppLauncherGrpcClient(QObject *parent) : QObject(parent)
+{
+ stub_ = AppLauncher::NewStub(grpc::CreateChannel("localhost:50052", grpc::InsecureChannelCredentials()));
+
+ // Create thread to read status events
+ AppStatusEventReader *reader = new AppStatusEventReader(stub_);
+ reader->moveToThread(&m_event_thread);
+ connect(&m_event_thread, &QThread::started, reader, &AppStatusEventReader::GetStatusEvents);
+ connect(reader, &AppStatusEventReader::finished, &m_event_thread, &QThread::quit);
+ // FIXME: Normally the thread finishing would be connected per the below
+ // to trigger cleaning up the object. That seems to trigger a crash
+ // for not entirely obvious reasons. It seems unrelated to the signal
+ // connection to AppLauncherClient, as not connecting does not prevent
+ // the crash; further investigation is required.
+ //connect(reader, &AppStatusEventReader::finished, reader, &AppStatusEventReader::deleteLater);
+ //connect(&m_event_thread, &QThread::finished, &m_event_thread, &QThread::deleteLater);
+
+ // To avoid having an intermediary slot+signal in this class, try
+ // casting parent to AppLauncherClient and connect directly to its
+ // slot. Callers should set parent to ensure this works as required.
+ if (parent) {
+ AppLauncherClient *launcher = qobject_cast<AppLauncherClient*>(parent);
+ if (launcher)
+ connect(reader,
+ &AppStatusEventReader::statusUpdate,
+ launcher,
+ &AppLauncherClient::sendStatusEvent);
+ }
+
+ // Start status event handling
+ m_event_thread.start();
+}
+
+bool AppLauncherGrpcClient::StartApplication(const QString &id)
+{
+ StartRequest request;
+ request.set_id(id.toStdString());
+
+ ClientContext context;
+ StartResponse response;
+ Status status = stub_->StartApplication(&context, request, &response);
+
+ return status.ok();
+}
+
+bool AppLauncherGrpcClient::ListApplications(QList<QMap<QString, QString>> &list)
+{
+ ListRequest request; // empty
+ ClientContext context;
+ ListResponse response;
+
+ Status status = stub_->ListApplications(&context, request, &response);
+ if (!status.ok())
+ return false;
+
+ for (int i = 0; i < response.apps_size(); i++) {
+ QMap<QString, QString> appinfo_map;
+ AppInfo appinfo = response.apps(i);
+ appinfo_map["id"] = QString::fromStdString(appinfo.id());
+ appinfo_map["name"] = QString::fromStdString(appinfo.name());
+ appinfo_map["icon_path"] = QString::fromStdString(appinfo.icon_path());
+ list.append(appinfo_map);
+ }
+
+ return true;
+}
diff --git a/applauncher/AppLauncherGrpcClient.h b/applauncher/AppLauncherGrpcClient.h
new file mode 100644
index 0000000..f9e1d0f
--- /dev/null
+++ b/applauncher/AppLauncherGrpcClient.h
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Copyright (C) 2022 Konsulko Group
+ */
+
+#ifndef APPLAUNCHER_GRPC_CLIENT_H
+#define APPLAUNCHER_GRPC_CLIENT_H
+
+#include <QObject>
+#include <QList>
+#include <QMap>
+#include <QThread>
+#include <grpcpp/grpcpp.h>
+
+#include "applauncher.grpc.pb.h"
+
+using grpc::Channel;
+
+class AppStatusEventReader : public QObject
+{
+ Q_OBJECT
+public:
+ AppStatusEventReader(std::shared_ptr<automotivegradelinux::AppLauncher::Stub> &stub,
+ QObject *parent = Q_NULLPTR) : QObject(parent), stub_(stub) {}
+
+public slots:
+ void GetStatusEvents();
+
+signals:
+ void statusUpdate(const QString &id, const QString &status);
+ void finished();
+
+private:
+ std::shared_ptr<automotivegradelinux::AppLauncher::Stub> stub_;
+};
+
+class AppLauncherGrpcClient : public QObject
+{
+ Q_OBJECT
+
+public:
+ AppLauncherGrpcClient(QObject *parent = Q_NULLPTR);
+
+ bool StartApplication(const QString &id);
+
+ bool ListApplications(QList<QMap<QString, QString>> &list);
+
+private:
+ std::shared_ptr<automotivegradelinux::AppLauncher::Stub> stub_;
+
+ QThread m_event_thread;
+
+};
+
+#endif // APPLAUNCHER_GRPC_CLIENT_H
diff --git a/applauncher/meson.build b/applauncher/meson.build
new file mode 100644
index 0000000..b36184a
--- /dev/null
+++ b/applauncher/meson.build
@@ -0,0 +1,56 @@
+cpp = meson.get_compiler('cpp')
+grpcpp_reflection_dep = cpp.find_library('grpc++_reflection')
+
+qt5_dep = dependency('qt5', modules: ['Qml'])
+applauncher_dep = [
+ qt5_dep,
+ dependency('protobuf'),
+ dependency('grpc'),
+ dependency('grpc++'),
+ grpcpp_reflection_dep,
+]
+
+protoc = find_program('protoc')
+grpc_cpp = find_program('grpc_cpp_plugin')
+
+protoc_gen = generator(protoc, \
+ output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
+ arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/protos',
+ '--cpp_out=@BUILD_DIR@',
+ '@INPUT@'])
+generated_protoc_sources = protoc_gen.process('protos/applauncher.proto')
+
+grpc_gen = generator(protoc, \
+ output : ['@BASENAME@.grpc.pb.cc', '@BASENAME@.grpc.pb.h'],
+ arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/protos',
+ '--grpc_out=@BUILD_DIR@',
+ '--plugin=protoc-gen-grpc=' + grpc_cpp.path(),
+ '@INPUT@'])
+generated_grpc_sources = grpc_gen.process('protos/applauncher.proto')
+
+moc_files = qt5.compile_moc(headers : ['AppLauncherClient.h', 'AppLauncherGrpcClient.h'],
+ dependencies: qt5_dep)
+
+src = [
+ 'AppLauncherClient.cpp',
+ 'AppLauncherGrpcClient.cpp',
+ generated_protoc_sources,
+ generated_grpc_sources,
+ moc_files
+]
+lib = shared_library('qtappfw-applauncher',
+ sources: src,
+ version: '1.0.0',
+ soversion: '0',
+ dependencies: applauncher_dep,
+ install: true)
+
+install_headers('AppLauncherClient.h')
+
+pkg_mod = import('pkgconfig')
+pkg_mod.generate(libraries : lib,
+ version : '1.0',
+ name : 'libqtappfw-applauncher',
+ filebase : 'qtappfw-applauncher',
+ requires: 'Qt5Qml',
+ description : 'Library wrapping AGL AppLauncher API in Qt objects')
diff --git a/applauncher/protos/applauncher.proto b/applauncher/protos/applauncher.proto
new file mode 100644
index 0000000..0b8e0fc
--- /dev/null
+++ b/applauncher/protos/applauncher.proto
@@ -0,0 +1,50 @@
+syntax = "proto3";
+
+package automotivegradelinux;
+
+service AppLauncher {
+ rpc StartApplication(StartRequest) returns (StartResponse) {}
+ rpc ListApplications(ListRequest) returns (ListResponse) {}
+ rpc GetStatusEvents(StatusRequest) returns (stream StatusResponse) {}
+}
+
+message StartRequest {
+ string id = 1;
+}
+
+message StartResponse {
+ bool status = 1;
+ string message = 2;
+}
+
+message ListRequest {
+}
+
+message ListResponse {
+ repeated AppInfo apps = 1;
+}
+
+message AppInfo {
+ string id = 1;
+ string name = 2;
+ string icon_path = 3;
+}
+
+message StatusRequest {
+}
+
+message AppStatus {
+ string id = 1;
+ string status = 2;
+}
+
+// Future-proofing for e.g. potentially signaling a list refresh
+message LauncherStatus {
+}
+
+message StatusResponse {
+ oneof status {
+ AppStatus app = 1;
+ LauncherStatus launcher = 2;
+ }
+}