fix: reset stuck start lock on system Always-on VPN restart (#5958)
* fix: reset stuck start lock on system Always-on VPN restart When Android restarts CoreVpnService for Always-on VPN (via VpnService.SERVICE_INTERFACE or null intent), a stuck isStartingLock from a killed process prevents tryLockStart() from succeeding, causing an immediate return without setupVpnService(). Fix: Reset stuck start lock via unlockStart() on system-initiated VPN start/restart, allowing setupVpnService() to establish the TUN interface. If setup fails during system start, return START_STICKY to keep the service alive for OS reconnection. Also accept LOCKED_BOOT_COMPLETED and MY_PACKAGE_REPLACED in BootReceiver. * Update BootReceiver.kt * Update CoreVpnService.kt --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
1 parent
842567fb89
commit
9896dd2974
3 files changed
+32
-7
No files matched your search
@@ -282,6 +282,8 @@
|
|||||||
android:label="BootReceiver">
|
android:label="BootReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.v2ray.ang.receiver
|
|||||||
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.os.UserManager
|
||||||
import com.v2ray.ang.AppConfig
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.core.CoreServiceManager
|
import com.v2ray.ang.core.CoreServiceManager
|
||||||
import com.v2ray.ang.handler.MmkvManager
|
import com.v2ray.ang.handler.MmkvManager
|
||||||
@@ -12,18 +13,34 @@ import com.v2ray.ang.util.LogUtil
|
|||||||
class BootReceiver : BroadcastReceiver() {
|
class BootReceiver : BroadcastReceiver() {
|
||||||
/**
|
/**
|
||||||
* This method is called when the BroadcastReceiver is receiving an Intent broadcast.
|
* This method is called when the BroadcastReceiver is receiving an Intent broadcast.
|
||||||
* It checks if the context is not null and the action is ACTION_BOOT_COMPLETED.
|
* It handles BOOT_COMPLETED, LOCKED_BOOT_COMPLETED, and MY_PACKAGE_REPLACED.
|
||||||
* If the conditions are met, it starts the V2Ray service.
|
* If the conditions are met, it starts the V2Ray service.
|
||||||
*
|
*
|
||||||
* @param context The Context in which the receiver is running.
|
* @param context The Context in which the receiver is running.
|
||||||
* @param intent The Intent being received.
|
* @param intent The Intent being received.
|
||||||
*/
|
*/
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
LogUtil.i(AppConfig.TAG, "BootReceiver received: ${intent?.action}")
|
val action = intent?.action ?: return
|
||||||
|
if (context == null) return
|
||||||
|
|
||||||
if (context == null || intent?.action != Intent.ACTION_BOOT_COMPLETED) {
|
LogUtil.i(AppConfig.TAG, "BootReceiver received: $action")
|
||||||
LogUtil.w(AppConfig.TAG, "BootReceiver: Invalid context or action")
|
|
||||||
return
|
when (action) {
|
||||||
|
Intent.ACTION_BOOT_COMPLETED,
|
||||||
|
Intent.ACTION_MY_PACKAGE_REPLACED -> {
|
||||||
|
// Continue
|
||||||
|
}
|
||||||
|
Intent.ACTION_LOCKED_BOOT_COMPLETED -> {
|
||||||
|
val userManager = context.getSystemService(Context.USER_SERVICE) as? UserManager
|
||||||
|
if (userManager != null && !userManager.isUserUnlocked) {
|
||||||
|
LogUtil.w(AppConfig.TAG, "BootReceiver: User is locked, skipping auto start")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
LogUtil.w(AppConfig.TAG, "BootReceiver: Unhandled action: $action")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MmkvManager.decodeStartOnBoot()) {
|
if (!MmkvManager.decodeStartOnBoot()) {
|
||||||
|
|||||||
@@ -116,20 +116,26 @@ class CoreVpnService : VpnService(), ServiceControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
// Always-on VPN restarts from OS deliver intent.action == SERVICE_INTERFACE or null intent.
|
||||||
|
// Reset any stuck start lock left by a killed process to allow setupVpnService() to run.
|
||||||
|
val isSystemVpnStart = intent == null || intent.action == SERVICE_INTERFACE
|
||||||
|
if (isSystemVpnStart) {
|
||||||
|
unlockStart()
|
||||||
|
}
|
||||||
if (!tryLockStart()) {
|
if (!tryLockStart()) {
|
||||||
LogUtil.w(AppConfig.TAG, "StartCore-VPN: Start already in progress")
|
LogUtil.w(AppConfig.TAG, "StartCore-VPN: Start already in progress")
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
LogUtil.i(AppConfig.TAG, "StartCore-VPN: Service command received")
|
LogUtil.i(AppConfig.TAG, "StartCore-VPN: Service command received, systemVpnStart=$isSystemVpnStart")
|
||||||
NotificationManager.showNotification(null)
|
NotificationManager.showNotification(null)
|
||||||
if (!setupVpnService()) {
|
if (!setupVpnService()) {
|
||||||
unlockStart()
|
unlockStart()
|
||||||
|
// Stop service if setup fails to avoid infinite restart loops (START_STICKY)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
startService()
|
startService()
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
//return super.onStartCommand(intent, flags, startId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getService(): Service {
|
override fun getService(): Service {
|
||||||
|
|||||||
Reference in new issue
Block a user