Merge pull request #452 from amnezia-vpn/refactoring/android

Android refactoring
This commit is contained in:
pokamest 2023-12-23 13:48:26 -05:00 committed by GitHub
commit 5ad54bfdc1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
238 changed files with 5101 additions and 84686 deletions

1
.gitattributes vendored
View file

@ -3,3 +3,4 @@ deploy/data/windows/x64/tap/windows_10/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_7/OemVista.inf eol=crlf
deploy/data/windows/x32/tap/windows_10/OemVista.inf eol=crlf
client/3rd/* linguist-vendored
client/android/gradlew.bat eol=crlf

View file

@ -273,21 +273,10 @@ jobs:
name: 'Build-Android'
runs-on: ubuntu-latest
strategy:
matrix:
include:
- abi: 'x86_64'
qt_arch: 'android_x86_64'
- abi: 'x86'
qt_arch: 'android_x86'
- abi: 'armeabi-v7a'
qt_arch: 'android_armv7'
- abi: 'arm64-v8a'
qt_arch: 'android_arm64_v8a'
env:
QT_VERSION: 6.5.2
ANDROID_BUILD_PLATFORM: android-33
ANDROID_BUILD_PLATFORM: android-34
QT_VERSION: 6.6.1
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
steps:
- name: 'Install desktop Qt'
@ -297,29 +286,58 @@ jobs:
host: 'linux'
target: 'desktop'
arch: 'gcc_64'
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android Qt'
- name: 'Install android_x86_64 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: ${{ matrix.qt_arch }}
modules: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
arch: 'android_x86_64'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_x86 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_x86'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_armv7 Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_armv7'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Install android_arm64_v8a Qt'
uses: jurplel/install-qt-action@v3
with:
version: ${{ env.QT_VERSION }}
host: 'linux'
target: 'android'
arch: 'android_arm64_v8a'
modules: ${{ env.QT_MODULES }}
dir: ${{ runner.temp }}
setup-python: 'true'
set-env: 'true'
extra: '--external 7z --base ${{ env.QT_MIRROR }}'
- name: 'Grant execute permission for qt-cmake'
shell: bash
run: |
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/${{ matrix.qt_arch }}/bin/qt-cmake
chmod +x ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/android_x86_64/bin/qt-cmake
- name: 'Get sources'
uses: actions/checkout@v3
@ -333,15 +351,14 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
java-version: '17'
cache: 'gradle'
- name: 'Setup Android NDK'
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: 'r25c'
local-cache: 'true'
ndk-version: 'r26b'
- name: 'Decode keystore secret to file'
env:
@ -354,16 +371,36 @@ jobs:
env:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
QT_HOST_PATH: ${{ runner.temp }}/Qt/${{ env.QT_VERSION }}/gcc_64
QT_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
QT_ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
QT_ANDROID_KEYSTORE_STORE_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
QT_ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android.keystore
ANDROID_KEYSTORE_KEY_ALIAS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_ALIAS }}
ANDROID_KEYSTORE_KEY_PASS: ${{ secrets.ANDROID_RELEASE_KEYSTORE_KEY_PASS }}
shell: bash
run: ./deploy/build_android.sh --apk ${{ matrix.abi }} --platform ${{ env.ANDROID_BUILD_PLATFORM }}
run: ./deploy/build_android.sh --apk all --build-platform ${{ env.ANDROID_BUILD_PLATFORM }}
- name: 'Upload apk'
- name: 'Upload x86_64 apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-${{ matrix.abi }}
path: deploy/build/AmneziaVPN-${{ matrix.abi }}-release-signed.apk
name: AmneziaVPN-android-x86_64
path: deploy/build/AmneziaVPN-x86_64-release.apk
retention-days: 7
- name: 'Upload x86 apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-x86
path: deploy/build/AmneziaVPN-x86-release.apk
retention-days: 7
- name: 'Upload arm64-v8a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-arm64-v8a
path: deploy/build/AmneziaVPN-arm64-v8a-release.apk
retention-days: 7
- name: 'Upload armeabi-v7a apk'
uses: actions/upload-artifact@v3
with:
name: AmneziaVPN-android-armeabi-v7a
path: deploy/build/AmneziaVPN-armeabi-v7a-release.apk
retention-days: 7

View file

@ -11,6 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
set(RELEASE_DATE "${CURRENT_DATE}")
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
set(APP_ANDROID_VERSION_CODE 39)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")

View file

@ -91,13 +91,11 @@ void AmneziaApplication::init()
initControllers();
#ifdef Q_OS_ANDROID
connect(AndroidController::instance(), &AndroidController::initialized, this,
[this](bool status, bool connected, const QDateTime &connectionDate) {
if (connected) {
m_connectionController->onConnectionStateChanged(Vpn::ConnectionState::Connected);
if (m_vpnConnection)
m_vpnConnection->restoreConnection();
}
connect(AndroidController::instance(), &AndroidController::initConnectionState, this,
[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,166 +1,153 @@
<?xml version="1.0"?>
<manifest
<!-- Leave package attribute for androiddeployqt -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.amnezia.vpn"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="-- %%INSERT_VERSION_NAME%% --"
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies
of the application. Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:anyDensity="true"
android:smallScreens="true"/>
<uses-permission android:name="android.permission.INTERNET" />
<!-- To request network state -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Enable when VPN-per-app mode will be implemented -->
<!-- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> -->
<application
android:name=".qt.AmneziaApp"
android:hardwareAccelerated="true"
android:name=".AmneziaApplication"
android:label="-- %%INSERT_APP_NAME%% --"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
android:allowNativeHeapPointerTagging="false"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:icon="@drawable/icon"
android:roundIcon="@drawable/icon_round">
android:icon="@mipmap/icon"
android:roundIcon="@mipmap/icon_round"
android:theme="@style/NoActionBar">
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name=".qt.VPNActivity"
android:label="-- %%INSERT_APP_NAME%% --"
android:screenOrientation="unspecified"
android:name=".AmneziaActivity"
android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density
|fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc"
android:launchMode="singleInstance"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<!-- android:theme="@style/splashScreenTheme"-->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
<action android:name="org.amnezia.vpn.IMPORT_CONFIG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
<meta-data
android:name="android.app.extract_android_style"
android:value="minimal" />
<meta-data
android:name="android.app.background_running"
android:value="false"/>
<meta-data
android:name="android.app.arguments"
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
</activity>
<activity
android:name=".qt.CameraActivity"
android:name=".CameraActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false" />
<activity
android:name=".qt.ImportConfigActivity"
android:exported="true" >
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.vpn"/>
<data android:pathPattern=".*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
android:name=".VpnRequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false"
android:theme="@style/Translucent" />
<activity
android:name=".ImportConfigActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="true"
android:theme="@style/Translucent">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/octet-stream" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.cfg"/>
<data android:pathPattern=".*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="vpn" android:host="*" />
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.conf"/>
<data android:pathPattern=".*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.vpn" />
<data android:pathPattern=".*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn" />
<data android:pathPattern=".*\\.cfg" />
<data android:pathPattern=".*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg" />
<data android:pathPattern=".*\\.conf" />
<data android:pathPattern=".*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf" />
</intent-filter>
</activity>
<service
android:name=".VPNService"
android:process=":QtOnlyProcess"
android:name=".AmneziaVpnService"
android:process=":amneziaVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="connectedDevice"
android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
android:foregroundServiceType="specialUse"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="vpn" />
</service>
<service
android:name=".qt.VPNPermissionHelper"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="connectedDevice"
android:exported="true">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
</service>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.amnezia.vpn.fileprovider"
android:authorities="org.amnezia.vpn.qtprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/fileprovider"/>
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/qtprovider_paths" />
</provider>
</application>
</manifest>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,17 +0,0 @@
[proxy_all]
[bypass_list]
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/29
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/3

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.awg"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":wireguard"))
}

View file

@ -0,0 +1,80 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.wireguard.Wireguard
import org.json.JSONObject
/**
* Config example:
* {
* "protocol": "awg",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "awg_config_data": {
* "H1": "969537490",
* "H2": "481688153",
* "H3": "2049399200",
* "H4": "52029755",
* "Jc": "3",
* "Jmax": "1000",
* "Jmin": "50",
* "S1": "49",
* "S2": "60",
* "client_ip": "10.8.1.1",
* "hostName": "100.100.100.0",
* "port": 12345,
* "client_pub_key": "clientPublicKeyBase64",
* "client_priv_key": "privateKeyBase64",
* "psk_key": "presharedKeyBase64",
* "server_pub_key": "publicKeyBase64",
* "config": "[Interface]
* Address = 10.8.1.1/32
* DNS = 1.1.1.1, 1.0.0.1
* PrivateKey = privateKeyBase64
* Jc = 3
* Jmin = 50
* Jmax = 1000
* S1 = 49
* S2 = 60
* H1 = 969537490
* H2 = 481688153
* H3 = 2049399200
* H4 = 52029755
*
* [Peer]
* PublicKey = publicKeyBase64
* PresharedKey = presharedKeyBase64
* AllowedIPs = 0.0.0.0/0, ::/0
* Endpoint = 100.100.100.0:12345
* PersistentKeepalive = 25
* "
* }
* }
*/
class Awg : Wireguard() {
override val ifName: String = "awg0"
override fun parseConfig(config: JSONObject): AwgConfig {
val configDataJson = config.getJSONObject("awg_config_data")
val configData = parseConfigData(configDataJson.getString("config"))
return AwgConfig.build {
configWireguard(configData)
configSplitTunneling(config)
configData["Jc"]?.let { setJc(it.toInt()) }
configData["Jmin"]?.let { setJmin(it.toInt()) }
configData["Jmax"]?.let { setJmax(it.toInt()) }
configData["S1"]?.let { setS1(it.toInt()) }
configData["S2"]?.let { setS2(it.toInt()) }
configData["H1"]?.let { setH1(it.toLong()) }
configData["H2"]?.let { setH2(it.toLong()) }
configData["H3"]?.let { setH3(it.toLong()) }
configData["H4"]?.let { setH4(it.toLong()) }
}
}
}

