Added missing files

This commit is contained in:
Dmitriy Karpushin 2022-12-27 11:10:01 +03:00
parent 82165eaf37
commit ad7fc937a9
5 changed files with 465 additions and 4 deletions

View file

@ -277,12 +277,16 @@ if(ANDROID)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_controller.h
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_notificationhandler.h
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidutils.h
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidvpnactivity.h
${CMAKE_CURRENT_LIST_DIR}/protocols/android_vpnprotocol.h
)
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_controller.cp
${CMAKE_CURRENT_LIST_DIR}/platforms/android/android_notificationhandler.cpp
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidutils.cpp
${CMAKE_CURRENT_LIST_DIR}/platforms/android/androidvpnactivity.cpp
${CMAKE_CURRENT_LIST_DIR}/protocols/android_vpnprotocol.cpp
)
endif()
@ -465,6 +469,8 @@ set_source_files_properties(
endif()
if(ANDROID)
set(QT_ANDROID_BUILD_ALL_ABIS ON)
add_custom_command(
TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
@ -489,16 +495,17 @@ if(ANDROID)
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/PackageManagerHelper.java
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNActivity.kt
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNApplication.java
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
${CMAKE_CURRENT_LIST_DIR}/android/src/org/amnezia/vpn/qt/VPNPermissionHelper.kt
${CMAKE_CURRENT_BINARY_DIR}
)
set_property(TARGET ${PROJECT}
set_property(TARGET ${PROJECT} PROPERTY
QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_LIST_DIR}/android
)
foreach(abi IN ANDROID_ABIS)
foreach(abi IN LISTS ${QT_ANDROID_ABIS})
if(ANDROID_TARGET_ARCH EQUAL ${abi})
set(LIBS ${LIBS}
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/android/${abi}/libcrypto.a
@ -518,6 +525,7 @@ if(ANDROID)
${CMAKE_CURRENT_LIST_DIR}/android/lib/openvpn/${abi}/libovpnexec.so
)
endforeach()
endif()
target_link_libraries(${PROJECT} PRIVATE ${LIBS})

View file

@ -0,0 +1,174 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "androidutils.h"
#include <QApplication>
#include <QJniEnvironment>
#include <QJniObject>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkCookieJar>
#include <QUrlQuery>
#include <QTimer>
#include "jni.h"
namespace {
AndroidUtils* s_instance = nullptr;
} // namespace
// static
QString AndroidUtils::GetDeviceName() {
QJniEnvironment env;
jclass BUILD = env->FindClass("android/os/Build");
jfieldID model = env->GetStaticFieldID(BUILD, "MODEL", "Ljava/lang/String;");
jstring value = (jstring)env->GetStaticObjectField(BUILD, model);
if (!value) {
return QString("Android Device");
}
const char* buffer = env->GetStringUTFChars(value, nullptr);
if (!buffer) {
return QString("Android Device");
}
QString res(buffer);
env->ReleaseStringUTFChars(value, buffer);
return res;
};
// static
AndroidUtils* AndroidUtils::instance() {
if (!s_instance) {
Q_ASSERT(qApp);
s_instance = new AndroidUtils(qApp);
}
return s_instance;
}
AndroidUtils::AndroidUtils(QObject* parent) : QObject(parent) {
Q_ASSERT(!s_instance);
s_instance = this;
}
AndroidUtils::~AndroidUtils() {
Q_ASSERT(s_instance == this);
s_instance = nullptr;
}
// static
void AndroidUtils::dispatchToMainThread(std::function<void()> callback) {
QTimer* timer = new QTimer();
timer->moveToThread(qApp->thread());
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [=]() {
callback();
timer->deleteLater();
});
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection);
}
// static
QByteArray AndroidUtils::getQByteArrayFromJString(JNIEnv* env, jstring data) {
const char* buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
qDebug() << "getQByteArrayFromJString - failed to parse data.";
return QByteArray();
}
QByteArray out(buffer);
env->ReleaseStringUTFChars(data, buffer);
return out;
}
// static
QString AndroidUtils::getQStringFromJString(JNIEnv* env, jstring data) {
const char* buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
qDebug() << "getQStringFromJString - failed to parse data.";
return QString();
}
QString out(buffer);
env->ReleaseStringUTFChars(data, buffer);
return out;
}
// static
QJsonObject AndroidUtils::getQJsonObjectFromJString(JNIEnv* env, jstring data) {
QByteArray raw(getQByteArrayFromJString(env, data));
QJsonParseError jsonError;
QJsonDocument json = QJsonDocument::fromJson(raw, &jsonError);
if (QJsonParseError::NoError != jsonError.error) {
qDebug() << "getQJsonObjectFromJstring - error parsing json. Code: "
<< jsonError.error << "Offset: " << jsonError.offset
<< "Message: " << jsonError.errorString()
<< "Data: " << raw;
return QJsonObject();
}
if (!json.isObject()) {
qDebug() << "getQJsonObjectFromJString - object expected.";
return QJsonObject();
}
return json.object();
}
QJniObject AndroidUtils::getActivity() {
return QNativeInterface::QAndroidApplication::context();
}
int AndroidUtils::GetSDKVersion() {
QJniEnvironment env;
jclass versionClass = env->FindClass("android/os/Build$VERSION");
jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
int sdk = env->GetStaticIntField(versionClass, sdkIntFieldID);
return sdk;
}
QString AndroidUtils::GetManufacturer() {
QJniEnvironment env;
jclass buildClass = env->FindClass("android/os/Build");
jfieldID manuFacturerField =
env->GetStaticFieldID(buildClass, "MANUFACTURER", "Ljava/lang/String;");
jstring value =
(jstring)env->GetStaticObjectField(buildClass, manuFacturerField);
const char* buffer = env->GetStringUTFChars(value, nullptr);
if (!buffer) {
qDebug() << "Failed to fetch MANUFACTURER";
return QByteArray();
}
QString res(buffer);
qDebug() << "MANUFACTURER: " << res;
env->ReleaseStringUTFChars(value, buffer);
return res;
}
void AndroidUtils::runOnAndroidThreadSync(const std::function<void()> runnable) {
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable)
.waitForFinished();
}
void AndroidUtils::runOnAndroidThreadAsync(const std::function<void()> runnable) {
QNativeInterface::QAndroidApplication::runOnAndroidMainThread(runnable);
}
// Static
// Creates a copy of the passed QByteArray in the JVM and passes back a ref
jbyteArray AndroidUtils::tojByteArray(const QByteArray& data) {
QJniEnvironment env;
jbyteArray out = env->NewByteArray(data.size());
env->SetByteArrayRegion(out, 0, data.size(),
reinterpret_cast<const jbyte*>(data.constData()));
return out;
}

