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
connect(AndroidController::instance(), &AndroidController::serviceIsAlive, this,
[this](bool connected) {
if (connected) {
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
}
[this](Vpn::ConnectionState state) {
m_connectionController->onConnectionStateChanged(state);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
});
if (!AndroidController::instance()->initialize()) {
qCritical() << QString("Init failed");

View file

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

View file

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

View file

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

View file

@ -123,7 +123,7 @@ class AmneziaVpnService : VpnService() {
clientMessenger.send {
ServiceEvent.STATUS.packToMessage {
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
*/
object QtAndroidController {
external fun onStatus(isVpnConnected: Boolean)
external fun onStatus(stateCode: Int)
external fun onServiceDisconnected()
external fun onServiceError()

View file

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

View file

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