View file

@ -0,0 +1,108 @@
package org.amnezia.vpn.protocol.awg
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.wireguard.WireguardConfig
class AwgConfig private constructor(
wireguardConfigBuilder: WireguardConfig.Builder,
val jc: Int,
val jmin: Int,
val jmax: Int,
val s1: Int,
val s2: Int,
val h1: Long,
val h2: Long,
val h3: Long,
val h4: Long
) : WireguardConfig(wireguardConfigBuilder) {
private constructor(builder: Builder) : this(
builder,
builder.jc,
builder.jmin,
builder.jmax,
builder.s1,
builder.s2,
builder.h1,
builder.h2,
builder.h3,
builder.h4
)
override fun appendDeviceLine(sb: StringBuilder) = with(sb) {
super.appendDeviceLine(this)
appendLine("jc=$jc")
appendLine("jmin=$jmin")
appendLine("jmax=$jmax")
appendLine("s1=$s1")
appendLine("s2=$s2")
appendLine("h1=$h1")
appendLine("h2=$h2")
appendLine("h3=$h3")
appendLine("h4=$h4")
}
class Builder : WireguardConfig.Builder() {
private var _jc: Int? = null
internal var jc: Int
get() = _jc ?: throw BadConfigException("AWG: parameter jc is undefined")
private set(value) { _jc = value }
private var _jmin: Int? = null
internal var jmin: Int
get() = _jmin ?: throw BadConfigException("AWG: parameter jmin is undefined")
private set(value) { _jmin = value }
private var _jmax: Int? = null
internal var jmax: Int
get() = _jmax ?: throw BadConfigException("AWG: parameter jmax is undefined")
private set(value) { _jmax = value }
private var _s1: Int? = null
internal var s1: Int
get() = _s1 ?: throw BadConfigException("AWG: parameter s1 is undefined")
private set(value) { _s1 = value }
private var _s2: Int? = null
internal var s2: Int
get() = _s2 ?: throw BadConfigException("AWG: parameter s2 is undefined")
private set(value) { _s2 = value }
private var _h1: Long? = null
internal var h1: Long
get() = _h1 ?: throw BadConfigException("AWG: parameter h1 is undefined")
private set(value) { _h1 = value }
private var _h2: Long? = null
internal var h2: Long
get() = _h2 ?: throw BadConfigException("AWG: parameter h2 is undefined")
private set(value) { _h2 = value }
private var _h3: Long? = null
internal var h3: Long
get() = _h3 ?: throw BadConfigException("AWG: parameter h3 is undefined")
private set(value) { _h3 = value }
private var _h4: Long? = null
internal var h4: Long
get() = _h4 ?: throw BadConfigException("AWG: parameter h4 is undefined")
private set(value) { _h4 = value }
fun setJc(jc: Int) = apply { this.jc = jc }
fun setJmin(jmin: Int) = apply { this.jmin = jmin }
fun setJmax(jmax: Int) = apply { this.jmax = jmax }
fun setS1(s1: Int) = apply { this.s1 = s1 }
fun setS2(s2: Int) = apply { this.s2 = s2 }
fun setH1(h1: Long) = apply { this.h1 = h1 }
fun setH2(h2: Long) = apply { this.h2 = h2 }
fun setH3(h3: Long) = apply { this.h3 = h3 }
fun setH4(h4: Long) = apply { this.h4 = h4 }
override fun build(): AwgConfig = AwgConfig(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): AwgConfig = Builder().apply(block).build()
}
}

View file

@ -1,153 +1,5 @@
apply plugin: 'com.github.ben-manes.versions'
buildscript {
ext{
kotlin_version = "1.7.22"
// for libwg
appcompatVersion = '1.1.0'
annotationsVersion = '1.0.1'
databindingVersion = '3.3.1'
jsr305Version = '3.0.2'
streamsupportVersion = '1.7.0'
threetenabpVersion = '1.1.1'
groupName = 'org.amnezia.vpn'
minSdkVer = '24'
cmakeMinVersion = "3.25.0+"
}
repositories {
google()
jcenter()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation group: 'org.json', name: 'json', version: '20220924'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation project(path: ':shadowsocks')
// CameraX core library using the camera2 implementation
def camerax_version = "1.2.1"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("androidx.camera:camera-extensions:${camerax_version}")
def camerax_ml_version = "1.2.0-beta02"
def ml_kit_version = "17.0.3"
implementation("androidx.camera:camera-mlkit-vision:${camerax_ml_version}")
implementation("com.google.mlkit:barcode-scanning:${ml_kit_version}")
}
androidExtensions {
experimental = true
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
// Extract native libraries from the APK
packagingOptions.jniLibs.useLegacyPackaging true
dexOptions {
javaMaxHeapSize "3g"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qtAndroidDir + '/res', 'res']
resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
androidTest.assets.srcDirs += files("${qtAndroidDir}/schemas".toString())
}
}
tasks.withType(JavaCompile) {
options.incremental = true
}
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
lintOptions {
abortOnError false
}
// Do not compress Qt binary resources file
aaptOptions {
noCompress 'rcc'
}
defaultConfig {
resConfig "en"
minSdkVersion = 24
targetSdkVersion = 34
versionCode 39 // Change to a higher number
versionName "4.1.0" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
]
}
}
// dummy file for androiddeployqt
// android.bundle.enableUncompressedNativeLibs is deprecated
// disable adding gradle property android.bundle.enableUncompressedNativeLibs by androiddeployqt
useLegacyPackaging

View file

@ -0,0 +1,115 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("property-delegate")
}
kotlin {
jvmToolchain(17)
}
// get values from gradle or local properties
val qtTargetSdkVersion: String by gradleProperties
val qtTargetAbiList: String by gradleProperties
val outputBaseName: String by gradleProperties
android {
namespace = "org.amnezia.vpn"
buildFeatures {
viewBinding = true
}
androidResources {
// don't compress Qt binary resources file
noCompress += "rcc"
}
packaging {
// compress .so binary libraries
jniLibs.useLegacyPackaging = true
}
defaultConfig {
applicationId = "org.amnezia.vpn"
targetSdk = qtTargetSdkVersion.toInt()
// keeps language resources for only the locales specified below
resourceConfigurations += listOf("en", "ru", "b+zh+Hans")
}
sourceSets {
getByName("main") {
manifest.srcFile("AndroidManifest.xml")
java.setSrcDirs(listOf("src"))
res.setSrcDirs(listOf("res"))
// androyddeployqt creates the folders below
assets.setSrcDirs(listOf("assets"))
jniLibs.setSrcDirs(listOf("libs"))
}
}
signingConfigs {
register("release") {
storeFile = providers.environmentVariable("ANDROID_KEYSTORE_PATH").orNull?.let { file(it) }
storePassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
keyAlias = providers.environmentVariable("ANDROID_KEYSTORE_KEY_ALIAS").orNull
keyPassword = providers.environmentVariable("ANDROID_KEYSTORE_KEY_PASS").orNull
}
}
buildTypes {
release {
// exclude coroutine debug resource from release build
packaging {
resources.excludes += "DebugProbesKt.bin"
}
signingConfig = signingConfigs["release"]
}
}
splits {
abi {
isEnable = true
reset()
include(*qtTargetAbiList.split(',').toTypedArray())
isUniversalApk = false
}
}
// fix for Qt Creator to allow deploying the application to a device
// to enable this fix, add the line outputBaseName=android-build to local.properties
if (outputBaseName.isNotEmpty()) {
applicationVariants.all {
outputs.map { it as BaseVariantOutputImpl }
.forEach { output ->
if (output.outputFileName.endsWith(".apk")) {
output.outputFileName = "$outputBaseName-${buildType.name}.apk"
}
}
}
}
lint {
disable += "InvalidFragmentVersionForActivityResult"
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
implementation(project(":qt"))
implementation(project(":utils"))
implementation(project(":protocolApi"))
implementation(project(":wireguard"))
implementation(project(":awg"))
implementation(project(":openvpn"))
implementation(project(":cloak"))
implementation(libs.androidx.core)
implementation(libs.androidx.activity)
implementation(libs.androidx.security.crypto)
implementation(libs.kotlinx.coroutines)
implementation(libs.bundles.androidx.camera)
implementation(libs.google.mlkit)
}

View file

@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.cloak"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(project(":openvpn"))
}