View file

@ -0,0 +1,49 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ANDROIDUTILS_H
#define ANDROIDUTILS_H
#include <jni.h>
#include <QJniEnvironment>
#include <QJniObject>
#include <QObject>
#include <QString>
#include <QUrl>
class AndroidUtils final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(AndroidUtils)
public:
static QString GetDeviceName();
static int GetSDKVersion();
static QString GetManufacturer();
static AndroidUtils* instance();
static void dispatchToMainThread(std::function<void()> callback);
static QByteArray getQByteArrayFromJString(JNIEnv* env, jstring data);
static jbyteArray tojByteArray(const QByteArray& data);
static QString getQStringFromJString(JNIEnv* env, jstring data);
static QJsonObject getQJsonObjectFromJString(JNIEnv* env, jstring data);
static QJniObject getActivity();
static void runOnAndroidThreadSync(const std::function<void()> runnable);
static void runOnAndroidThreadAsync(const std::function<void()> runnable);
private:
AndroidUtils(QObject* parent);
~AndroidUtils();
};
#endif // ANDROIDUTILS_H

View file

@ -0,0 +1,137 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "androidvpnactivity.h"
#include <QApplication>
#include <QJniEnvironment>
#include <QJniObject>
#include <QJsonDocument>
#include <QJsonObject>
#include "androidutils.h"
#include "jni.h"
namespace {
AndroidVPNActivity* s_instance = nullptr;
constexpr auto CLASSNAME = "org.amnezia.vpn.qt.VPNActivity";
}
AndroidVPNActivity::AndroidVPNActivity() {
AndroidUtils::runOnAndroidThreadAsync([]() {
JNINativeMethod methods[]{
{"handleBackButton", "()Z", reinterpret_cast<bool*>(handleBackButton)},
{"onServiceMessage", "(ILjava/lang/String;)V",
reinterpret_cast<void*>(onServiceMessage)},
{"qtOnServiceConnected", "()V",
reinterpret_cast<void*>(onServiceConnected)},
{"qtOnServiceDisconnected", "()V",
reinterpret_cast<void*>(onServiceDisconnected)},
};
QJniObject javaClass(CLASSNAME);
QJniEnvironment env;
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
env->DeleteLocalRef(objectClass);
});
}
void AndroidVPNActivity::maybeInit() {
if (s_instance == nullptr) {
s_instance = new AndroidVPNActivity();
}
}
// static
bool AndroidVPNActivity::handleBackButton(JNIEnv* env, jobject thiz) {
Q_UNUSED(env);
Q_UNUSED(thiz);
// return Navigator::instance()->eventHandled();
}
void AndroidVPNActivity::connectService() {
QJniObject::callStaticMethod<void>(CLASSNAME, "connectService", "()V");
}
// static
AndroidVPNActivity* AndroidVPNActivity::instance() {
if (s_instance == nullptr) {
AndroidVPNActivity::maybeInit();
}
return s_instance;
}
// static
void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) {
int messageType = (int)type;
QJniEnvironment env;
QJniObject::callStaticMethod<void>(
CLASSNAME, "sendToService", "(ILjava/lang/String;)V",
static_cast<int>(messageType),
QJniObject::fromString(data).object<jstring>());
}
// static
void AndroidVPNActivity::onServiceMessage(JNIEnv* env, jobject thiz,
jint messageType, jstring body) {
Q_UNUSED(thiz);
const char* buffer = env->GetStringUTFChars(body, nullptr);
if (!buffer) {
return;
}
QString parcelBody(buffer);
env->ReleaseStringUTFChars(body, buffer);
AndroidUtils::dispatchToMainThread([messageType, parcelBody] {
AndroidVPNActivity::instance()->handleServiceMessage(messageType,
parcelBody);
});
}
void AndroidVPNActivity::handleServiceMessage(int code, const QString& data) {
auto mode = (ServiceEvents)code;
switch (mode) {
case ServiceEvents::EVENT_INIT:
emit eventInitialized(data);
break;
case ServiceEvents::EVENT_CONNECTED:
emit eventConnected(data);
break;
case ServiceEvents::EVENT_DISCONNECTED:
emit eventDisconnected(data);
break;
case ServiceEvents::EVENT_STATISTIC_UPDATE:
emit eventStatisticUpdate(data);
break;
case ServiceEvents::EVENT_BACKEND_LOGS:
emit eventBackendLogs(data);
break;
case ServiceEvents::EVENT_ACTIVATION_ERROR:
emit eventActivationError(data);
break;
case ServiceEvents::EVENT_CONFIG_IMPORT:
emit eventConfigImport(data);
break;
default:
Q_ASSERT(false);
}
}
void AndroidVPNActivity::onServiceConnected(JNIEnv* env, jobject thiz) {
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidVPNActivity::instance()->serviceConnected();
}
void AndroidVPNActivity::onServiceDisconnected(JNIEnv* env, jobject thiz) {
Q_UNUSED(env);
Q_UNUSED(thiz);
emit AndroidVPNActivity::instance()->serviceDisconnected();
}

