Add passing the VPN connection status when rebinding to the service

This commit is contained in:
albexk 2023-12-05 16:10:29 +03:00
parent 5835a756ce
commit dc6e3ec53b
8 changed files with 65 additions and 28 deletions

View file

@ -92,12 +92,10 @@ void AmneziaApplication::init()
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::serviceIsAlive, this, connect(AndroidController::instance(), &AndroidController::serviceIsAlive, this,
[this](bool connected) { [this](Vpn::ConnectionState state) {
if (connected) { m_connectionController->onConnectionStateChanged(state);
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected); if (m_vpnConnection)
if (m_vpnConnection) m_vpnConnection->restoreConnection();
m_vpnConnection->restoreConnection();
}
}); });
if (!AndroidController::instance()->initialize()) { if (!AndroidController::instance()->initialize()) {
qCritical() << QString("Init failed"); qCritical() << QString("Init failed");

View file

@ -1,5 +1,6 @@
package org.amnezia.vpn.protocol package org.amnezia.vpn.protocol
// keep synchronized with client/platforms/android/android_controller.h ConnectionState
enum class ProtocolState { enum class ProtocolState {
CONNECTED, CONNECTED,
CONNECTING, CONNECTING,

View file

@ -2,19 +2,19 @@ package org.amnezia.vpn.protocol
import android.os.Bundle import android.os.Bundle
private const val IS_CONNECTED_KEY = "isConnected" private const val STATE_KEY = "state"
@Suppress("DataClassPrivateConstructor") @Suppress("DataClassPrivateConstructor")
data class Status private constructor( data class Status private constructor(
val isConnected: Boolean = false val state: ProtocolState
) { ) {
private constructor(builder: Builder) : this(builder.isConnected) private constructor(builder: Builder) : this(builder.state)
class Builder { class Builder {
var isConnected: Boolean = false lateinit var state: ProtocolState
private set private set
fun setConnected(isConnected: Boolean) = apply { this.isConnected = isConnected } fun setState(state: ProtocolState) = apply { this.state = state }
fun build(): Status = Status(this) fun build(): Status = Status(this)
} }
@ -24,11 +24,11 @@ data class Status private constructor(
} }
} }
fun Bundle.putStatus(statistics: Status) { fun Bundle.putStatus(status: Status) {
putBoolean(IS_CONNECTED_KEY, statistics.isConnected) putInt(STATE_KEY, status.state.ordinal)
} }
fun Bundle.getStatus(): Status = fun Bundle.getStatus(): Status =
Status.build { Status.build {
setConnected(getBoolean(IS_CONNECTED_KEY)) setState(ProtocolState.entries[getInt(STATE_KEY)])
} }

View file

@ -62,8 +62,8 @@ class AmneziaActivity : QtActivity() {
ServiceEvent.STATUS -> { ServiceEvent.STATUS -> {
if (isWaitingStatus) { if (isWaitingStatus) {
isWaitingStatus = false isWaitingStatus = false
msg.data?.getStatus()?.let { (isConnected) -> msg.data?.getStatus()?.let { (state) ->
QtAndroidController.onStatus(isConnected) QtAndroidController.onStatus(state.ordinal)
} }
} }
} }

View file

@ -123,7 +123,7 @@ class AmneziaVpnService : VpnService() {
clientMessenger.send { clientMessenger.send {
ServiceEvent.STATUS.packToMessage { ServiceEvent.STATUS.packToMessage {
putStatus(Status.build { putStatus(Status.build {
setConnected(this@AmneziaVpnService.isConnected) setState(this@AmneziaVpnService.protocolState.value)
}) })
} }
} }

View file

@ -5,7 +5,7 @@ package org.amnezia.vpn.qt
* called by events in the Android part of the client * called by events in the Android part of the client
*/ */
object QtAndroidController { object QtAndroidController {
external fun onStatus(isVpnConnected: Boolean) external fun onStatus(stateCode: Int)
external fun onServiceDisconnected() external fun onServiceDisconnected()
external fun onServiceError() external fun onServiceError()

View file

@ -15,12 +15,12 @@ namespace
AndroidController::AndroidController() : QObject() AndroidController::AndroidController() : QObject()
{ {
connect(this, &AndroidController::status, this, connect(this, &AndroidController::status, this,
[this](bool isVpnConnected) { [this](AndroidController::ConnectionState state) {
qDebug() << "Android event: status; connected:" << isVpnConnected; qDebug() << "Android event: status; state:" << textConnectionState(state);
if (isWaitingStatus) { if (isWaitingStatus) {
qDebug() << "Android VPN service is alive, initialization by service status"; qDebug() << "Android VPN service is alive, initialization by service status";
isWaitingStatus = false; isWaitingStatus = false;
emit serviceIsAlive(isVpnConnected); emit serviceIsAlive(convertState(state));
} }
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
@ -30,7 +30,7 @@ AndroidController::AndroidController() : QObject()
[this]() { [this]() {
qDebug() << "Android event: service disconnected"; qDebug() << "Android event: service disconnected";
isWaitingStatus = true; isWaitingStatus = true;
emit connectionStateChanged(Vpn::ConnectionState::Unknown); emit connectionStateChanged(Vpn::ConnectionState::Disconnected);
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
@ -95,7 +95,7 @@ bool AndroidController::initialize()
qDebug() << "Initialize AndroidController"; qDebug() << "Initialize AndroidController";
const JNINativeMethod methods[] = { const JNINativeMethod methods[] = {
{"onStatus", "(Z)V", reinterpret_cast<void *>(onStatus)}, {"onStatus", "(I)V", reinterpret_cast<void *>(onStatus)},
{"onServiceDisconnected", "()V", reinterpret_cast<void *>(onServiceDisconnected)}, {"onServiceDisconnected", "()V", reinterpret_cast<void *>(onServiceDisconnected)},
{"onServiceError", "()V", reinterpret_cast<void *>(onServiceError)}, {"onServiceError", "()V", reinterpret_cast<void *>(onServiceError)},
{"onVpnPermissionRejected", "()V", reinterpret_cast<void *>(onVpnPermissionRejected)}, {"onVpnPermissionRejected", "()V", reinterpret_cast<void *>(onVpnPermissionRejected)},
@ -179,14 +179,40 @@ void AndroidController::qtAndroidControllerInitialized()
callActivityMethod("qtAndroidControllerInitialized", "()V"); callActivityMethod("qtAndroidControllerInitialized", "()V");
} }
// static
Vpn::ConnectionState AndroidController::convertState(AndroidController::ConnectionState state)
{
switch (state) {
case AndroidController::ConnectionState::CONNECTED: return Vpn::ConnectionState::Connected;
case AndroidController::ConnectionState::CONNECTING: return Vpn::ConnectionState::Connecting;
case AndroidController::ConnectionState::DISCONNECTED: return Vpn::ConnectionState::Disconnected;
case AndroidController::ConnectionState::DISCONNECTING: return Vpn::ConnectionState::Disconnecting;
case AndroidController::ConnectionState::UNKNOWN: return Vpn::ConnectionState::Unknown;
}
}
// static
QString AndroidController::textConnectionState(AndroidController::ConnectionState state)
{
switch (state) {
case AndroidController::ConnectionState::CONNECTED: return "CONNECTED";
case AndroidController::ConnectionState::CONNECTING: return "CONNECTING";
case AndroidController::ConnectionState::DISCONNECTED: return "DISCONNECTED";
case AndroidController::ConnectionState::DISCONNECTING: return "DISCONNECTING";
case AndroidController::ConnectionState::UNKNOWN: return "UNKNOWN";
}
}
// JNI functions called by Android // JNI functions called by Android
// static // static
void AndroidController::onStatus(JNIEnv *env, jobject thiz, jboolean isVpnConnected) void AndroidController::onStatus(JNIEnv *env, jobject thiz, jint stateCode)
{ {
Q_UNUSED(env); Q_UNUSED(env);
Q_UNUSED(thiz); Q_UNUSED(thiz);
emit AndroidController::instance()->status(isVpnConnected); auto state = ConnectionState(stateCode);
emit AndroidController::instance()->status(state);
} }
// static // static

View file

@ -17,6 +17,15 @@ public:
bool initialize(); bool initialize();
// keep synchronized with org.amnezia.vpn.protocol.ProtocolState
enum class ConnectionState {
CONNECTED,
CONNECTING,
DISCONNECTED,
DISCONNECTING,
UNKNOWN
};
ErrorCode start(const QJsonObject &vpnConfig); ErrorCode start(const QJsonObject &vpnConfig);
void stop(); void stop();
void setNotificationText(const QString &title, const QString &message, int timerSec); void setNotificationText(const QString &title, const QString &message, int timerSec);
@ -25,7 +34,7 @@ public:
signals: signals:
void connectionStateChanged(Vpn::ConnectionState state); void connectionStateChanged(Vpn::ConnectionState state);
void status(bool isVpnConnected); void status(ConnectionState state);
void serviceDisconnected(); void serviceDisconnected();
void serviceError(); void serviceError();
void vpnPermissionRejected(); void vpnPermissionRejected();
@ -34,15 +43,18 @@ signals:
void statisticsUpdated(quint64 rxBytes, quint64 txBytes); void statisticsUpdated(quint64 rxBytes, quint64 txBytes);
void configImported(); void configImported();
void importConfigFromOutside(QString &data); void importConfigFromOutside(QString &data);
void serviceIsAlive(bool connected); void serviceIsAlive(Vpn::ConnectionState state);
private: private:
bool isWaitingStatus = true; bool isWaitingStatus = true;
void qtAndroidControllerInitialized(); void qtAndroidControllerInitialized();
static Vpn::ConnectionState convertState(ConnectionState state);
static QString textConnectionState(ConnectionState state);
// JNI functions called by Android // JNI functions called by Android
static void onStatus(JNIEnv *env, jobject thiz, jboolean isVpnConnected); static void onStatus(JNIEnv *env, jobject thiz, jint stateCode);
static void onServiceDisconnected(JNIEnv *env, jobject thiz); static void onServiceDisconnected(JNIEnv *env, jobject thiz);
static void onServiceError(JNIEnv *env, jobject thiz); static void onServiceError(JNIEnv *env, jobject thiz);
static void onVpnPermissionRejected(JNIEnv *env, jobject thiz); static void onVpnPermissionRejected(JNIEnv *env, jobject thiz);