View file

@ -0,0 +1,69 @@
package org.amnezia.vpn.protocol.cloak
import android.util.Base64
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.openvpn.OpenVpn
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "cloak",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* "cloak_config_data": {
* "BrowserSig": "chrome",
* "EncryptionMethod": "aes-gcm",
* "NumConn": 1,
* "ProxyMethod": "openvpn",
* "PublicKey": "PublicKey=",
* "RemoteHost": "100.100.100.0",
* "RemotePort": "443",
* "ServerName": "servername",
* "StreamTimeout": 300,
* "Transport": "direct",
* "UID": "UID="
* }
* }
*/
class Cloak : OpenVpn() {
override fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
val openVpnConfigStr = config.getJSONObject("openvpn_config_data").getString("config")
val cloakConfigJson = checkCloakJson(config.getJSONObject("cloak_config_data"))
val cloakConfigStr = Base64.encodeToString(cloakConfigJson.toString().toByteArray(), Base64.DEFAULT)
val configStr = "$openVpnConfigStr\n<cloak>\n$cloakConfigStr\n</cloak>\n"
openVpnConfig.usePluggableTransports = true
openVpnConfig.content = configStr
return openVpnConfig
}
private fun checkCloakJson(cloakConfigJson: JSONObject): JSONObject {
cloakConfigJson.put("NumConn", 1)
cloakConfigJson.put("ProxyMethod", "openvpn")
if (cloakConfigJson.has("port")) {
val port = cloakConfigJson["port"]
cloakConfigJson.remove("port")
cloakConfigJson.put("RemotePort", port)
}
if (cloakConfigJson.has("remote")) {
val remote = cloakConfigJson["remote"]
cloakConfigJson.remove("remote")
cloakConfigJson.put("RemoteHost", remote)
}
return cloakConfigJson
}
}

View file

@ -1,27 +1,46 @@
# Project-wide Gradle settings.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# Gradle caching allows reusing the build artifacts from a previous
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true
# Specifies the JVM arguments used for the daemon process
org.gradle.jvmargs=-Xms512m -Xmx4g -XX:+UseParallelGC -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError \
-Dfile.encoding=UTF-8
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configureondemand=true
# Use AndroidX library instead of a Support Library
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Disable adding android:testOnly attribute to the manifest
android.injected.testOnly=false
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
android.bundle.enableUncompressedNativeLibs=false
androidBuildToolsVersion=30.0.2
androidCompileSdkVersion=30
org.gradle.caching=true
org.gradle.parallel=true
android.enableJetifier=true
android.injected.testOnly=false
kapt.use.worker.api=false
kapt.incremental.apt=false
# Disable providing custom values to resources from buildscript by default
android.defaults.buildfeatures.resvalues=false
# Disable compileShaders tasks by default
android.defaults.buildfeatures.shaders=false
# Disable Android resource processing for libraries by default
android.library.defaults.buildfeatures.androidresources=false
# Qt variables
# At build time androiddeployqt replaces these values with:
# androidCompileSdkVersion - androiddeployqt --android-platform parameter
# androidBuildToolsVersion - QT_ANDROID_SDK_BUILD_TOOLS_REVISION cmake target parameter
# qtMinSdkVersion - QT_ANDROID_MIN_SDK_VERSION cmake target parameter
# qtTargetSdkVersion - QT_ANDROID_TARGET_SDK_VERSION cmake target parameter
# androidNdkVersion - version from ANDROID_NDK_ROOT environment variable
# qtTargetAbiList - qt-cmake QT_ANDROID_ABIS parameter
# qtAndroidDir - path to qt binding java source code
# buildDir - hardcoded "build" value in androiddeployqt
# For development copy and set local values for these parameters in local.properties
#androidCompileSdkVersion=android-34
#androidBuildToolsVersion=34.0.0
#qtMinSdkVersion=24
#qtTargetSdkVersion=34
#androidNdkVersion=26.1.10909125
#qtTargetAbiList=x86_64
#qtAndroidDir=/QT_BASE/android_ABI/src/android/java
#buildDir=build
# Note about qtAndroidDir:
# Some IDEs (for example, IntelliJ IDEA) may index all data from a common root of the project and qtAndroidDir.
# Therefore, it's recommended to copy qt android files to a directory inside the project
# and specify the path to that directory in qtAndroidDir.

View file