View file

@ -0,0 +1,93 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ANDROIDVPNACTIVITY_H
#define ANDROIDVPNACTIVITY_H
#include <QObject>
#include "jni.h"
// Binder Codes for VPNServiceBinder
// See also - VPNServiceBinder.kt
// Actions that are Requestable
enum ServiceAction {
// Activate the vpn. Body requires a json wg-conf
ACTION_ACTIVATE = 1,
// Deactivate the vpn. Body is empty
ACTION_DEACTIVATE = 2,
// Register an IBinder to recieve events body is an Ibinder
ACTION_REGISTERLISTENER = 3,
// Requests an EVENT_STATISTIC_UPDATE to be send
ACTION_REQUEST_STATISTIC = 4,
ACTION_REQUEST_GET_LOG = 5,
// Requests to clean up the internal log
ACTION_REQUEST_CLEANUP_LOG = 6,
// Retry activation using the last config
// Used when the activation is aborted for VPN-Permission prompt
ACTION_RESUME_ACTIVATE = 7,
// Sets the current notification text.
// Does nothing if there is no notification
ACTION_SET_NOTIFICATION_TEXT = 8,
// Sets the fallback text if the OS triggered the VPN-Service
// to show a notification
ACTION_SET_NOTIFICATION_FALLBACK = 9,
// Share used config
ACTION_SHARE_CONFIG = 10,
};
typedef enum ServiceAction ServiceAction;
// Event Types that will be Dispatched after registration
enum ServiceEvents {
// The Service has Accecpted our Binder
// Responds with the current status of the vpn.
EVENT_INIT = 0,
// WG-Go has enabled the adapter (empty response)
EVENT_CONNECTED = 1,
// WG-Go has disabled the adapter (empty response)
EVENT_DISCONNECTED = 2,
// Contains the Current transfered bytes to endpoint x.
EVENT_STATISTIC_UPDATE = 3,
EVENT_BACKEND_LOGS = 4,
// An Error happened during activation
// Contains the error message
EVENT_ACTIVATION_ERROR = 5,
EVENT_NEED_PERMISSION = 6,
// Import of existing config
EVENT_CONFIG_IMPORT = 7,
};
typedef enum ServiceEvents ServiceEvents;
class AndroidVPNActivity : public QObject
{
Q_OBJECT
public:
static void maybeInit();
static AndroidVPNActivity* instance();
static bool handleBackButton(JNIEnv* env, jobject thiz);
static void sendToService(ServiceAction type, const QString& data);
static void connectService();
signals:
void serviceConnected();
void serviceDisconnected();
void eventInitialized(const QString& data);
void eventConnected(const QString& data);
void eventDisconnected(const QString& data);
void eventStatisticUpdate(const QString& data);
void eventBackendLogs(const QString& data);
void eventActivationError(const QString& data);
void eventConfigImport(const QString& data);
private:
AndroidVPNActivity();
static void onServiceMessage(JNIEnv* env, jobject thiz, jint messageType, jstring body);
static void onServiceConnected(JNIEnv* env, jobject thiz);
static void onServiceDisconnected(JNIEnv* env, jobject thiz);
void handleServiceMessage(int code, const QString& data);
};
#endif // ANDROIDVPNACTIVITY_H