Compare commits

...
Sign in to create a new pull request.

23 commits

Author SHA1 Message Date
albexk
215b417da3 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-07-07 16:01:29 +03:00
albexk
97dd76ea7b Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-05-26 12:33:40 +03:00
albexk
35d762ccf9 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-05-20 11:42:18 +03:00
albexk
d0086de333 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-03-21 16:11:42 +03:00
albexk
9eef389cdb Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-03-10 19:20:13 +03:00
albexk
b9a0364b3b Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-03-04 12:15:37 +03:00
albexk
0b63efcd67 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-02-25 18:33:47 +03:00
albexk
b3060187ef Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-02-24 17:43:02 +03:00
albexk
b6118e4c9f Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-02-13 20:34:31 +03:00
pokamest
4135eb0110
Merge pull request #1389 from amnezia-vpn/android7-pull
pull fixes
2025-01-31 23:02:15 +01:00
pokamest
936adcafa6
Merge branch 'android-7' into android7-pull 2025-01-31 23:00:45 +01:00
albexk
ad62fc4aca Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2025-01-15 17:33:36 +03:00
albexk
aaa12e51f0 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
#	client/android/src/org/amnezia/vpn/AmneziaActivity.kt
2025-01-13 19:14:03 +03:00
albexk
a440ddd7e7 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2024-11-08 11:54:44 +03:00
albexk
0e571af728 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2024-10-25 19:24:54 +03:00
albexk
e46e983bb8 Merge branch 'dev' into android-7
# Conflicts:
#	CMakeLists.txt
2024-10-24 19:39:00 +03:00
albexk
1ae9a57b57 Fix merge 2024-10-22 14:19:44 +03:00
albexk
5e80223e7a Merge branch 'dev' into android-7
# Conflicts:
#	.github/workflows/deploy.yml
#	CMakeLists.txt
#	client/android/src/org/amnezia/vpn/AmneziaActivity.kt
#	client/android/src/org/amnezia/vpn/AmneziaVpnService.kt
#	client/android/src/org/amnezia/vpn/ServiceNotification.kt
#	client/android/utils/src/main/kotlin/Log.kt
#	client/android/utils/src/main/kotlin/net/NetworkState.kt
2024-10-22 12:51:10 +03:00
albexk
4d6174f5d8 Fix GA 2024-10-02 15:25:25 +03:00
albexk
ce9a062bea Update version code to separate versions for new and old Androids 2024-10-02 15:20:55 +03:00
albexk
4910dcfa96 Set the maximum version of Androids to 7.1 (API 25) 2024-10-02 15:18:05 +03:00
albexk
744b45476c Bump version to 4.8.2.0 2024-10-01 20:23:06 +03:00
albexk
ca43c6e69e Up Qt to 6.7.3 2024-10-01 20:21:56 +03:00
17 changed files with 45 additions and 265 deletions

View file

@ -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.*'

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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)
} }
} }

View file

@ -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))

View file

@ -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"))))
} }

View file

@ -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
*/ */

View file

@ -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
}
} }

View file

@ -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
} }
/** /**

View file

@ -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

View file

@ -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) {

View file

@ -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"
} }

View file

@ -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
} }

View file

@ -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()

View file

@ -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)