@ -0,0 +1,35 @@
[versions]
agp = "8.2.0"
kotlin = "1.9.20"
androidx-core = "1.12.0"
androidx-activity = "1.8.1"
androidx-annotation = "1.7.0"
androidx-camera = "1.3.0"
androidx-security-crypto = "1.1.0-alpha06"
kotlinx-coroutines = "1.7.3"
google-mlkit = "17.2.0"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidx-camera" }
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidx-camera" }
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidx-camera" }
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "androidx-camera" }
androidx-security-crypto = { module = "androidx.security:security-crypto-ktx", version.ref = "androidx-security-crypto" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
google-mlkit = { module = "com.google.mlkit:barcode-scanning", version.ref = "google-mlkit" }
[bundles]
androidx-camera = [
"androidx-camera-core",
"androidx-camera-lifecycle",
"androidx-camera-view",
"androidx-camera-camera2"
]
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View file

@ -0,0 +1,25 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
kotlin {
jvmToolchain(17)
}
gradlePlugin {
plugins {
register("settingsGradlePropertyDelegate") {
id = "settings-property-delegate"
implementationClass = "SettingsPropertyDelegate"
}
register("projectGradlePropertyDelegate") {
id = "property-delegate"
implementationClass = "ProjectPropertyDelegate"
}
}
}

View file

@ -0,0 +1,49 @@
import java.io.File
import java.io.FileInputStream
import java.io.InputStreamReader
import java.util.Properties
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.initialization.Settings
import org.gradle.api.provider.ProviderFactory
private fun localProperties(rootDir: File) = Properties().apply {
val localProperties = File(rootDir, "local.properties")
if (localProperties.isFile) {
InputStreamReader(FileInputStream(localProperties), Charsets.UTF_8).use {
load(it)
}
}
}
private class PropertyDelegate(
rootDir: File,
private val providers: ProviderFactory,
private val localProperties: Properties = localProperties(rootDir)
) : ReadOnlyProperty<Any?, String> {
override fun getValue(thisRef: Any?, property: KProperty<*>): String =
providers.gradleProperty(property.name).orNull ?: localProperties.getProperty(property.name).orEmpty()
}
private lateinit var settingsPropertyDelegate: ReadOnlyProperty<Any?, String>
private lateinit var projectPropertyDelegate: ReadOnlyProperty<Any?, String>
class SettingsPropertyDelegate : Plugin<Settings> {
override fun apply(settings: Settings) {
settingsPropertyDelegate = PropertyDelegate(settings.rootDir, settings.providers)
}
}
class ProjectPropertyDelegate : Plugin<Project> {
override fun apply(project: Project) {
projectPropertyDelegate = PropertyDelegate(project.rootDir, project.providers)
}
}
val Settings.gradleProperties: ReadOnlyProperty<Any?, String>
get() = settingsPropertyDelegate
val Project.gradleProperties: ReadOnlyProperty<Any?, String>
get() = projectPropertyDelegate

Binary file not shown.

View file

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

309
client/android/gradlew vendored
View file

@ -1,78 +1,127 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -81,92 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View file

@ -1,4 +1,20 @@
@if "%DEBUG%" == "" @echo off
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -9,19 +25,23 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -35,7 +55,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,38 +65,26 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

View file

@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol.openvpn"
}
dependencies {
compileOnly(project(":utils"))
compileOnly(project(":protocolApi"))
implementation(libs.kotlinx.coroutines)
}

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_Config {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Config obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_ConnectionInfo {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ConnectionInfo obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_DynamicChallenge {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_DynamicChallenge obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_EvalConfig {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_EvalConfig obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_Event {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Event obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKIBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKICertRequest extends ClientAPI_ExternalPKIReque
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKICertRequest obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_ExternalPKIRequestBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKIRequestBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -20,6 +20,18 @@ public class ClientAPI_ExternalPKISignRequest extends ClientAPI_ExternalPKIReque
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ExternalPKISignRequest obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_InterfaceStats {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_InterfaceStats obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_KeyValue {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_KeyValue obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_LLVector extends java.util.AbstractList<Long> implements
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_LLVector obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_LogInfo {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_LogInfo obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_MergeConfig {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_MergeConfig obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -20,6 +20,18 @@ public class ClientAPI_OpenVPNClient extends ClientAPI_TunBuilderBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_OpenVPNClient obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_OpenVPNClientHelper {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_OpenVPNClientHelper obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_ProvideCreds {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ProvideCreds obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_RemoteOverride {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_RemoteOverride obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_ServerEntry {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ServerEntry obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_ServerEntryVector extends java.util.AbstractList<ClientAP
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_ServerEntryVector obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_SessionToken {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_SessionToken obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_Status {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_Status obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_StringVec extends java.util.AbstractList<String> implemen
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_StringVec obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_TransportStats {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_TransportStats obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -21,6 +21,18 @@ public class ClientAPI_TunBuilderBase {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(ClientAPI_TunBuilderBase obj) {
long ptr = 0;
if (obj != null) {
if (!obj.swigCMemOwn)
throw new RuntimeException("Cannot release ownership as memory is not owned");
ptr = obj.swigCPtr;
obj.swigCMemOwn = false;
obj.delete();
}
return ptr;
}
@SuppressWarnings("deprecation")
protected void finalize() {
delete();

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__string {
protected static long getCPtr(SWIGTYPE_p_std__string obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(SWIGTYPE_p_std__string obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
@ -22,5 +22,9 @@ public class SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t {
protected static long getCPtr(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected static long swigRelease(SWIGTYPE_p_std__vectorT_openvpn__ClientAPI__KeyValue_t obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */

View file

@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.1
* This file was automatically generated by SWIG (https://www.swig.org).
* Version 4.1.1
*
* Do not make changes to this file unless you know what you are doing--modify
* Do not make changes to this file unless you know what you are doing - modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */

View file

@ -0,0 +1,136 @@
package org.amnezia.vpn.protocol.openvpn
import android.content.Context
import android.net.VpnService.Builder
import android.os.Build
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import net.openvpn.ovpn3.ClientAPI_Config
import org.amnezia.vpn.protocol.BadConfigException
import org.amnezia.vpn.protocol.Protocol
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.Statistics
import org.amnezia.vpn.protocol.VpnStartException
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.getLocalNetworks
import org.json.JSONObject
/**
* Config Example:
* {
* "protocol": "openvpn",
* "description": "Server 1",
* "dns1": "1.1.1.1",
* "dns2": "1.0.0.1",
* "hostName": "100.100.100.0",
* "splitTunnelSites": [
* ],
* "splitTunnelType": 0,
* "openvpn_config_data": {
* "config": "openVpnConfig"
* }
* }
*/
open class OpenVpn : Protocol() {
private lateinit var context: Context
private var openVpnClient: OpenVpnClient? = null
private lateinit var scope: CoroutineScope
override val statistics: Statistics
get() {
openVpnClient?.let { client ->
val stats = client.transport_stats()
return Statistics.build {
setRxBytes(stats.bytesIn)
setTxBytes(stats.bytesOut)
}
}
return Statistics.EMPTY_STATISTICS
}
override fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
super.initialize(context, state, onError)
loadSharedLibrary(context, "ovpn3")
this.context = context
scope = CoroutineScope(Dispatchers.IO)
}
override fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) {
val configBuilder = OpenVpnConfig.Builder()
openVpnClient = OpenVpnClient(
configBuilder = configBuilder,
state = state,
getLocalNetworks = { ipv6 -> getLocalNetworks(context, ipv6) },
establish = makeEstablish(vpnBuilder),
protect = protect,
onError = onError
)
try {
openVpnClient?.let { client ->
val openVpnConfig = parseConfig(config)
val evalConfig = client.eval_config(openVpnConfig)
if (evalConfig.error) {
throw BadConfigException("OpenVPN config parse error: ${evalConfig.message}")
}
configBuilder.apply {
// fix for split tunneling
// The exclude split tunneling OpenVpn configuration does not contain a default route.
// It is required for split tunneling in newer versions of Android.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
addRoute(InetNetwork("0.0.0.0", 0))
addRoute(InetNetwork("::", 0))
}
configSplitTunneling(config)
}
scope.launch {
val status = client.connect()
if (status.error) {
state.value = DISCONNECTED
onError("OpenVpn connect() error: ${status.status}: ${status.message}")
}
}
}
} catch (e: Exception) {
openVpnClient = null
throw e
}
}
override fun stopVpn() {
openVpnClient?.stop()
openVpnClient = null
}
override fun reconnectVpn(vpnBuilder: Builder) {
openVpnClient?.let {
it.establish = makeEstablish(vpnBuilder)
it.reconnect(0)
}
}
protected open fun parseConfig(config: JSONObject): ClientAPI_Config {
val openVpnConfig = ClientAPI_Config()
openVpnConfig.content = config.getJSONObject("openvpn_config_data").getString("config")
return openVpnConfig
}
private fun makeEstablish(vpnBuilder: Builder): (OpenVpnConfig.Builder) -> Int = { configBuilder ->
val openVpnConfig = configBuilder.build()
buildVpnInterface(openVpnConfig, vpnBuilder)
vpnBuilder.establish().use { tunFd ->
if (tunFd == null) {
throw VpnStartException("Create VPN interface: permission not granted or revoked")
}
return@use tunFd.detachFd()
}
}
}

View file

@ -0,0 +1,428 @@
package org.amnezia.vpn.protocol.openvpn
import android.net.ProxyInfo
import android.os.Build
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import net.openvpn.ovpn3.ClientAPI_Config
import net.openvpn.ovpn3.ClientAPI_EvalConfig
import net.openvpn.ovpn3.ClientAPI_Event
import net.openvpn.ovpn3.ClientAPI_LogInfo
import net.openvpn.ovpn3.ClientAPI_OpenVPNClient
import net.openvpn.ovpn3.ClientAPI_Status
import net.openvpn.ovpn3.ClientAPI_StringVec
import net.openvpn.ovpn3.ClientAPI_TransportStats
import org.amnezia.vpn.protocol.ProtocolState
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
import org.amnezia.vpn.protocol.ProtocolState.RECONNECTING
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.parseInetAddress
private const val TAG = "OpenVpnClient"
private const val EMULATED_EXCLUDE_ROUTES = (1 shl 16)
class OpenVpnClient(
private val configBuilder: OpenVpnConfig.Builder,
private val state: MutableStateFlow<ProtocolState>,
private val getLocalNetworks: (Boolean) -> List<InetNetwork>,
internal var establish: (OpenVpnConfig.Builder) -> Int,
private val protect: (Int) -> Boolean,
private val onError: (String) -> Unit
) : ClientAPI_OpenVPNClient() {
/**************************************************************************
* Tun builder callbacks
**************************************************************************/
// Tun builder methods, loosely based on the Android VpnService.Builder
// abstraction. These methods comprise an abstraction layer that
// allows the OpenVPN C++ core to call out to external methods for
// establishing the tunnel, adding routes, etc.
// All methods returning bool use the return
// value to indicate success (true) or fail (false).
// tun_builder_new() should be called first, then arbitrary setter methods,
// and finally tun_builder_establish to return the socket descriptor
// for the session. IP addresses are pre-validated before being passed to
// these methods.
// This interface is based on Android's VpnService.Builder.
// Callback to construct a new tun builder
// Should be called first.
override fun tun_builder_new(): Boolean {
Log.v(TAG, "tun_builder_new")
configBuilder.clearAddresses()
return true
}
// Callback to set MTU of the VPN interface
// Never called more than once per tun_builder session.
override fun tun_builder_set_mtu(mtu: Int): Boolean {
Log.v(TAG, "tun_builder_set_mtu: $mtu")
configBuilder.setMtu(mtu)
return true
}
// Callback to add network address to VPN interface
// May be called more than once per tun_builder session
override fun tun_builder_add_address(
address: String, prefix_length: Int,
gateway: String, ipv6: Boolean, net30: Boolean
): Boolean {
Log.v(TAG, "tun_builder_add_address: $address, $prefix_length, $gateway, $ipv6, $net30")
configBuilder.addAddress(InetNetwork(address, prefix_length))
return true
}
// Callback to add route to VPN interface
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_add_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_add_route: $address, $prefix_length, $metric, $ipv6")
if (address == "remote_host") return false
configBuilder.addRoute(InetNetwork(address, prefix_length))
return true
}
// Callback to exclude route from VPN interface
// May be called more than once per tun_builder session
// metric is optional and should be ignored if < 0
override fun tun_builder_exclude_route(address: String, prefix_length: Int, metric: Int, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_exclude_route: $address, $prefix_length, $metric, $ipv6")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
configBuilder.excludeRoute(InetNetwork(address, prefix_length))
}
return true
}
// Callback to add DNS server to VPN interface
// May be called more than once per tun_builder session
// If reroute_dns is true, all DNS traffic should be routed over the
// tunnel, while if false, only DNS traffic that matches an added search
// domain should be routed.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_dns_server(address: String, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_add_dns_server: $address, $ipv6")
configBuilder.addDnsServer(parseInetAddress(address))
return true
}
// Optional callback that indicates whether traffic of a certain
// address family (AF_INET or AF_INET6) should be
// blocked or allowed, to prevent unencrypted packet leakage when
// the tunnel is IPv4-only/IPv6-only, but the local machine
// has connectivity with the other protocol to the internet.
// Controlled by "block-ipv6" and block-ipv6 config var.
// If addresses are added for a family this setting should be
// ignored for that family
// See also Android's VPNService.Builder.allowFamily method
/* override fun tun_builder_set_allow_family(af: Int, allow: Boolean): Boolean {
Log.v(TAG, "tun_builder_set_allow_family: $af, $allow")
return true
} */
// Callback to set address of remote server
// Never called more than once per tun_builder session.
override fun tun_builder_set_remote_address(address: String, ipv6: Boolean): Boolean {
Log.v(TAG, "tun_builder_set_remote_address: $address, $ipv6")
return true
}
// Optional callback that indicates OSI layer, should be 2 or 3.
// Defaults to 3.
override fun tun_builder_set_layer(layer: Int): Boolean {
Log.v(TAG, "tun_builder_set_layer: $layer")
return layer == 3
}
// Callback to set the session name
// Never called more than once per tun_builder session.
override fun tun_builder_set_session_name(name: String): Boolean {
Log.v(TAG, "tun_builder_set_session_name: $name")
return true
}
// Callback to establish the VPN tunnel, returning a file descriptor
// to the tunnel, which the caller will henceforth own. Returns -1
// if the tunnel could not be established.
// Always called last after tun_builder session has been configured.
override fun tun_builder_establish(): Int {
Log.v(TAG, "tun_builder_establish")
return establish(configBuilder)
}
// Callback to reroute default gateway to VPN interface.
// ipv4 is true if the default route to be added should be IPv4.
// ipv6 is true if the default route to be added should be IPv6.
// flags are defined in RGWFlags (rgwflags.hpp).
// Never called more than once per tun_builder session.
override fun tun_builder_reroute_gw(ipv4: Boolean, ipv6: Boolean, flags: Long): Boolean {
Log.v(TAG, "tun_builder_reroute_gw: $ipv4, $ipv6, $flags")
if ((flags and EMULATED_EXCLUDE_ROUTES.toLong()) != 0L) return true
if (ipv4) {
configBuilder.addRoute(InetNetwork("0.0.0.0", 0))
}
if (ipv6) {
configBuilder.addRoute(InetNetwork("::", 0))
}
return true
}
// Callback to add search domain to DNS resolver
// May be called more than once per tun_builder session
// See tun_builder_add_dns_server above for description of
// reroute_dns parameter.
// Guaranteed to be called after tun_builder_reroute_gw.
override fun tun_builder_add_search_domain(domain: String): Boolean {
Log.v(TAG, "tun_builder_add_search_domain: $domain")
configBuilder.setSearchDomain(domain)
return true
}
// Callback to set the HTTP proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
Log.v(TAG, "tun_builder_set_proxy_http: $host, $port")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
configBuilder.setHttpProxy(ProxyInfo.buildDirectProxy(host, port))
} catch (e: Exception) {
Log.e(TAG, "Could not set proxy: ${e.message}")
return false
}
}
return true
}
// Callback to set the HTTPS proxy
// Never called more than once per tun_builder session.
override fun tun_builder_set_proxy_https(host: String, port: Int): Boolean {
Log.v(TAG, "tun_builder_set_proxy_https: $host, $port")
return false
}
// When the exclude local network option is enabled this
// function is called to get a list of local networks so routes
// to exclude them from the VPN network are generated
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
override fun tun_builder_get_local_networks(ipv6: Boolean): ClientAPI_StringVec {
Log.v(TAG, "tun_builder_get_local_networks: $ipv6")
val networks = ClientAPI_StringVec()
for (address in getLocalNetworks(ipv6)) {
networks.add(address.toString())
}
return networks
}
// Optional callback to set default value for route metric.
// Guaranteed to be called before other methods that deal
// with routes such as tun_builder_add_route and
// tun_builder_reroute_gw. Route metric is ignored
// if < 0.
/* override fun tun_builder_set_route_metric_default(metric: Int): Boolean {
Log.v(TAG, "tun_builder_set_route_metric_default: $metric")
return super.tun_builder_set_route_metric_default(metric)
} */
// Callback to add a host which should bypass the proxy
// May be called more than once per tun_builder session
/* override fun tun_builder_add_proxy_bypass(bypass_host: String): Boolean {
Log.v(TAG, "tun_builder_add_proxy_bypass: $bypass_host")
return super.tun_builder_add_proxy_bypass(bypass_host)
} */
// Callback to set the proxy "Auto Config URL"
// Never called more than once per tun_builder session.
/* override fun tun_builder_set_proxy_auto_config_url(url: String): Boolean {
Log.v(TAG, "tun_builder_set_proxy_auto_config_url: $url")
return super.tun_builder_set_proxy_auto_config_url(url)
} */
// Callback to add Windows WINS server to VPN interface.
// WINS server addresses are always IPv4.
// May be called more than once per tun_builder session.
// Guaranteed to be called after tun_builder_reroute_gw.
/* override fun tun_builder_add_wins_server(address: String): Boolean {
Log.v(TAG, "tun_builder_add_wins_server: $address")
return super.tun_builder_add_wins_server(address)
} */
// Optional callback to set a DNS suffix on tun/tap adapter.
// Currently only implemented on Windows, where it will
// set the "Connection-specific DNS Suffix" property on
// the TAP driver.
/* override fun tun_builder_set_adapter_domain_suffix(name: String): Boolean {
Log.v(TAG, "tun_builder_set_adapter_domain_suffix: $name")
return super.tun_builder_set_adapter_domain_suffix(name)
} */
// Return true if tun interface may be persisted, i.e. rolled
// into a new session with properties untouched. This method
// is only called after all other tests of persistence
// allowability succeed, therefore it can veto persistence.
// If persistence is ultimately enabled,
// tun_builder_establish_lite() will be called. Otherwise,
// tun_builder_establish() will be called.
/* override fun tun_builder_persist(): Boolean {
Log.v(TAG, "tun_builder_persist")
return super.tun_builder_persist()
} */
// Indicates a reconnection with persisted tun state.
/* override fun tun_builder_establish_lite() {
Log.v(TAG, "tun_builder_establish_lite")
super.tun_builder_establish_lite()
} */
// Indicates that tunnel is being torn down.
// If disconnect == true, then the teardown is occurring
// prior to final disconnect.
/* override fun tun_builder_teardown(disconnect: Boolean) {
Log.v(TAG, "tun_builder_teardown: $disconnect")
super.tun_builder_teardown(disconnect)
} */
/**************************************************************************
* Connection control methods
**************************************************************************/
// Parse OpenVPN configuration file.
override fun eval_config(arg0: ClientAPI_Config): ClientAPI_EvalConfig {
Log.v(TAG, "eval_config")
return super.eval_config(arg0)
}
// Primary VPN client connect method, doesn't return until disconnect.
// Should be called by a worker thread. This method will make callbacks
// to event() and log() functions. Make sure to call eval_config()
// and possibly provide_creds() as well before this function.
override fun connect(): ClientAPI_Status {
Log.v(TAG, "connect")
return super.connect()
}
// Callback to "protect" a socket from being routed through the tunnel.
// Will be called from the thread executing connect().
// The remote and ipv6 are the remote host this socket will connect to
override fun socket_protect(socket: Int, remote: String, ipv6: Boolean): Boolean {
Log.v(TAG, "socket_protect: $socket, $remote, $ipv6")
return protect(socket)
}
// Stop the client. Only meaningful when connect() is running.
// May be called asynchronously from a different thread
// when connect() is running.
override fun stop() {
Log.v(TAG, "stop")
super.stop()
}
// Pause the client -- useful to avoid continuous reconnection attempts
// when network is down. May be called from a different thread
// when connect() is running.
override fun pause(reason: String) {
Log.v(TAG, "pause: $reason")
super.pause(reason)
}
// Resume the client after it has been paused. May be called from a
// different thread when connect() is running.
override fun resume() {
Log.v(TAG, "resume")
super.resume()
}
// Do a disconnect/reconnect cycle n seconds from now. May be called
// from a different thread when connect() is running.
override fun reconnect(seconds: Int) {
Log.v(TAG, "reconnect")
super.reconnect(seconds)
}
// When a connection is close to timeout, the core will call this
// method. If it returns false, the core will disconnect with a
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
// state.
override fun pause_on_connection_timeout(): Boolean {
Log.v(TAG, "pause_on_connection_timeout")
return false
}
// Return information about the most recent connection. Should be called
// after an event of type "CONNECTED".
/* override fun connection_info(): ClientAPI_ConnectionInfo {
Log.v(TAG, "connection_info")
return super.connection_info()
} */
/**************************************************************************
* Status callbacks
**************************************************************************/
// Callback for delivering events during connect() call.
// Will be called from the thread executing connect().
override fun event(event: ClientAPI_Event) {
val name = event.name
val info = event.info
Log.v(TAG, "OpenVpn event: $name: $info")
when (name) {
"COMPRESSION_ENABLED", "WARN" -> Log.w(TAG, "$name: $info")
"CONNECTED" -> state.value = CONNECTED
"DISCONNECTED" -> state.value = DISCONNECTED
"RECONNECTING" -> {
state.getAndUpdate { state ->
if (state == DISCONNECTED || state == CONNECTED) RECONNECTING
else state
}
}
}
if (event.error || event.fatal) {
state.value = DISCONNECTED
onError("OpenVpn ${if (event.error) "ERROR" else "FATAL"}: $name: $info")
}
}
// Callback for logging.
// Will be called from the thread executing connect().
override fun log(arg0: ClientAPI_LogInfo) {
arg0.text.dropLastWhile { it == '\n' }.let {
Log.d(TAG, "OpenVpnLog: $it")
}
}
/**************************************************************************
* Stats methods
**************************************************************************/
// return transport stats only
override fun transport_stats(): ClientAPI_TransportStats {
Log.v(TAG, "transport_stats")
return super.transport_stats()
}
// return a stats value, index should be >= 0 and < stats_n()
/* override fun stats_value(index: Int): Long {
Log.v(TAG, "stats_value: $index")
return super.stats_value(index)
} */
// return all stats in a bundle
/* override fun stats_bundle(): ClientAPI_LLVector {
Log.v(TAG, "stats_bundle")
return super.stats_bundle()
} */
// return tun stats only
/* override fun tun_stats(): ClientAPI_InterfaceStats {
Log.v(TAG, "tun_stats")
return super.tun_stats()
} */
// post control channel message
/* override fun post_cc_msg(msg: String) {
Log.v(TAG, "post_cc_msg: $msg")
super.post_cc_msg(msg)
} */
}

View file

@ -0,0 +1,20 @@
package org.amnezia.vpn.protocol.openvpn
import org.amnezia.vpn.protocol.ProtocolConfig
private const val OPENVPN_DEFAULT_MTU = 1500
class OpenVpnConfig private constructor(
protocolConfigBuilder: ProtocolConfig.Builder
) : ProtocolConfig(protocolConfigBuilder) {
class Builder : ProtocolConfig.Builder(false) {
override var mtu: Int = OPENVPN_DEFAULT_MTU
override fun build(): OpenVpnConfig = OpenVpnConfig(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): OpenVpnConfig = Builder().apply(block).build()
}
}

View file

@ -0,0 +1,18 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.kotlin.android.get().pluginId)
}
kotlin {
jvmToolchain(17)
}
android {
namespace = "org.amnezia.vpn.protocol"
}
dependencies {
compileOnly(project(":utils"))
implementation(libs.androidx.annotation)
implementation(libs.kotlinx.coroutines)
}

View file

@ -0,0 +1,9 @@
package org.amnezia.vpn.protocol
sealed class ProtocolException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)
class LoadLibraryException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class BadConfigException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnStartException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)
class VpnException(message: String? = null, cause: Throwable? = null) : ProtocolException(message, cause)

View file

@ -0,0 +1,217 @@
package org.amnezia.vpn.protocol
import android.annotation.SuppressLint
import android.content.Context
import android.net.IpPrefix
import android.net.VpnService
import android.net.VpnService.Builder
import android.os.Build
import android.system.OsConstants
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipFile
import kotlinx.coroutines.flow.MutableStateFlow
import org.amnezia.vpn.util.Log
import org.amnezia.vpn.util.net.InetNetwork
import org.amnezia.vpn.util.net.IpRange
import org.amnezia.vpn.util.net.IpRangeSet
import org.json.JSONObject
private const val TAG = "Protocol"
const val VPN_SESSION_NAME = "AmneziaVPN"
private const val SPLIT_TUNNEL_DISABLE = 0
private const val SPLIT_TUNNEL_INCLUDE = 1
private const val SPLIT_TUNNEL_EXCLUDE = 2
abstract class Protocol {
abstract val statistics: Statistics
protected lateinit var state: MutableStateFlow<ProtocolState>
protected lateinit var onError: (String) -> Unit
open fun initialize(context: Context, state: MutableStateFlow<ProtocolState>, onError: (String) -> Unit) {
this.state = state
this.onError = onError
}
abstract fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean)
abstract fun stopVpn()
abstract fun reconnectVpn(vpnBuilder: Builder)
protected fun ProtocolConfig.Builder.configSplitTunneling(config: JSONObject) {
if (!allowSplitTunneling) {
Log.i(TAG, "Global address split tunneling is prohibited, " +
"only tunneling from the protocol config is used")
return
}
val splitTunnelType = config.optInt("splitTunnelType")
if (splitTunnelType == SPLIT_TUNNEL_DISABLE) return
val splitTunnelSites = config.getJSONArray("splitTunnelSites")
when (splitTunnelType) {
SPLIT_TUNNEL_INCLUDE -> {
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
// add routes from config
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
addRoute(address)
}
}
SPLIT_TUNNEL_EXCLUDE -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// exclude routes from config
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
excludeRoute(address)
}
} else {
// For older versions of Android, build a list of subnets without excluded addresses
val ipRangeSet = IpRangeSet()
ipRangeSet.remove(IpRange("127.0.0.0", 8))
for (i in 0 until splitTunnelSites.length()) {
val address = InetNetwork.parse(splitTunnelSites.getString(i))
ipRangeSet.remove(IpRange(address))
}
// remove default routes, if any
removeRoute(InetNetwork("0.0.0.0", 0))
removeRoute(InetNetwork("::", 0))
ipRangeSet.subnets().forEach(::addRoute)
addRoute(InetNetwork("2000::", 3))
}
}
}
}
protected open fun buildVpnInterface(config: ProtocolConfig, vpnBuilder: Builder) {
vpnBuilder.setSession(VPN_SESSION_NAME)
for (addr in config.addresses) {
Log.d(TAG, "addAddress: $addr")
vpnBuilder.addAddress(addr)
}
for (addr in config.dnsServers) {
Log.d(TAG, "addDnsServer: $addr")
vpnBuilder.addDnsServer(addr)
}
// fix for Samsung android ignoring DNS servers outside the VPN route range
if (Build.BRAND == "samsung") {
for (addr in config.dnsServers) {
Log.d(TAG, "addRoute: $addr")
vpnBuilder.addRoute(InetNetwork(addr))
}
}
config.searchDomain?.let {
Log.d(TAG, "addSearchDomain: $it")
vpnBuilder.addSearchDomain(it)
}
for (addr in config.routes) {
Log.d(TAG, "addRoute: $addr")
vpnBuilder.addRoute(addr)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
for (addr in config.excludedRoutes) {
Log.d(TAG, "excludeRoute: $addr")
vpnBuilder.excludeRoute(addr)
}
}
for (app in config.excludedApplications) {
Log.d(TAG, "addDisallowedApplication: $app")
vpnBuilder.addDisallowedApplication(app)
}
Log.d(TAG, "setMtu: ${config.mtu}")
vpnBuilder.setMtu(config.mtu)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
config.httpProxy?.let {
Log.d(TAG, "setHttpProxy: $it")
vpnBuilder.setHttpProxy(it)
}
}
if (config.allowAllAF) {
Log.d(TAG, "allowFamily")
vpnBuilder.allowFamily(OsConstants.AF_INET)
vpnBuilder.allowFamily(OsConstants.AF_INET6)
}
Log.d(TAG, "setBlocking: ${config.blockingMode}")
vpnBuilder.setBlocking(config.blockingMode)
vpnBuilder.setUnderlyingNetworks(null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
vpnBuilder.setMetered(false)
}
companion object {
private fun extractLibrary(context: Context, libraryName: String, destination: File): Boolean {
Log.d(TAG, "Extracting library: $libraryName")
val apks = hashSetOf<String>()
context.applicationInfo.run {
sourceDir?.let { apks += it }
splitSourceDirs?.let { apks += it }
}
for (abi in Build.SUPPORTED_ABIS) {
for (apk in apks) {
ZipFile(File(apk), ZipFile.OPEN_READ).use { zipFile ->
val mappedName = System.mapLibraryName(libraryName)
val libraryZipPath = listOf("lib", abi, mappedName).joinToString(File.separator)
val zipEntry = zipFile.getEntry(libraryZipPath)
zipEntry?.let {
Log.d(TAG, "Extracting apk:/$libraryZipPath to ${destination.absolutePath}")
FileOutputStream(destination).use { outStream ->
zipFile.getInputStream(zipEntry).use { inStream ->
inStream.copyTo(outStream, 32 * 1024)
outStream.fd.sync()
}
}
}
return true
}
}
}
return false
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
fun loadSharedLibrary(context: Context, libraryName: String) {
Log.d(TAG, "Loading library: $libraryName")
try {
System.loadLibrary(libraryName)
return
} catch (_: UnsatisfiedLinkError) {
Log.d(TAG, "Failed to load library, try to extract it from apk")
}
var tempFile: File? = null
try {
tempFile = File.createTempFile("lib", ".so", context.codeCacheDir)
if (extractLibrary(context, libraryName, tempFile)) {
System.load(tempFile.absolutePath)
return
}
} catch (e: Exception) {
throw LoadLibraryException("Failed to load library apk: $libraryName", e)
} finally {
tempFile?.delete()
}
}
}
}
private fun VpnService.Builder.addAddress(addr: InetNetwork) = addAddress(addr.address, addr.mask)
private fun VpnService.Builder.addRoute(addr: InetNetwork) = addRoute(addr.address, addr.mask)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun VpnService.Builder.excludeRoute(addr: InetNetwork) = excludeRoute(IpPrefix(addr.address, addr.mask))

View file

@ -0,0 +1,113 @@
package org.amnezia.vpn.protocol
import android.net.ProxyInfo
import android.os.Build
import androidx.annotation.RequiresApi
import java.net.InetAddress
import org.amnezia.vpn.util.net.InetNetwork
open class ProtocolConfig protected constructor(
val addresses: Set<InetNetwork>,
val dnsServers: Set<InetAddress>,
val searchDomain: String?,
val routes: Set<InetNetwork>,
val excludedRoutes: Set<InetNetwork>,
val excludedApplications: Set<String>,
val httpProxy: ProxyInfo?,
val allowAllAF: Boolean,
val blockingMode: Boolean,
val mtu: Int
) {
protected constructor(builder: Builder) : this(
builder.addresses,
builder.dnsServers,
builder.searchDomain,
builder.routes,
builder.excludedRoutes,
builder.excludedApplications,
builder.httpProxy,
builder.allowAllAF,
builder.blockingMode,
builder.mtu
)
open class Builder(blockingMode: Boolean) {
internal val addresses: MutableSet<InetNetwork> = hashSetOf()
internal val dnsServers: MutableSet<InetAddress> = hashSetOf()
internal val routes: MutableSet<InetNetwork> = hashSetOf()
internal val excludedRoutes: MutableSet<InetNetwork> = hashSetOf()
internal val excludedApplications: MutableSet<String> = hashSetOf()
internal var searchDomain: String? = null
private set
internal var httpProxy: ProxyInfo? = null
private set
internal var allowAllAF: Boolean = false
private set
internal var blockingMode: Boolean = blockingMode
private set
internal var allowSplitTunneling: Boolean = true
private set
open var mtu: Int = 0
protected set
fun addAddress(addr: InetNetwork) = apply { this.addresses += addr }
fun addAddresses(addresses: Collection<InetNetwork>) = apply { this.addresses += addresses }
fun clearAddresses() = apply { this.addresses.clear() }
fun addDnsServer(dnsServer: InetAddress) = apply { this.dnsServers += dnsServer }
fun addDnsServers(dnsServers: Collection<InetAddress>) = apply { this.dnsServers += dnsServers }
fun setSearchDomain(domain: String) = apply { this.searchDomain = domain }
fun addRoute(route: InetNetwork) = apply { this.routes += route }
fun addRoutes(routes: Collection<InetNetwork>) = apply { this.routes += routes }
fun removeRoute(route: InetNetwork) = apply { this.routes.remove(route) }
fun clearRoutes() = apply { this.routes.clear() }
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun excludeRoute(route: InetNetwork) = apply { this.excludedRoutes += route }
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun excludeRoutes(routes: Collection<InetNetwork>) = apply { this.excludedRoutes += routes }
fun excludeApplication(application: String) = apply { this.excludedApplications += application }
fun excludeApplications(applications: Collection<String>) = apply { this.excludedApplications += applications }
@RequiresApi(Build.VERSION_CODES.Q)
fun setHttpProxy(httpProxy: ProxyInfo) = apply { this.httpProxy = httpProxy }
fun setAllowAllAF(allowAllAF: Boolean) = apply { this.allowAllAF = allowAllAF }
fun setBlockingMode(blockingMode: Boolean) = apply { this.blockingMode = blockingMode }
fun disableSplitTunneling() = apply { this.allowSplitTunneling = false }
fun setMtu(mtu: Int) = apply { this.mtu = mtu }
private fun validate() {
val errorMessage = StringBuilder()
with(errorMessage) {
if (addresses.isEmpty()) appendLine("VPN interface network address not specified.")
if (routes.isEmpty()) appendLine("VPN interface route not specified.")
if (mtu == 0) appendLine("MTU not set.")
}
if (errorMessage.isNotEmpty()) throw BadConfigException(errorMessage.toString())
}
open fun build(): ProtocolConfig = validate().run { ProtocolConfig(this@Builder) }
}
companion object {
inline fun build(blockingMode: Boolean, block: Builder.() -> Unit): ProtocolConfig =
Builder(blockingMode).apply(block).build()
}
}

View file

@ -0,0 +1,11 @@
package org.amnezia.vpn.protocol
// keep synchronized with client/platforms/android/android_controller.h ConnectionState
enum class ProtocolState {
CONNECTED,
CONNECTING,
DISCONNECTED,
DISCONNECTING,
RECONNECTING,
UNKNOWN
}

View file

@ -0,0 +1,50 @@
package org.amnezia.vpn.protocol
import android.os.Bundle
private const val RX_BYTES_KEY = "rxBytes"
private const val TX_BYTES_KEY = "txBytes"
@Suppress("DataClassPrivateConstructor")
data class Statistics private constructor(
val rxBytes: Long = 0L,
val txBytes: Long = 0L
) {
private constructor(builder: Builder) : this(builder.rxBytes, builder.txBytes)
@Suppress("SuspiciousEqualsCombination")
fun isEmpty(): Boolean = this === EMPTY_STATISTICS || this == EMPTY_STATISTICS
class Builder {
var rxBytes: Long = 0L
private set
var txBytes: Long = 0L
private set
fun setRxBytes(rxBytes: Long) = apply { this.rxBytes = rxBytes }
fun setTxBytes(txBytes: Long) = apply { this.txBytes = txBytes }
fun build(): Statistics =
if (rxBytes + txBytes == 0L) EMPTY_STATISTICS
else Statistics(this)
}
companion object {
val EMPTY_STATISTICS: Statistics = Statistics()
inline fun build(block: Builder.() -> Unit): Statistics = Builder().apply(block).build()
}
}
fun Bundle.putStatistics(statistics: Statistics) {
putLong(RX_BYTES_KEY, statistics.rxBytes)
putLong(TX_BYTES_KEY, statistics.txBytes)
}
fun Bundle.getStatistics(): Statistics =
Statistics.build {
setRxBytes(getLong(RX_BYTES_KEY))
setTxBytes(getLong(TX_BYTES_KEY))
}

View file

@ -0,0 +1,34 @@
package org.amnezia.vpn.protocol
import android.os.Bundle
private const val STATE_KEY = "state"
@Suppress("DataClassPrivateConstructor")
data class Status private constructor(
val state: ProtocolState
) {
private constructor(builder: Builder) : this(builder.state)
class Builder {
lateinit var state: ProtocolState
private set
fun setState(state: ProtocolState) = apply { this.state = state }
fun build(): Status = Status(this)
}
companion object {
inline fun build(block: Builder.() -> Unit): Status = Builder().apply(block).build()
}
}
fun Bundle.putStatus(status: Status) {
putInt(STATE_KEY, status.state.ordinal)
}
fun Bundle.getStatus(): Status =
Status.build {
setState(ProtocolState.entries[getInt(STATE_KEY)])
}

View file

@ -0,0 +1,25 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
id("property-delegate")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
val qtAndroidDir: String by gradleProperties
android {
namespace = "org.qtproject.qt.android.binding"
sourceSets {
getByName("main") {
java.setSrcDirs(listOf("$qtAndroidDir/src"))
res.setSrcDirs(listOf("$qtAndroidDir/res"))
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar", "*.aar"))))
}

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

View file

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.Dialog">
<style name="NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
<style name="Translucent" parent="NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowCloseOnTouchOutside">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="ThemeOverlay.AppCompat.DayNight" parent="ThemeOverlay.AppCompat.Light"/>
</resources>
</resources>

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="/" />
</paths>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="files_path" path="/"/>
</paths>

View file

@ -1,132 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 1000,
"identityHash": "14b379f7776710b79b9d617090efe40e",
"entities": [
{
"tableName": "Profile",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remotePort",
"columnName": "remotePort",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "method",
"columnName": "method",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remoteDns",
"columnName": "remoteDns",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "udpdns",
"columnName": "udpdns",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ipv6",
"columnName": "ipv6",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '14b379f7776710b79b9d617090efe40e')"
]
}
}

View file

@ -1,46 +0,0 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}

View file

@ -1,21 +0,0 @@
pluginManagement {
repositories {
google()
mavenCentral()
jcenter()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter()
maven { url 'https://jitpack.io' }
}
}
include ':shadowsocks'

View file

@ -0,0 +1,60 @@
import com.android.build.api.dsl.SettingsExtension
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
includeBuild("./gradle/plugins")
}
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
includeBuild("./gradle/plugins")
plugins {
id("com.android.settings") version "8.2.0"
id("settings-property-delegate")
}
rootProject.name = "AmneziaVPN"
rootProject.buildFileName = "build.gradle.kts"
include(":qt")
include(":utils")
include(":protocolApi")
include(":wireguard")
include(":awg")
include(":openvpn")
include(":cloak")
// get values from gradle or local properties
val androidBuildToolsVersion: String by gradleProperties
val androidCompileSdkVersion: String by gradleProperties
val androidNdkVersion: String by gradleProperties
val qtMinSdkVersion: String by gradleProperties
// set default values for all modules
configure<SettingsExtension> {
buildToolsVersion = androidBuildToolsVersion
compileSdk = androidCompileSdkVersion.substringAfter('-').toInt()
minSdk = qtMinSdkVersion.toInt()
ndkVersion = androidNdkVersion
}
// stop Gradle running by androiddeployqt
gradle.taskGraph.whenReady {
if (providers.environmentVariable("ANDROIDDEPLOYQT_RUN").isPresent
&& !providers.systemProperty("explicitRun").isPresent) {
allTasks.forEach { it.enabled = false }
}
}

View file

@ -1,70 +0,0 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
//apply plugin: 'com.novoda.bintray-release'
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0.0"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
androidExtensions {
experimental = true
}
//def lifecycleVersion = '2.0.0'
def roomVersion = "2.4.3"
//def preferencexVersion = '1.0.0'
dependencies {
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.30-M1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.room:room-runtime:$roomVersion" // runtime
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.7.1"
implementation "androidx.browser:browser:1.3.0-alpha01"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "com.google.android.material:material:1.2.0-alpha05"
implementation "com.google.code.gson:gson:2.8.5"
implementation "dnsjava:dnsjava:2.1.9"
implementation "com.github.kruton:jsocks:1.0.0"
implementation "com.afollestad.material-dialogs:core:2.6.0"
// api "com.takisoft.preferencex:preferencex:1.0.0"
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
api 'com.github.kruton:jsocks:1.0.0'
kapt "androidx.room:room-compiler:$roomVersion"
kapt "androidx.lifecycle:lifecycle-compiler:2.4.0"
}

View file

@ -1,12 +0,0 @@
#!/usr/bin/env perl
## ArchLinux install package via pacman: perl-net-cidr-lite
use strict;
use warnings;
use Net::CIDR::Lite;
my $cidr = Net::CIDR::Lite->new;
while (my $line=<>) {
$cidr->add($line);
}
foreach my $line( @{$cidr->list} ) {
print "<item>$line</item>\n";
}

View file

@ -1,20 +0,0 @@
#!/usr/bin/python
# -*- encoding: utf8 -*-
import sys
import IPy
def main():
china_list_set = IPy.IPSet()
for line in sys.stdin:
china_list_set.add(IPy.IP(line))
# 输出结果
for ip in china_list_set:
print '<item>' + str(ip) + '</item>'
if __name__ == "__main__":
main()

View file

@ -1,121 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import pkgutil
import urlparse
import socket
import logging
from argparse import ArgumentParser
from datetime import date
__all__ = ['main']
def parse_args():
parser = ArgumentParser()
parser.add_argument('-i', '--input', dest='input', required=True,
help='path to gfwlist', metavar='GFWLIST')
parser.add_argument('-f', '--file', dest='output', required=True,
help='path to output acl', metavar='ACL')
return parser.parse_args()
def decode_gfwlist(content):
# decode base64 if have to
try:
return content.decode('base64')
except:
return content
def get_hostname(something):
try:
# quite enough for GFW
if not something.startswith('http:'):
something = 'http://' + something
r = urlparse.urlparse(something)
return r.hostname
except Exception as e:
logging.error(e)
return None
def add_domain_to_set(s, something):
hostname = get_hostname(something)
if hostname is not None:
if hostname.startswith('.'):
hostname = hostname.lstrip('.')
if hostname.endswith('/'):
hostname = hostname.rstrip('/')
if hostname:
s.add(hostname)
def parse_gfwlist(content):
gfwlist = content.splitlines(False)
domains = set()
for line in gfwlist:
if line.find('.*') >= 0:
continue
elif line.find('*') >= 0:
line = line.replace('*', '/')
if line.startswith('!'):
continue
elif line.startswith('['):
continue
elif line.startswith('@'):
# ignore white list
continue
elif line.startswith('||'):
add_domain_to_set(domains, line.lstrip('||'))
elif line.startswith('|'):
add_domain_to_set(domains, line.lstrip('|'))
elif line.startswith('.'):
add_domain_to_set(domains, line.lstrip('.'))
else:
add_domain_to_set(domains, line)
# TODO: reduce ['www.google.com', 'google.com'] to ['google.com']
return domains
def generate_acl(domains):
header ="""#
# GFW list from https://github.com/gfwlist/gfwlist/blob/master/gfwlist.txt
# updated on DATE
#
[bypass_all]
[proxy_list]
"""
header = header.replace('DATE', str(date.today()))
proxy_content = ""
ip_content = ""
for domain in sorted(domains):
try:
socket.inet_aton(domain)
ip_content += (domain + "\n")
except socket.error:
domain = domain.replace('.', '\.')
proxy_content += ('(^|\.)' + domain + '$\n')
proxy_content = header + ip_content + proxy_content
return proxy_content
def main():
args = parse_args()
with open(args.input, 'rb') as f:
content = f.read()
content = decode_gfwlist(content)
domains = parse_gfwlist(content)
acl_content = generate_acl(domains)
with open(args.output, 'wb') as f:
f.write(acl_content)
if __name__ == '__main__':
main()

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="ImpliedQuantity" severity="warning" />
<issue id="ExtraTranslation" severity="warning" />
<issue id="MissingDefaultResource" severity="warning" />
<issue id="MissingTranslation" severity="informational" />
</lint>

View file

@ -1,132 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.amnezia.vpn.shadowsocks.core"
tools:ignore="MissingLeanbackLauncher">
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
tools:targetApi="n">
<meta-data
android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg" />
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ShadowsocksVpnService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:label="@string/app_name"-->
<!-- android:permission="android.permission.BIND_VPN_SERVICE"-->
<!-- android:process=":BG"-->
<!-- tools:targetApi="n">-->
<!-- <intent-filter>-->
<!-- <action android:name="android.net.VpnService" />-->
<!-- </intent-filter>-->
<!-- </service>-->
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.TransproxyService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:process=":QtOnlyProcess"-->
<!-- tools:targetApi="n" />-->
<!-- <service-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.bg.ProxyService"-->
<!-- android:directBootAware="true"-->
<!-- android:exported="false"-->
<!-- android:process=":QtOnlyProcess"-->
<!-- tools:targetApi="n" />-->
<!-- <activity-->
<!-- android:name="org.amnezia.vpn.shadowsocks.core.VpnRequestActivity"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:launchMode="singleTask"-->
<!-- android:taskAffinity=""-->
<!-- android:theme="@style/Theme.AppCompat.Translucent" />-->
<receiver
android:name="org.amnezia.vpn.shadowsocks.core.BootReceiver"
android:directBootAware="true"
android:enabled="false"
android:process=":QtOnlyProcess"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager/src/main/AndroidManifest.xml -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="androidx.work.impl.WorkManagerInitializer"
tools:node="remove" />
<service
android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<service
android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
android:directBootAware="true"
android:process=":QtOnlyProcess"
tools:replace="android:directBootAware" />
</application>
</manifest>

View file

@ -1,13 +0,0 @@
package org.amnezia.vpn.shadowsocks.core.aidl;
import org.amnezia.vpn.shadowsocks.core.aidl.IShadowsocksServiceCallback;
interface IShadowsocksService {
int getState();
String getProfileName();
void registerCallback(in IShadowsocksServiceCallback cb);
void startListeningForBandwidth(in IShadowsocksServiceCallback cb, long timeout);
oneway void stopListeningForBandwidth(in IShadowsocksServiceCallback cb);
oneway void unregisterCallback(in IShadowsocksServiceCallback cb);
}

View file

@ -1,18 +0,0 @@
package org.amnezia.vpn.shadowsocks.core.aidl;
import org.amnezia.vpn.shadowsocks.core.aidl.TrafficStats;
//"oneway" unexpected. xinlake
interface IShadowsocksServiceCallback {
oneway void stateChanged(int state, String profileName, String msg);
oneway void trafficUpdated(long profileId, in TrafficStats stats);
// Traffic data has persisted to database, listener should refetch their data from database
oneway void trafficPersisted(long profileId);
}
//oneway interface IShadowsocksServiceCallback {
// void stateChanged(int state, String profileName, String msg);
// void trafficUpdated(long profileId, in TrafficStats stats);
// // Traffic data has persisted to database, listener should refetch their data from database
// void trafficPersisted(long profileId);
//}

View file

@ -1,3 +0,0 @@
package org.amnezia.vpn.shadowsocks.core.aidl;
parcelable TrafficStats;

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more