Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
215b417da3 | ||
![]() |
97dd76ea7b | ||
![]() |
35d762ccf9 | ||
![]() |
d0086de333 | ||
![]() |
9eef389cdb | ||
![]() |
b9a0364b3b | ||
![]() |
0b63efcd67 | ||
![]() |
b3060187ef | ||
![]() |
b6118e4c9f | ||
![]() |
4135eb0110 | ||
![]() |
936adcafa6 | ||
![]() |
ad62fc4aca | ||
![]() |
aaa12e51f0 | ||
![]() |
a440ddd7e7 | ||
![]() |
0e571af728 | ||
![]() |
e46e983bb8 | ||
![]() |
1ae9a57b57 | ||
![]() |
5e80223e7a | ||
![]() |
4d6174f5d8 | ||
![]() |
ce9a062bea | ||
![]() |
4910dcfa96 | ||
![]() |
744b45476c | ||
![]() |
ca43c6e69e |
17 changed files with 45 additions and 265 deletions
4
.github/workflows/deploy.yml
vendored
4
.github/workflows/deploy.yml
vendored
|
@ -381,7 +381,7 @@ jobs:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_BUILD_PLATFORM: android-34
|
ANDROID_BUILD_PLATFORM: android-34
|
||||||
QT_VERSION: 6.7.3
|
QT_VERSION: 6.6.3
|
||||||
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools'
|
||||||
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }}
|
||||||
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
PROD_S3_ENDPOINT: ${{ secrets.PROD_S3_ENDPOINT }}
|
||||||
|
@ -398,7 +398,7 @@ jobs:
|
||||||
version: ${{ env.QT_VERSION }}
|
version: ${{ env.QT_VERSION }}
|
||||||
host: 'linux'
|
host: 'linux'
|
||||||
target: 'desktop'
|
target: 'desktop'
|
||||||
arch: 'linux_gcc_64'
|
arch: 'gcc_64'
|
||||||
modules: ${{ env.QT_MODULES }}
|
modules: ${{ env.QT_MODULES }}
|
||||||
dir: ${{ runner.temp }}
|
dir: ${{ runner.temp }}
|
||||||
py7zrversion: '==0.22.*'
|
py7zrversion: '==0.22.*'
|
||||||
|
|
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2087)
|
set(APP_ANDROID_VERSION_CODE 1087)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
<manifest
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="org.amnezia.vpn"
|
||||||
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
android:versionName="-- %%INSERT_VERSION_NAME%% --"
|
||||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
|
|
||||||
|
<uses-sdk android:maxSdkVersion="25" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
<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.any" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
|
@ -67,6 +70,9 @@
|
||||||
android:name="android.app.lib_name"
|
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" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|
|
@ -33,7 +33,7 @@ android.library.defaults.buildfeatures.androidresources=false
|
||||||
# For development copy and set local values for these parameters in local.properties
|
# For development copy and set local values for these parameters in local.properties
|
||||||
#androidCompileSdkVersion=android-34
|
#androidCompileSdkVersion=android-34
|
||||||
#androidBuildToolsVersion=34.0.0
|
#androidBuildToolsVersion=34.0.0
|
||||||
#qtMinSdkVersion=26
|
#qtMinSdkVersion=24
|
||||||
#qtTargetSdkVersion=34
|
#qtTargetSdkVersion=34
|
||||||
#androidNdkVersion=26.1.10909125
|
#androidNdkVersion=26.1.10909125
|
||||||
#qtTargetAbiList=x86_64
|
#qtTargetAbiList=x86_64
|
||||||
|
|
|
@ -183,14 +183,6 @@ class OpenVpnClient(
|
||||||
// Never called more than once per tun_builder session.
|
// Never called more than once per tun_builder session.
|
||||||
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
|
override fun tun_builder_set_proxy_http(host: String, port: Int): Boolean {
|
||||||
Log.d(TAG, "tun_builder_set_proxy_http: $host, $port")
|
Log.d(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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,12 +113,7 @@ abstract class Protocol {
|
||||||
Log.d(TAG, "addRoute: $inetNetwork")
|
Log.d(TAG, "addRoute: $inetNetwork")
|
||||||
vpnBuilder.addRoute(inetNetwork)
|
vpnBuilder.addRoute(inetNetwork)
|
||||||
} else {
|
} else {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
|
||||||
Log.d(TAG, "excludeRoute: $inetNetwork")
|
|
||||||
vpnBuilder.excludeRoute(inetNetwork)
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Trying to exclude route $inetNetwork on old Android")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,13 +130,6 @@ abstract class Protocol {
|
||||||
Log.d(TAG, "setMtu: ${config.mtu}")
|
Log.d(TAG, "setMtu: ${config.mtu}")
|
||||||
vpnBuilder.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) {
|
if (config.allowAllAF) {
|
||||||
Log.d(TAG, "allowFamily")
|
Log.d(TAG, "allowFamily")
|
||||||
vpnBuilder.allowFamily(OsConstants.AF_INET)
|
vpnBuilder.allowFamily(OsConstants.AF_INET)
|
||||||
|
@ -151,8 +139,6 @@ abstract class Protocol {
|
||||||
Log.d(TAG, "setBlocking: ${config.blockingMode}")
|
Log.d(TAG, "setBlocking: ${config.blockingMode}")
|
||||||
vpnBuilder.setBlocking(config.blockingMode)
|
vpnBuilder.setBlocking(config.blockingMode)
|
||||||
vpnBuilder.setUnderlyingNetworks(null)
|
vpnBuilder.setUnderlyingNetworks(null)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
|
||||||
vpnBuilder.setMetered(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ open class ProtocolConfig protected constructor(
|
||||||
}
|
}
|
||||||
// for older versions of Android, build a list of subnets without excluded routes
|
// for older versions of Android, build a list of subnets without excluded routes
|
||||||
// and add them to routes
|
// and add them to routes
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && routes.any { !it.include }) {
|
if (routes.any { !it.include }) {
|
||||||
val ipRangeSet = IpRangeSet()
|
val ipRangeSet = IpRangeSet()
|
||||||
routes.forEach {
|
routes.forEach {
|
||||||
if (it.include) ipRangeSet.add(IpRange(it.inetNetwork))
|
if (it.include) ipRangeSet.add(IpRange(it.inetNetwork))
|
||||||
|
|
|
@ -21,5 +21,5 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
|
implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar"))))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@ package org.amnezia.vpn
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.EXTRA_MIME_TYPES
|
import android.content.Intent.EXTRA_MIME_TYPES
|
||||||
|
@ -77,7 +75,6 @@ class AmneziaActivity : QtActivity() {
|
||||||
private var isWaitingStatus = true
|
private var isWaitingStatus = true
|
||||||
private var isServiceConnected = false
|
private var isServiceConnected = false
|
||||||
private var isInBoundState = false
|
private var isInBoundState = false
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
|
||||||
private lateinit var vpnServiceMessenger: IpcMessenger
|
private lateinit var vpnServiceMessenger: IpcMessenger
|
||||||
private var pfd: ParcelFileDescriptor? = null
|
private var pfd: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
|
@ -186,7 +183,6 @@ class AmneziaActivity : QtActivity() {
|
||||||
doBindService()
|
doBindService()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
registerBroadcastReceivers()
|
|
||||||
intent?.let(::processIntent)
|
intent?.let(::processIntent)
|
||||||
runBlocking { vpnProto = proto.await() }
|
runBlocking { vpnProto = proto.await() }
|
||||||
}
|
}
|
||||||
|
@ -202,26 +198,6 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerBroadcastReceivers() {
|
|
||||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
registerBroadcastReceiver(
|
|
||||||
arrayOf(
|
|
||||||
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
|
|
||||||
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Log.v(
|
|
||||||
TAG, "Notification state changed: ${it?.action}, blocked = " +
|
|
||||||
"${it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)}"
|
|
||||||
)
|
|
||||||
mainScope.launch {
|
|
||||||
qtInitialized.await()
|
|
||||||
QtAndroidController.onNotificationStateChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
Log.v(TAG, "onNewIntent: $intent")
|
Log.v(TAG, "onNewIntent: $intent")
|
||||||
|
@ -267,8 +243,6 @@ class AmneziaActivity : QtActivity() {
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(TAG, "Destroy Amnezia activity")
|
Log.d(TAG, "Destroy Amnezia activity")
|
||||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
|
||||||
notificationStateReceiver = null
|
|
||||||
mainScope.cancel()
|
mainScope.cancel()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
@ -747,7 +721,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun isNotificationPermissionGranted(): Boolean = applicationContext.isNotificationPermissionGranted()
|
fun isNotificationPermissionGranted(): Boolean = true
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun requestNotificationPermission() {
|
fun requestNotificationPermission() {
|
||||||
|
@ -847,67 +821,6 @@ class AmneziaActivity : QtActivity() {
|
||||||
0, 0, 1.0f, 1.0f, 0, 0, 0,0
|
0, 0, 1.0f, 1.0f, 0, 0, 0,0
|
||||||
)
|
)
|
||||||
|
|
||||||
// workaround for a bug in Qt that causes the mouse click event not to be handled
|
|
||||||
// also disable right-click, as it causes the application to crash
|
|
||||||
private var lastButtonState = 0
|
|
||||||
private fun MotionEvent.fixCopy(): MotionEvent = MotionEvent.obtain(
|
|
||||||
downTime,
|
|
||||||
eventTime,
|
|
||||||
action,
|
|
||||||
pointerCount,
|
|
||||||
(0 until pointerCount).map { i ->
|
|
||||||
MotionEvent.PointerProperties().apply {
|
|
||||||
getPointerProperties(i, this)
|
|
||||||
}
|
|
||||||
}.toTypedArray(),
|
|
||||||
(0 until pointerCount).map { i ->
|
|
||||||
MotionEvent.PointerCoords().apply {
|
|
||||||
getPointerCoords(i, this)
|
|
||||||
}
|
|
||||||
}.toTypedArray(),
|
|
||||||
metaState,
|
|
||||||
MotionEvent.BUTTON_PRIMARY,
|
|
||||||
xPrecision,
|
|
||||||
yPrecision,
|
|
||||||
deviceId,
|
|
||||||
edgeFlags,
|
|
||||||
source,
|
|
||||||
flags
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun handleMouseEvent(ev: MotionEvent, superDispatch: (MotionEvent?) -> Boolean): Boolean {
|
|
||||||
when (ev.action) {
|
|
||||||
MotionEvent.ACTION_DOWN -> {
|
|
||||||
lastButtonState = ev.buttonState
|
|
||||||
if (ev.buttonState == MotionEvent.BUTTON_SECONDARY) return true
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent.ACTION_UP -> {
|
|
||||||
when (lastButtonState) {
|
|
||||||
MotionEvent.BUTTON_SECONDARY -> return true
|
|
||||||
MotionEvent.BUTTON_PRIMARY -> {
|
|
||||||
val modEvent = ev.fixCopy()
|
|
||||||
return superDispatch(modEvent).apply { modEvent.recycle() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return superDispatch(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
|
||||||
Log.v(TAG, "dispatchTouch: $ev")
|
|
||||||
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
|
||||||
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
|
||||||
}
|
|
||||||
return super.dispatchTouchEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
|
|
||||||
ev?.let { return handleMouseEvent(ev) { super.dispatchTrackballEvent(it) }}
|
|
||||||
return super.dispatchTrackballEvent(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utils methods
|
* Utils methods
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package org.amnezia.vpn
|
package org.amnezia.vpn
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
import android.service.quicksettings.Tile
|
import android.service.quicksettings.Tile
|
||||||
|
@ -148,7 +145,8 @@ class AmneziaTileService : TileService() {
|
||||||
Intent(this, AmneziaActivity::class.java).apply {
|
Intent(this, AmneziaActivity::class.java).apply {
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
}.also {
|
}.also {
|
||||||
startActivityAndCollapseCompat(it)
|
@Suppress("DEPRECATION")
|
||||||
|
startActivityAndCollapse(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,7 +190,8 @@ class AmneziaTileService : TileService() {
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
putExtra(EXTRA_PROTOCOL, vpnProto)
|
putExtra(EXTRA_PROTOCOL, vpnProto)
|
||||||
}.also {
|
}.also {
|
||||||
startActivityAndCollapseCompat(it)
|
@Suppress("DEPRECATION")
|
||||||
|
startActivityAndCollapse(it)
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,23 +215,6 @@ class AmneziaTileService : TileService() {
|
||||||
|
|
||||||
private fun stopVpn() = vpnServiceMessenger.send(Action.DISCONNECT)
|
private fun stopVpn() = vpnServiceMessenger.send(Action.DISCONNECT)
|
||||||
|
|
||||||
@SuppressLint("StartActivityAndCollapseDeprecated")
|
|
||||||
private fun startActivityAndCollapseCompat(intent: Intent) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
startActivityAndCollapse(
|
|
||||||
PendingIntent.getActivity(
|
|
||||||
applicationContext,
|
|
||||||
0,
|
|
||||||
intent,
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
startActivityAndCollapse(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateVpnState(state: ProtocolState) =
|
private fun updateVpnState(state: ProtocolState) =
|
||||||
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
|
scope.launch { VpnStateStore.store { it.copy(protocolState = state) } }
|
||||||
|
|
||||||
|
@ -249,17 +231,14 @@ class AmneziaTileService : TileService() {
|
||||||
when (val protocolState = vpnState.protocolState) {
|
when (val protocolState = vpnState.protocolState) {
|
||||||
CONNECTED -> {
|
CONNECTED -> {
|
||||||
state = Tile.STATE_ACTIVE
|
state = Tile.STATE_ACTIVE
|
||||||
subtitleCompat = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DISCONNECTED, UNKNOWN -> {
|
DISCONNECTED, UNKNOWN -> {
|
||||||
state = Tile.STATE_INACTIVE
|
state = Tile.STATE_INACTIVE
|
||||||
subtitleCompat = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTING, DISCONNECTING, RECONNECTING -> {
|
CONNECTING, DISCONNECTING, RECONNECTING -> {
|
||||||
state = Tile.STATE_UNAVAILABLE
|
state = Tile.STATE_UNAVAILABLE
|
||||||
subtitleCompat = getString(protocolState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateTile()
|
updateTile()
|
||||||
|
@ -267,17 +246,4 @@ class AmneziaTileService : TileService() {
|
||||||
// double update to fix weird visual glitches
|
// double update to fix weird visual glitches
|
||||||
tile.updateTile()
|
tile.updateTile()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var Tile.subtitleCompat: CharSequence?
|
|
||||||
set(value) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
this.subtitle = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
get() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
return this.subtitle
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,10 @@ package org.amnezia.vpn
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
|
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
|
|
||||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
|
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
@ -104,7 +100,6 @@ open class AmneziaVpnService : VpnService() {
|
||||||
private lateinit var networkState: NetworkState
|
private lateinit var networkState: NetworkState
|
||||||
private lateinit var trafficStats: TrafficStats
|
private lateinit var trafficStats: TrafficStats
|
||||||
private var controlReceiver: BroadcastReceiver? = null
|
private var controlReceiver: BroadcastReceiver? = null
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
|
||||||
private var screenOnReceiver: BroadcastReceiver? = null
|
private var screenOnReceiver: BroadcastReceiver? = null
|
||||||
private var screenOffReceiver: BroadcastReceiver? = null
|
private var screenOffReceiver: BroadcastReceiver? = null
|
||||||
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
|
private val clientMessengers = ConcurrentHashMap<Messenger, IpcMessenger>()
|
||||||
|
@ -189,16 +184,6 @@ open class AmneziaVpnService : VpnService() {
|
||||||
Messenger(actionMessageHandler)
|
Messenger(actionMessageHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notification setup
|
|
||||||
*/
|
|
||||||
private val foregroundServiceTypeCompat
|
|
||||||
get() = when {
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> FOREGROUND_SERVICE_TYPE_MANIFEST
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) }
|
private val serviceNotification: ServiceNotification by lazy(NONE) { ServiceNotification(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -232,7 +217,7 @@ open class AmneziaVpnService : VpnService() {
|
||||||
ServiceCompat.startForeground(
|
ServiceCompat.startForeground(
|
||||||
this, NOTIFICATION_ID,
|
this, NOTIFICATION_ID,
|
||||||
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
|
serviceNotification.buildNotification(serverName, vpnProto?.label, protocolState.value),
|
||||||
foregroundServiceTypeCompat
|
0
|
||||||
)
|
)
|
||||||
return START_REDELIVER_INTENT
|
return START_REDELIVER_INTENT
|
||||||
}
|
}
|
||||||
|
@ -309,23 +294,6 @@ open class AmneziaVpnService : VpnService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationStateReceiver = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
registerBroadcastReceiver(
|
|
||||||
arrayOf(
|
|
||||||
NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
|
|
||||||
NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
val state = it?.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false)
|
|
||||||
Log.v(TAG, "Notification state changed: ${it?.action}, blocked = $state")
|
|
||||||
if (state == false) {
|
|
||||||
enableNotification()
|
|
||||||
} else {
|
|
||||||
disableNotification()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
|
|
||||||
registerScreenStateBroadcastReceivers()
|
registerScreenStateBroadcastReceivers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,10 +321,8 @@ open class AmneziaVpnService : VpnService() {
|
||||||
private fun unregisterBroadcastReceivers() {
|
private fun unregisterBroadcastReceivers() {
|
||||||
Log.d(TAG, "Unregister broadcast receivers")
|
Log.d(TAG, "Unregister broadcast receivers")
|
||||||
unregisterBroadcastReceiver(controlReceiver)
|
unregisterBroadcastReceiver(controlReceiver)
|
||||||
unregisterBroadcastReceiver(notificationStateReceiver)
|
|
||||||
unregisterScreenStateBroadcastReceivers()
|
unregisterScreenStateBroadcastReceivers()
|
||||||
controlReceiver = null
|
controlReceiver = null
|
||||||
notificationStateReceiver = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
package org.amnezia.vpn
|
package org.amnezia.vpn
|
||||||
|
|
||||||
import android.Manifest.permission
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.app.NotificationChannelCompat.Builder
|
import androidx.core.app.NotificationChannelCompat.Builder
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.Action
|
import androidx.core.app.NotificationCompat.Action
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import org.amnezia.vpn.protocol.ProtocolState
|
import org.amnezia.vpn.protocol.ProtocolState
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
||||||
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
||||||
|
@ -85,27 +81,17 @@ class ServiceNotification(private val context: Context) {
|
||||||
.setSubText(getSpeedString(speed))
|
.setSubText(getSpeedString(speed))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun isNotificationEnabled(): Boolean {
|
fun isNotificationEnabled(): Boolean = notificationManager.areNotificationsEnabled()
|
||||||
if (!context.isNotificationPermissionGranted()) return false
|
|
||||||
if (!notificationManager.areNotificationsEnabled()) return false
|
|
||||||
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)?.let {
|
|
||||||
it.importance != NotificationManager.IMPORTANCE_NONE
|
|
||||||
} ?: true
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
||||||
if (context.isNotificationPermissionGranted()) {
|
Log.v(TAG, "Update notification: $serverName, $state")
|
||||||
Log.v(TAG, "Update notification: $serverName, $state")
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
||||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
fun updateSpeed(speed: TrafficData) {
|
fun updateSpeed(speed: TrafficData) {
|
||||||
if (context.isNotificationPermissionGranted()) {
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
|
||||||
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSpeedString(traffic: TrafficData) =
|
private fun getSpeedString(traffic: TrafficData) =
|
||||||
|
@ -166,8 +152,3 @@ class ServiceNotification(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.isNotificationPermissionGranted(): Boolean =
|
|
||||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
|
|
||||||
ContextCompat.checkSelfPermission(this, permission.POST_NOTIFICATIONS) ==
|
|
||||||
PackageManager.PERMISSION_GRANTED
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.content.Intent
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
@ -31,12 +30,9 @@ class VpnRequestActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d(TAG, "Start request activity")
|
Log.d(TAG, "Start request activity")
|
||||||
vpnProto = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
@Suppress("DEPRECATION")
|
||||||
intent.extras?.getSerializable(EXTRA_PROTOCOL, VpnProto::class.java)
|
vpnProto = intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
intent.extras?.getSerializable(EXTRA_PROTOCOL) as VpnProto
|
|
||||||
}
|
|
||||||
val requestIntent = VpnService.prepare(applicationContext)
|
val requestIntent = VpnService.prepare(applicationContext)
|
||||||
if (requestIntent != null) {
|
if (requestIntent != null) {
|
||||||
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.amnezia.vpn.util
|
package org.amnezia.vpn.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.icu.text.DateFormat
|
||||||
|
import android.icu.text.SimpleDateFormat
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -8,8 +10,8 @@ import java.io.IOException
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
import java.nio.channels.FileChannel
|
import java.nio.channels.FileChannel
|
||||||
import java.nio.channels.FileLock
|
import java.nio.channels.FileLock
|
||||||
import java.time.LocalDateTime
|
import java.util.Date
|
||||||
import java.time.format.DateTimeFormatter
|
import java.util.Locale
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import org.amnezia.vpn.util.Log.Priority.D
|
import org.amnezia.vpn.util.Log.Priority.D
|
||||||
import org.amnezia.vpn.util.Log.Priority.E
|
import org.amnezia.vpn.util.Log.Priority.E
|
||||||
|
@ -37,7 +39,9 @@ private const val LOG_MAX_FILE_SIZE = 1024 * 1024
|
||||||
* | | | create a report and/or terminate the process |
|
* | | | create a report and/or terminate the process |
|
||||||
*/
|
*/
|
||||||
object Log {
|
object Log {
|
||||||
private val dateTimeFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)
|
private val dateTimeFormat = object : ThreadLocal<DateFormat>() {
|
||||||
|
override fun initialValue(): DateFormat = SimpleDateFormat(DATE_TIME_PATTERN, Locale.US)
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var logDir: File
|
private lateinit var logDir: File
|
||||||
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
private val logFile: File by lazy { File(logDir, LOG_FILE_NAME) }
|
||||||
|
@ -135,7 +139,7 @@ object Log {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
private fun formatLogMsg(tag: String, msg: String, priority: Priority): String {
|
||||||
val date = LocalDateTime.now().format(dateTimeFormat)
|
val date = dateTimeFormat.get()?.format(Date())
|
||||||
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
return "$date ${Process.myPid()} ${Process.myTid()} $priority [${Thread.currentThread().name}] " +
|
||||||
"$tag: $msg\n"
|
"$tag: $msg\n"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,9 @@ import android.net.NetworkCapabilities
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||||
import android.net.NetworkRequest
|
import android.net.NetworkRequest
|
||||||
import android.os.Build
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import org.amnezia.vpn.util.Log
|
import org.amnezia.vpn.util.Log
|
||||||
|
|
||||||
private const val TAG = "NetworkState"
|
private const val TAG = "NetworkState"
|
||||||
|
@ -47,7 +45,9 @@ class NetworkState(
|
||||||
|
|
||||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
|
Log.v(TAG, "onCapabilitiesChanged: $network, $networkCapabilities")
|
||||||
checkNetworkState(network, networkCapabilities)
|
handler.post {
|
||||||
|
checkNetworkState(network, networkCapabilities)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
|
private fun checkNetworkState(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
@ -76,33 +76,10 @@ class NetworkState(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun bindNetworkListener() {
|
fun bindNetworkListener() {
|
||||||
if (isListenerBound) return
|
if (isListenerBound) return
|
||||||
Log.d(TAG, "Bind network listener")
|
Log.d(TAG, "Bind network listener")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
connectivityManager.requestNetwork(networkRequest, networkCallback)
|
||||||
connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler)
|
|
||||||
} else {
|
|
||||||
val numberAttempts = 300
|
|
||||||
var attemptCount = 0
|
|
||||||
while(true) {
|
|
||||||
try {
|
|
||||||
connectivityManager.requestNetwork(networkRequest, networkCallback, handler)
|
|
||||||
break
|
|
||||||
} catch (e: SecurityException) {
|
|
||||||
Log.e(TAG, "Failed to bind network listener: $e")
|
|
||||||
// Android 11 bug: https://issuetracker.google.com/issues/175055271
|
|
||||||
if (e.message?.startsWith("Package android does not belong to") == true) {
|
|
||||||
if (++attemptCount > numberAttempts) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
delay(1000)
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isListenerBound = true
|
isListenerBound = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.amnezia.vpn.util.net
|
package org.amnezia.vpn.util.net
|
||||||
|
|
||||||
import android.net.TrafficStats
|
import android.net.TrafficStats
|
||||||
import android.os.Build
|
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
@ -17,18 +16,12 @@ class TrafficStats {
|
||||||
private var lastTrafficData = TrafficData.ZERO
|
private var lastTrafficData = TrafficData.ZERO
|
||||||
private var lastTimestamp = 0L
|
private var lastTimestamp = 0L
|
||||||
|
|
||||||
private val getTrafficDataCompat: () -> TrafficData =
|
private val getTrafficDataCompat: () -> TrafficData = run {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
val uid = Process.myUid()
|
||||||
val iface = "tun0"
|
fun(): TrafficData {
|
||||||
fun(): TrafficData {
|
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
|
||||||
return TrafficData(TrafficStats.getRxBytes(iface), TrafficStats.getTxBytes(iface))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val uid = Process.myUid()
|
|
||||||
fun(): TrafficData {
|
|
||||||
return TrafficData(TrafficStats.getUidRxBytes(uid), TrafficStats.getUidTxBytes(uid))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
lastTrafficData = getTrafficDataCompat()
|
lastTrafficData = getTrafficDataCompat()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
message("Client android ${CMAKE_ANDROID_ARCH_ABI} build")
|
||||||
|
|
||||||
set(APP_ANDROID_MIN_SDK 26)
|
set(APP_ANDROID_MIN_SDK 24)
|
||||||
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
set(ANDROID_PLATFORM "android-${APP_ANDROID_MIN_SDK}" CACHE STRING
|
||||||
"The minimum API level supported by the application or library" FORCE)
|
"The minimum API level supported by the application or library" FORCE)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue