Files
DefaultVPN/client/platforms/android/android_controller.cpp

268 lines
8.0 KiB
C++
Raw Normal View History

#include <QCoreApplication>
#include <QJniEnvironment>
2021-11-26 17:43:02 +03:00
#include <QJsonDocument>
#include "android_controller.h"
2023-11-21 22:48:52 +03:00
#include "ui/controllers/importController.h"
2021-11-26 17:43:02 +03:00
namespace
{
AndroidController *s_instance = nullptr;
2021-11-26 17:43:02 +03:00
constexpr auto QT_ANDROID_CONTROLLER_CLASS = "org/amnezia/vpn/qt/QtAndroidController";
} // namespace
2021-11-26 17:43:02 +03:00
AndroidController::AndroidController() : QObject()
2021-11-26 17:43:02 +03:00
{
connect(this, &AndroidController::status, this,
[this](bool isVpnConnected) {
qDebug() << "Android event: status; connected:" << isVpnConnected;
if (isWaitingInitStatus) {
qDebug() << "Android VPN service is alive, initialization by service status";
isWaitingInitStatus = false;
emit serviceIsAlive(isVpnConnected);
}
},
Qt::QueuedConnection);
connect(
this, &AndroidController::serviceDisconnected, this,
[this]() {
qDebug() << "Android event: service disconnected";
emit connectionStateChanged(Vpn::ConnectionState::Unknown);
},
Qt::QueuedConnection);
connect(
this, &AndroidController::serviceError, this,
[this]() {
qDebug() << "Android event: service error";
// todo: add error message
emit connectionStateChanged(Vpn::ConnectionState::Error);
},
Qt::QueuedConnection);
connect(
this, &AndroidController::vpnPermissionRejected, this,
[this]() {
qWarning() << "Android event: VPN permission rejected";
emit connectionStateChanged(Vpn::ConnectionState::Disconnected);
},
Qt::QueuedConnection);
connect(
this, &AndroidController::vpnConnected, this,
[this]() {
qDebug() << "Android event: VPN connected";
emit connectionStateChanged(Vpn::ConnectionState::Connected);
},
Qt::QueuedConnection);
connect(
this, &AndroidController::vpnDisconnected, this,
[this]() {
qDebug() << "Android event: VPN disconnected";
emit connectionStateChanged(Vpn::ConnectionState::Disconnected);
},
Qt::QueuedConnection);
connect(
this, &AndroidController::configImported, this,
[]() {
// todo: not yet implemented
qDebug() << "Transact: config import";
/*auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
QString buffer = doc.object()["config"].toString();
qDebug() << "Transact: config string" << buffer;
importConfigFromOutside(buffer);*/
},
Qt::QueuedConnection);
2021-11-26 17:43:02 +03:00
}
AndroidController *AndroidController::instance()
{
if (!s_instance) {
s_instance = new AndroidController();
}
2021-11-26 17:43:02 +03:00
return s_instance;
}
bool AndroidController::initialize()
2021-11-26 17:43:02 +03:00
{
qDebug() << "Initialize AndroidController";
2021-11-26 17:43:02 +03:00
const JNINativeMethod methods[] = {
{"onStatus", "(Z)V", reinterpret_cast<void *>(onStatus)},
{"onServiceDisconnected", "()V", reinterpret_cast<void *>(onServiceDisconnected)},
{"onServiceError", "()V", reinterpret_cast<void *>(onServiceError)},
{"onVpnPermissionRejected", "()V", reinterpret_cast<void *>(onVpnPermissionRejected)},
{"onVpnConnected", "()V", reinterpret_cast<void *>(onVpnConnected)},
{"onVpnDisconnected", "()V", reinterpret_cast<void *>(onVpnDisconnected)},
{"onStatisticsUpdate", "(JJ)V", reinterpret_cast<void *>(onStatisticsUpdate)},
{"onConfigImported", "()V", reinterpret_cast<void *>(onConfigImported)},
2023-11-21 22:48:52 +03:00
{"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast<bool *>(decodeQrCode)}
};
2021-11-26 17:43:02 +03:00
QJniEnvironment env;
bool registered = env.registerNativeMethods(QT_ANDROID_CONTROLLER_CLASS, methods,
sizeof(methods) / sizeof(JNINativeMethod));
if (!registered) {
qCritical() << "Failed native method registration";
return false;
}
qtAndroidControllerInitialized();
return true;
2021-11-26 17:43:02 +03:00
}
// static
template <typename Ret, typename ...Args>
auto AndroidController::callActivityMethod(const char *methodName, const char *signature,
const std::function<Ret()> &defValue, Args &&...args)
2021-11-26 17:43:02 +03:00
{
qDebug() << "Call activity method:" << methodName;
QJniObject activity = QNativeInterface::QAndroidApplication::context();
if (activity.isValid()) {
return activity.callMethod<Ret>(methodName, signature, std::forward<Args>(args)...);
} else {
qCritical() << "Activity is not valid";
return defValue();
}
}
2021-11-26 17:43:02 +03:00
// static
template <typename ...Args>
void AndroidController::callActivityMethod(const char *methodName, const char *signature, Args &&...args)
{
callActivityMethod<void>(methodName, signature, [] {}, std::forward<Args>(args)...);
}
ErrorCode AndroidController::start(const QJsonObject &vpnConfig)
{
isWaitingInitStatus = false;
auto config = QJsonDocument(vpnConfig).toJson();
callActivityMethod("start", "(Ljava/lang/String;)V",
QJniObject::fromString(config).object<jstring>());
2021-11-26 17:43:02 +03:00
return NoError;
2021-11-26 17:43:02 +03:00
}
void AndroidController::stop()
{
callActivityMethod("stop", "()V");
2021-11-26 17:43:02 +03:00
}
void AndroidController::saveFile(const QString &fileName, const QString &data)
{
callActivityMethod("saveFile", "(Ljava/lang/String;Ljava/lang/String;)V",
QJniObject::fromString(fileName).object<jstring>(),
QJniObject::fromString(data).object<jstring>());
2021-11-26 17:43:02 +03:00
}
void AndroidController::setNotificationText(const QString &title, const QString &message, int timerSec)
{
callActivityMethod("setNotificationText", "(Ljava/lang/String;Ljava/lang/String;I)V",
QJniObject::fromString(title).object<jstring>(),
QJniObject::fromString(message).object<jstring>(),
(jint) timerSec);
2021-11-26 17:43:02 +03:00
}
void AndroidController::startQrReaderActivity()
{
callActivityMethod("startQrCodeReader", "()V");
}
void AndroidController::qtAndroidControllerInitialized()
{
callActivityMethod("qtAndroidControllerInitialized", "()V");
2021-11-26 17:43:02 +03:00
}
// JNI functions called by Android
// static
void AndroidController::onStatus(JNIEnv *env, jobject thiz, jboolean isVpnConnected)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->status(isVpnConnected);
2021-11-26 17:43:02 +03:00
}
// static
void AndroidController::onServiceDisconnected(JNIEnv *env, jobject thiz)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
2021-11-26 17:43:02 +03:00
emit AndroidController::instance()->serviceDisconnected();
2021-11-26 17:43:02 +03:00
}
// static
void AndroidController::onServiceError(JNIEnv *env, jobject thiz)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
2021-11-26 17:43:02 +03:00
emit AndroidController::instance()->serviceError();
2021-11-26 17:43:02 +03:00
}
// static
void AndroidController::onVpnPermissionRejected(JNIEnv *env, jobject thiz)
2021-11-26 17:43:02 +03:00
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->vpnPermissionRejected();
2021-11-26 17:43:02 +03:00
}
// static
void AndroidController::onVpnConnected(JNIEnv *env, jobject thiz)
2021-11-26 17:43:02 +03:00
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->vpnConnected();
2021-11-26 17:43:02 +03:00
}
// static
void AndroidController::onVpnDisconnected(JNIEnv *env, jobject thiz)
2023-02-22 14:22:03 +03:00
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->vpnDisconnected();
2023-02-22 14:22:03 +03:00
}
// static
void AndroidController::onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes)
2022-12-14 18:52:19 +03:00
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->statisticsUpdated((quint64) rxBytes, (quint64) txBytes);
2022-12-14 18:52:19 +03:00
}
2021-11-26 17:43:02 +03:00
2023-11-21 22:48:52 +03:00
// static
void AndroidController::onConfigImported(JNIEnv *env, jobject thiz)
2021-11-26 17:43:02 +03:00
{
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidController::instance()->configImported();
2021-11-26 17:43:02 +03:00
}
2023-11-21 22:48:52 +03:00
// static
bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data)
{
Q_UNUSED(thiz);
const char *buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
return false;
}
QString code(buffer);
env->ReleaseStringUTFChars(data, buffer);
return ImportController::decodeQrCode(code);
}