Merge branch 'feature/android_qt6_moving' into feature/android_bandwidth_counter
# Conflicts: # client/platforms/android/android_controller.cpp # client/platforms/android/android_controller.h
This commit is contained in:
commit
39736e865e
255 changed files with 4322 additions and 7218 deletions
|
@ -21,13 +21,19 @@
|
|||
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"/>
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:anyDensity="true"
|
||||
android:smallScreens="true"/>
|
||||
|
||||
<application
|
||||
android:name=".qt.AmneziaApp"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:extractNativeLibs="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:icon="@drawable/icon">
|
||||
|
||||
<activity
|
||||
|
@ -36,21 +42,22 @@
|
|||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:screenOrientation="unspecified"
|
||||
android:launchMode="singleInstance"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/splashScreenTheme">
|
||||
android:exported="true">
|
||||
|
||||
<!-- android:theme="@style/splashScreenTheme"-->
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</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" />
|
||||
<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:mimeType="*/*"/>
|
||||
<data android:host="*"/>
|
||||
<data android:pathPattern=".*\\.vpn"/>
|
||||
<data android:pathPattern=".*\\..*\\.vpn"/>
|
||||
|
@ -58,14 +65,14 @@
|
|||
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
|
||||
</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" />
|
||||
<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:mimeType="*/*"/>
|
||||
<data android:host="*"/>
|
||||
<data android:pathPattern=".*\\.cfg"/>
|
||||
<data android:pathPattern=".*\\..*\\.cfg"/>
|
||||
|
@ -73,14 +80,14 @@
|
|||
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
|
||||
</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" />
|
||||
<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:mimeType="*/*"/>
|
||||
<data android:host="*"/>
|
||||
<data android:pathPattern=".*\\.conf"/>
|
||||
<data android:pathPattern=".*\\..*\\.conf"/>
|
||||
|
@ -88,102 +95,51 @@
|
|||
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
|
||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
|
||||
<!-- Application arguments -->
|
||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||
<!-- Application arguments -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Used to specify custom system library path to run with local system libs -->
|
||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||
<!-- Messages maps -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
||||
are done populating your window with content. -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<!-- Background running -->
|
||||
<!-- auto screen scale factor -->
|
||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||
<!-- auto screen scale factor -->
|
||||
<!-- extract android style -->
|
||||
<!-- available android:values :
|
||||
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||
* full - useful QWidget & Quick Controls 1 apps
|
||||
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
||||
* none - useful for apps that don't use any of the above Qt modules
|
||||
-->
|
||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||
<!-- extract android style -->
|
||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
|
||||
<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>
|
||||
|
||||
|
||||
<service
|
||||
android:name=".VPNService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:process=":QtOnlyProcess"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".qt.VPNPermissionHelper"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||
android:exported="true">
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<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: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/fileprovider"/>
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
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"
|
||||
|
@ -72,7 +72,11 @@ android {
|
|||
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
|
||||
//buildToolsVersion '28.0.3'
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
ndkVersion androidNdkVersion
|
||||
|
||||
// Extract native libraries from the APK
|
||||
packagingOptions.jniLibs.useLegacyPackaging true
|
||||
|
||||
dexOptions {
|
||||
javaMaxHeapSize "3g"
|
||||
|
@ -81,9 +85,9 @@ android {
|
|||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
|
||||
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
|
||||
res.srcDirs = [qt5AndroidDir + '/res', 'res']
|
||||
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']
|
||||
|
@ -139,6 +143,7 @@ android {
|
|||
debug {
|
||||
//applicationIdSuffix ".debug"
|
||||
//versionNameSuffix "-debug"
|
||||
minifyEnabled false
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<array name="qt_sources">
|
||||
<item>https://download.qt.io/ministro/android/qt5/qt-5.14</item>
|
||||
</array>
|
||||
|
||||
<!-- The following is handled automatically by the deployment tool. It should
|
||||
not be edited manually. -->
|
||||
<!-- DO NOT EDIT THIS: This file is populated automatically by the deployment tool. -->
|
||||
|
||||
<array name="bundled_libs">
|
||||
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||
|
@ -19,4 +14,8 @@
|
|||
<!-- %%INSERT_LOCAL_LIBS%% -->
|
||||
</array>
|
||||
|
||||
<string name="static_init_classes"><!-- %%INSERT_INIT_CLASSES%% --></string>
|
||||
<string name="use_local_qt_libs"><!-- %%USE_LOCAL_QT_LIBS%% --></string>
|
||||
<string name="bundle_local_qt_libs"><!-- %%BUNDLE_LOCAL_QT_LIBS%% --></string>
|
||||
<string name="system_libs_prefix"><!-- %%SYSTEM_LIBS_PREFIX%% --></string>
|
||||
</resources>
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.amnezia.vpn;
|
|||
import android.content.Context;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Intent;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
import org.qtproject.qt.android.bindings.QtActivity;
|
||||
|
||||
|
||||
import static android.content.Context.KEYGUARD_SERVICE;
|
||||
|
|
|
@ -154,31 +154,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||
private var flags = 0
|
||||
private var startId = 0
|
||||
|
||||
private lateinit var mMessenger: Messenger
|
||||
|
||||
internal class ExternalConfigImportHandler(
|
||||
context: Context,
|
||||
private val serviceBinder: VPNServiceBinder,
|
||||
private val applicationContext: Context = context.applicationContext
|
||||
) : Handler() {
|
||||
|
||||
override fun handleMessage(msg: Message) {
|
||||
when (msg.what) {
|
||||
IMPORT_COMMAND_CODE -> {
|
||||
val data = msg.data.getString(IMPORT_CONFIG_KEY)
|
||||
|
||||
if (data != null) {
|
||||
serviceBinder.importConfig(data)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun init() {
|
||||
if (mAlreadyInitialised) {
|
||||
return
|
||||
|
@ -217,13 +192,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||
override fun onBind(intent: Intent): IBinder {
|
||||
Log.v(tag, "Aman: onBind....................")
|
||||
|
||||
if (intent.action != null && intent.action == IMPORT_ACTION_CODE) {
|
||||
Log.v(tag, "Service bind for import of config")
|
||||
mMessenger = Messenger(ExternalConfigImportHandler(this, mBinder))
|
||||
return mMessenger.binder
|
||||
}
|
||||
|
||||
Log.v(tag, "Regular service bind")
|
||||
when (mProtocol) {
|
||||
"shadowsocks" -> {
|
||||
when (intent.action) {
|
||||
|
@ -720,7 +688,6 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
|
|||
|
||||
private fun startOpenVpn() {
|
||||
mOpenVPNThreadv3 = OpenVPNThreadv3(this)
|
||||
isUp = true
|
||||
|
||||
Thread({
|
||||
mOpenVPNThreadv3?.run()
|
||||
|
|
|
@ -33,6 +33,7 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
|||
const val setNotificationText = 8
|
||||
const val setFallBackNotification = 9
|
||||
const val shareConfig = 10
|
||||
const val importConfig = 11
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,13 +76,14 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
|||
ACTIONS.resumeActivate -> {
|
||||
// [data] is empty
|
||||
// Activate the current tunnel
|
||||
try {
|
||||
mResumeConfig?.let { this.mService.turnOn(it) }
|
||||
Log.i(tag, "resume activate")
|
||||
try {
|
||||
mResumeConfig?.let { this.mService.turnOn(it) }
|
||||
} catch (e: Exception) {
|
||||
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS.deactivate -> {
|
||||
// [data] here is empty
|
||||
|
@ -90,6 +92,7 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
|||
}
|
||||
|
||||
ACTIONS.registerEventListener -> {
|
||||
Log.i(tag, "register: start")
|
||||
// [data] contains the Binder that we need to dispatch the Events
|
||||
val binder = data.readStrongBinder()
|
||||
mListener = binder
|
||||
|
@ -150,6 +153,23 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
|||
return true
|
||||
}
|
||||
|
||||
ACTIONS.importConfig -> {
|
||||
val buffer = data.readString()
|
||||
|
||||
val obj = JSONObject()
|
||||
obj.put("config", buffer)
|
||||
|
||||
val resultString = obj.toString()
|
||||
|
||||
Log.i(tag, "Transact import config request")
|
||||
|
||||
if (mListener != null) {
|
||||
dispatchEvent(EVENTS.configImport, resultString)
|
||||
} else {
|
||||
mImportedConfig = resultString
|
||||
}
|
||||
}
|
||||
|
||||
IBinder.LAST_CALL_TRANSACTION -> {
|
||||
Log.e(tag, "The OS Requested to shut down the VPN")
|
||||
this.mService.turnOff()
|
||||
|
@ -176,9 +196,12 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
|||
try {
|
||||
mListener?.let {
|
||||
if (it.isBinderAlive) {
|
||||
Log.i(tag, "Dispatching event: binder alive")
|
||||
val data = Parcel.obtain()
|
||||
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
|
||||
it.transact(code, data, Parcel.obtain(), 0)
|
||||
} else {
|
||||
Log.i(tag, "Dispatching event: binder NOT alive")
|
||||
}
|
||||
}
|
||||
} catch (e: DeadObjectException) {
|
||||
|
@ -197,23 +220,7 @@ class VPNServiceBinder(service: VPNService) : Binder() {
|
|||
const val statisticUpdate = 3
|
||||
const val backendLogs = 4
|
||||
const val activationError = 5
|
||||
const val configImport = 6
|
||||
}
|
||||
|
||||
fun importConfig(config: String) {
|
||||
val obj = JSONObject()
|
||||
obj.put("config", config)
|
||||
|
||||
val resultString = obj.toString()
|
||||
|
||||
Log.i(tag, "Transact import config request")
|
||||
|
||||
if (mListener != null) {
|
||||
Log.i(tag, "binder alive")
|
||||
dispatchEvent(EVENTS.configImport, resultString)
|
||||
} else {
|
||||
Log.i(tag, "binder NOT alive")
|
||||
mImportedConfig = resultString
|
||||
}
|
||||
const val permissionRequired = 6
|
||||
const val configImport = 7
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package org.amnezia.vpn.qt
|
|||
import android.content.res.Configuration
|
||||
import org.amnezia.vpn.shadowsocks.core.Core
|
||||
import org.amnezia.vpn.shadowsocks.core.VpnManager
|
||||
import org.qtproject.qt5.android.bindings.QtActivity
|
||||
import org.qtproject.qt5.android.bindings.QtApplication
|
||||
import org.qtproject.qt.android.bindings.QtActivity
|
||||
import org.qtproject.qt.android.bindings.QtApplication
|
||||
import android.app.Application
|
||||
|
||||
class AmneziaApp: Application() {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.amnezia.vpn.qt;
|
||||
|
||||
import android.Manifest
|
||||
|
@ -20,18 +24,33 @@ import org.amnezia.vpn.VPNServiceBinder
|
|||
import org.amnezia.vpn.IMPORT_COMMAND_CODE
|
||||
import org.amnezia.vpn.IMPORT_ACTION_CODE
|
||||
import org.amnezia.vpn.IMPORT_CONFIG_KEY
|
||||
import org.qtproject.qt5.android.bindings.QtActivity
|
||||
import org.qtproject.qt.android.bindings.QtActivity
|
||||
import java.io.*
|
||||
|
||||
class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
||||
class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
|
||||
|
||||
private var configString: String? = null
|
||||
private var vpnServiceBinder: Messenger? = null
|
||||
private var vpnServiceBinder: IBinder? = null
|
||||
private var isBound = false
|
||||
|
||||
private val TAG = "VPNActivity"
|
||||
private val STORAGE_PERMISSION_CODE = 42
|
||||
|
||||
companion object {
|
||||
private lateinit var instance: VPNActivity
|
||||
|
||||
@JvmStatic fun getInstance(): VPNActivity {
|
||||
return instance
|
||||
}
|
||||
|
||||
@JvmStatic fun connectService() {
|
||||
VPNActivity.getInstance().initServiceConnection()
|
||||
}
|
||||
|
||||
@JvmStatic fun sendToService(actionCode: Int, body: String) {
|
||||
VPNActivity.getInstance().dispatchParcel(actionCode, body)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val newIntent = intent
|
||||
|
@ -42,6 +61,48 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
|||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
instance = this;
|
||||
}
|
||||
|
||||
override fun getSystemService(name: String): Any? {
|
||||
return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") {
|
||||
// QT will always attempt to read the clipboard if content is there.
|
||||
// since we have no use of the clipboard in android 10+
|
||||
// we _can_ return null
|
||||
// And we defnitly should since android 12 displays clipboard access.
|
||||
null
|
||||
} else {
|
||||
super.getSystemService(name)
|
||||
}
|
||||
}
|
||||
|
||||
external fun handleBackButton(): Boolean
|
||||
|
||||
external fun onServiceMessage(actionCode: Int, body: String?)
|
||||
external fun qtOnServiceConnected()
|
||||
external fun qtOnServiceDisconnected()
|
||||
|
||||
private fun dispatchParcel(actionCode: Int, body: String) {
|
||||
if (!isBound) {
|
||||
Log.d(TAG, "dispatchParcel: not bound")
|
||||
return
|
||||
} else {
|
||||
Log.d(TAG, "dispatchParcel: bound")
|
||||
}
|
||||
|
||||
val out: Parcel = Parcel.obtain()
|
||||
out.writeByteArray(body.toByteArray())
|
||||
|
||||
try {
|
||||
vpnServiceBinder?.transact(actionCode, out, Parcel.obtain(), 0)
|
||||
} catch (e: DeadObjectException) {
|
||||
isBound = false
|
||||
vpnServiceBinder = null
|
||||
qtOnServiceDisconnected()
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(newIntent: Intent) {
|
||||
|
@ -63,19 +124,11 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (configString != null && !isBound) {
|
||||
bindVpnService()
|
||||
if (configString != null && isBound) {
|
||||
sendImportConfigCommand()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (vpnServiceBinder != null && isBound) {
|
||||
unbindService(connection)
|
||||
isBound = false
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun isReadStorageAllowed(): Boolean {
|
||||
val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
return permissionStatus == PackageManager.PERMISSION_GRANTED
|
||||
|
@ -90,8 +143,15 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
|||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Storage read permission granted")
|
||||
|
||||
if (configString == null) {
|
||||
configString = processIntent(intent, intent.action!!)
|
||||
}
|
||||
|
||||
if (configString != null) {
|
||||
bindVpnService()
|
||||
Log.d(TAG, "not empty")
|
||||
sendImportConfigCommand()
|
||||
} else {
|
||||
Log.d(TAG, "empty")
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show()
|
||||
|
@ -99,17 +159,6 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun bindVpnService() {
|
||||
try {
|
||||
val intent = Intent(this, VPNService::class.java)
|
||||
intent.action = IMPORT_ACTION_CODE
|
||||
|
||||
bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun processIntent(intent: Intent, action: String): String? {
|
||||
val scheme = intent.scheme
|
||||
|
||||
|
@ -158,23 +207,35 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun sendImportConfigCommand() {
|
||||
if (configString != null) {
|
||||
val msg: Parcel = Parcel.obtain()
|
||||
msg.writeString(configString!!)
|
||||
|
||||
try {
|
||||
vpnServiceBinder?.transact(ACTION_IMPORT_CONFIG, msg, Parcel.obtain(), 0)
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
configString = null
|
||||
}
|
||||
}
|
||||
|
||||
private var connection: ServiceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
|
||||
vpnServiceBinder = Messenger(binder)
|
||||
vpnServiceBinder = binder
|
||||
|
||||
if (configString != null) {
|
||||
val msg: Message = Message.obtain(null, IMPORT_COMMAND_CODE, 0, 0)
|
||||
val bundle = Bundle()
|
||||
bundle.putString(IMPORT_CONFIG_KEY, configString!!)
|
||||
msg.data = bundle
|
||||
|
||||
try {
|
||||
vpnServiceBinder?.send(msg)
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
configString = null
|
||||
// This is called when the connection with the service has been
|
||||
// established, giving us the object we can use to
|
||||
// interact with the service. We are communicating with the
|
||||
// service using a Messenger, so here we get a client-side
|
||||
// representation of that from the raw IBinder object.
|
||||
if (registerBinder()){
|
||||
qtOnServiceConnected();
|
||||
} else {
|
||||
qtOnServiceDisconnected();
|
||||
return
|
||||
}
|
||||
|
||||
isBound = true
|
||||
|
@ -183,9 +244,75 @@ class VPNActivity : org.qtproject.qt5.android.bindings.QtActivity() {
|
|||
override fun onServiceDisconnected(className: ComponentName) {
|
||||
vpnServiceBinder = null
|
||||
isBound = false
|
||||
qtOnServiceDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerBinder(): Boolean {
|
||||
val binder = VPNClientBinder()
|
||||
val out: Parcel = Parcel.obtain()
|
||||
out.writeStrongBinder(binder)
|
||||
|
||||
try {
|
||||
// Register our IBinder Listener
|
||||
vpnServiceBinder?.transact(ACTION_REGISTER_LISTENER, out, Parcel.obtain(), 0)
|
||||
return true
|
||||
} catch (e: DeadObjectException) {
|
||||
isBound = false
|
||||
vpnServiceBinder = null
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun initServiceConnection() {
|
||||
// We already have a connection to the service,
|
||||
// just need to re-register the binder
|
||||
if (isBound && vpnServiceBinder!!.isBinderAlive() && registerBinder()) {
|
||||
qtOnServiceConnected()
|
||||
return
|
||||
}
|
||||
|
||||
bindService(Intent(this, VPNService::class.java), connection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
// TODO: Move all ipc codes into a shared lib.
|
||||
// this is getting out of hand.
|
||||
private val PERMISSION_TRANSACTION = 1337
|
||||
private val ACTION_REGISTER_LISTENER = 3
|
||||
private val ACTION_RESUME_ACTIVATE = 7
|
||||
private val ACTION_IMPORT_CONFIG = 11
|
||||
private val EVENT_PERMISSION_REQURED = 6
|
||||
private val EVENT_DISCONNECTED = 2
|
||||
|
||||
|
||||
fun onPermissionRequest(code: Int, data: Parcel?) {
|
||||
if (code != EVENT_PERMISSION_REQURED) {
|
||||
return
|
||||
}
|
||||
|
||||
val x = Intent()
|
||||
x.readFromParcel(data)
|
||||
|
||||
startActivityForResult(x, PERMISSION_TRANSACTION)
|
||||
}
|
||||
|
||||
override protected fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == PERMISSION_TRANSACTION) {
|
||||
// THATS US!
|
||||
if (resultCode == RESULT_OK) {
|
||||
// Prompt accepted, tell service to retry.
|
||||
dispatchParcel(ACTION_RESUME_ACTIVATE, "")
|
||||
} else {
|
||||
// Tell the Client we've disconnected
|
||||
onServiceMessage(EVENT_DISCONNECTED, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) {
|
||||
onBackPressed()
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
|
|||
import org.amnezia.vpn.shadowsocks.core.Core;
|
||||
import org.amnezia.vpn.shadowsocks.core.VpnManager;
|
||||
|
||||
public class VPNApplication extends org.qtproject.qt5.android.bindings.QtApplication {
|
||||
public class VPNApplication extends org.qtproject.qt.android.bindings.QtApplication {
|
||||
private static VPNApplication instance;
|
||||
|
||||
@Override
|
||||
|
|
27
client/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
Normal file
27
client/android/src/org/amnezia/vpn/qt/VPNClientBinder.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.amnezia.vpn.qt
|
||||
|
||||
import android.os.Binder
|
||||
import android.os.Parcel
|
||||
import android.util.Log
|
||||
|
||||
const val permissionRequired = 6
|
||||
|
||||
class VPNClientBinder() : Binder() {
|
||||
|
||||
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
||||
if (code == permissionRequired) {
|
||||
VPNActivity.getInstance().onPermissionRequest(code, data)
|
||||
return true
|
||||
}
|
||||
|
||||
val buffer = data.createByteArray()
|
||||
val stringData = buffer?.let { String(it) }
|
||||
VPNActivity.getInstance().onServiceMessage(code, stringData)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package org.ftylitak.qzxing;
|
|||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
import org.qtproject.qt.android.bindings.QtActivity;
|
||||
import static org.ftylitak.qzxing.Utilities.REQUEST_CAMERA;
|
||||
|
||||
public class QZXingLiveActivity extends QtActivity {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue