Skip to content

Commit af12502

Browse files
fix: исправление мгновенного отключения VPN при подключении с выбранным сервером
- V2RayServiceManager: убран ранний выход 'if (coreController.isRunning) return' в startContextService. Этот выход не давал сервису стартовать повторно при рассинхронизации состояния. - V2RayServiceManager: исправлен startCoreLoop — вместо 'return false' при isRunning==true теперь принудительно останавливаем ядро перед новым стартом (stopLoop + 300ms задержка). Ранее: startService() получал false и вызывал stopAllService() -> VPN мигал и выключался. - V2RayVpnService: при null vpnInterface теперь вызывается stopAllService() вместо просто return, чтобы сервис не оставался в неопределённом состоянии. - Добавлены расширенные логи в startCoreLoop, stopCoreLoop, startContextService, startService, MSG_STATE_SWITCH_SERVER, smartConnect, switchServer.
1 parent ee49803 commit af12502

3 files changed

Lines changed: 88 additions & 27 deletions

File tree

V2rayNG/app/src/main/java/com/kiktor/v2whitelist/handler/SmartConnectManager.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,13 @@ object SmartConnectManager {
250250
MmkvManager.setSelectServer(best.first)
251251

252252
// Если VPN уже запущен — переключаем ядро, а не пытаемся стартовать заново
253-
if (V2RayServiceManager.isRunning()) {
253+
val isRunning = V2RayServiceManager.isRunning()
254+
Log.i(AppConfig.TAG, "smartConnect: V2RayServiceManager.isRunning()=$isRunning")
255+
if (isRunning) {
256+
Log.i(AppConfig.TAG, "smartConnect: VPN is running, sending SWITCH_SERVER message")
254257
MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_SWITCH_SERVER, "")
255258
} else {
259+
Log.i(AppConfig.TAG, "smartConnect: VPN is not running, calling startV2Ray()")
256260
withContext(Dispatchers.Main) {
257261
if (context is com.kiktor.v2whitelist.ui.MainActivity) {
258262
context.startV2Ray()
@@ -291,13 +295,17 @@ object SmartConnectManager {
291295
}
292296

293297
if (nextBest != null) {
294-
Log.i(AppConfig.TAG, "Switching to next best server: ${nextBest.second.remarks}")
298+
Log.i(AppConfig.TAG, "switchServer: Switching to ${nextBest.second.remarks}")
295299
sendStatus(context, context.getString(R.string.status_connecting_to, nextBest.second.remarks))
296300
MmkvManager.setSelectServer(nextBest.first)
297301

298-
if (V2RayServiceManager.isRunning()) {
302+
val isRunning = V2RayServiceManager.isRunning()
303+
Log.i(AppConfig.TAG, "switchServer: V2RayServiceManager.isRunning()=$isRunning")
304+
if (isRunning) {
305+
Log.i(AppConfig.TAG, "switchServer: VPN is running, sending SWITCH_SERVER message")
299306
MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_SWITCH_SERVER, "")
300307
} else {
308+
Log.i(AppConfig.TAG, "switchServer: VPN is not running, calling startV2Ray()")
301309
withContext(Dispatchers.Main) {
302310
if (context is com.kiktor.v2whitelist.ui.MainActivity) {
303311
context.startV2Ray()

V2rayNG/app/src/main/java/com/kiktor/v2whitelist/handler/V2RayServiceManager.kt

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,26 @@ object V2RayServiceManager {
9191
* @param context The context from which the service is started.
9292
*/
9393
private fun startContextService(context: Context) {
94-
if (coreController.isRunning) {
94+
// Не делаем ранний выход при coreController.isRunning — startCoreLoop сам разберётся.
95+
// Ранний выход здесь был причиной бага: сервис стартовал, но startCoreLoop видел
96+
// isRunning==true и возвращал false, что вызывало мгновенную остановку VPN.
97+
Log.d(AppConfig.TAG, "startContextService: coreController.isRunning=${coreController.isRunning}")
98+
val guid = MmkvManager.getSelectServer() ?: run {
99+
Log.w(AppConfig.TAG, "startContextService: no selected server, aborting")
100+
return
101+
}
102+
val config = MmkvManager.decodeServerConfig(guid) ?: run {
103+
Log.w(AppConfig.TAG, "startContextService: failed to decode server config for guid=$guid")
95104
return
96105
}
97-
val guid = MmkvManager.getSelectServer() ?: return
98-
val config = MmkvManager.decodeServerConfig(guid) ?: return
99106
if (config.configType != EConfigType.CUSTOM
100107
&& config.configType != EConfigType.POLICYGROUP
101108
&& !Utils.isValidUrl(config.server)
102109
&& !Utils.isPureIpAddress(config.server.orEmpty())
103-
) return
104-
// val result = V2rayConfigUtil.getV2rayConfig(context, guid)
105-
// if (!result.status) return
110+
) {
111+
Log.w(AppConfig.TAG, "startContextService: invalid server address '${config.server}', aborting")
112+
return
113+
}
106114

107115
GlobalScope.launch(Dispatchers.Main) {
108116
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PROXY_SHARING)) {
@@ -116,6 +124,7 @@ object V2RayServiceManager {
116124
} else {
117125
Intent(context.applicationContext, V2RayProxyOnlyService::class.java)
118126
}
127+
Log.i(AppConfig.TAG, "startContextService: launching foreground service for guid=$guid")
119128
ContextCompat.startForegroundService(context, intent)
120129
}
121130

@@ -125,16 +134,41 @@ object V2RayServiceManager {
125134
* Starts the V2Ray core service.
126135
*/
127136
fun startCoreLoop(vpnInterface: ParcelFileDescriptor?): Boolean {
137+
Log.i(AppConfig.TAG, "startCoreLoop: called, coreController.isRunning=${coreController.isRunning}")
138+
139+
// Исправление бага: если ядро считает себя запущенным — принудительно останавливаем его
140+
// перед новым стартом. Раньше здесь был `return false`, что вызывало мгновенную остановку
141+
// VPN т.к. startService() → stopAllService() при false.
128142
if (coreController.isRunning) {
129-
return false
143+
Log.w(AppConfig.TAG, "startCoreLoop: core is already running, stopping it first...")
144+
try {
145+
coreController.stopLoop()
146+
Thread.sleep(300L)
147+
Log.i(AppConfig.TAG, "startCoreLoop: old core stopped successfully")
148+
} catch (e: Exception) {
149+
Log.e(AppConfig.TAG, "startCoreLoop: failed to stop existing core", e)
150+
// Продолжаем попытку запуска даже если не смогли остановить
151+
}
130152
}
131153

132-
val service = getService() ?: return false
133-
val guid = MmkvManager.getSelectServer() ?: return false
134-
val config = MmkvManager.decodeServerConfig(guid) ?: return false
154+
val service = getService() ?: run {
155+
Log.e(AppConfig.TAG, "startCoreLoop: service is null, aborting")
156+
return false
157+
}
158+
val guid = MmkvManager.getSelectServer() ?: run {
159+
Log.e(AppConfig.TAG, "startCoreLoop: no selected server, aborting")
160+
return false
161+
}
162+
val config = MmkvManager.decodeServerConfig(guid) ?: run {
163+
Log.e(AppConfig.TAG, "startCoreLoop: failed to decode config for guid=$guid")
164+
return false
165+
}
166+
Log.d(AppConfig.TAG, "startCoreLoop: building config for '${config.remarks}' (guid=$guid)")
135167
val result = V2rayConfigManager.getV2rayConfig(service, guid)
136-
if (!result.status)
168+
if (!result.status) {
169+
Log.e(AppConfig.TAG, "startCoreLoop: V2rayConfigManager returned invalid config for guid=$guid")
137170
return false
171+
}
138172

139173
try {
140174
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
@@ -143,37 +177,39 @@ object V2RayServiceManager {
143177
mFilter.addAction(Intent.ACTION_USER_PRESENT)
144178
ContextCompat.registerReceiver(service, mMsgReceive, mFilter, Utils.receiverFlags())
145179
} catch (e: Exception) {
146-
Log.d(AppConfig.TAG, "Failed to register broadcast receiver", e)
147-
return false
180+
Log.d(AppConfig.TAG, "startCoreLoop: Failed to register broadcast receiver (may already be registered)", e)
181+
// Не возвращаем false — ресивер мог уже быть зарегистрирован (при switch server)
148182
}
149183

150184
currentConfig = config
151185
var tunFd = vpnInterface?.fd ?: 0
186+
Log.d(AppConfig.TAG, "startCoreLoop: tunFd=$tunFd, isUsingHevTun=${SettingsManager.isUsingHevTun()}")
152187
if (SettingsManager.isUsingHevTun()) {
153188
tunFd = 0
154189
}
155190

156191
try {
192+
Log.i(AppConfig.TAG, "startCoreLoop: calling coreController.startLoop()")
157193
NotificationManager.showNotification(currentConfig)
158194
coreController.startLoop(result.content, tunFd)
159195
} catch (e: Exception) {
160-
Log.e(AppConfig.TAG, "Failed to start Core loop", e)
196+
Log.e(AppConfig.TAG, "startCoreLoop: Failed to start Core loop", e)
161197
return false
162198
}
163199

164200
if (coreController.isRunning == false) {
201+
Log.e(AppConfig.TAG, "startCoreLoop: core did not start (isRunning=false after startLoop)")
165202
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "")
166203
NotificationManager.cancelNotification()
167204
return false
168205
}
169206

207+
Log.i(AppConfig.TAG, "startCoreLoop: core started successfully for '${config.remarks}'")
170208
try {
171209
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
172-
//NotificationManager.showNotification(currentConfig)
173210
NotificationManager.startSpeedNotification(currentConfig)
174-
175211
} catch (e: Exception) {
176-
Log.e(AppConfig.TAG, "Failed to startup service", e)
212+
Log.e(AppConfig.TAG, "startCoreLoop: Failed to send startup notifications", e)
177213
return false
178214
}
179215
return true
@@ -185,16 +221,24 @@ object V2RayServiceManager {
185221
* @return True if the core was stopped successfully, false otherwise.
186222
*/
187223
fun stopCoreLoop(): Boolean {
188-
val service = getService() ?: return false
224+
Log.i(AppConfig.TAG, "stopCoreLoop: called, coreController.isRunning=${coreController.isRunning}")
225+
val service = getService() ?: run {
226+
Log.w(AppConfig.TAG, "stopCoreLoop: service is null")
227+
return false
228+
}
189229

190230
if (coreController.isRunning) {
231+
Log.i(AppConfig.TAG, "stopCoreLoop: stopping V2Ray core loop...")
191232
CoroutineScope(Dispatchers.IO).launch {
192233
try {
193234
coreController.stopLoop()
235+
Log.i(AppConfig.TAG, "stopCoreLoop: core stopped successfully")
194236
} catch (e: Exception) {
195-
Log.e(AppConfig.TAG, "Failed to stop V2Ray loop", e)
237+
Log.e(AppConfig.TAG, "stopCoreLoop: Failed to stop V2Ray loop", e)
196238
}
197239
}
240+
} else {
241+
Log.d(AppConfig.TAG, "stopCoreLoop: core was not running, skipping stopLoop")
198242
}
199243

200244
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_STOP_SUCCESS, "")
@@ -203,7 +247,7 @@ object V2RayServiceManager {
203247
try {
204248
service.unregisterReceiver(mMsgReceive)
205249
} catch (e: Exception) {
206-
Log.d(AppConfig.TAG, "Failed to unregister broadcast receiver", e)
250+
Log.d(AppConfig.TAG, "stopCoreLoop: Failed to unregister broadcast receiver (may already be unregistered)", e)
207251
}
208252

209253
return true
@@ -355,11 +399,16 @@ object V2RayServiceManager {
355399
}
356400

357401
AppConfig.MSG_STATE_SWITCH_SERVER -> {
358-
Log.i(AppConfig.TAG, "Switching Server inside Service")
402+
Log.i(AppConfig.TAG, "MSG_STATE_SWITCH_SERVER: switching server, coreRunning=${coreController.isRunning}")
359403
val vpnInterface = serviceControl.getVpnInterface()
404+
Log.d(AppConfig.TAG, "MSG_STATE_SWITCH_SERVER: vpnInterface=${vpnInterface?.fd}")
405+
val guid = MmkvManager.getSelectServer()
406+
Log.i(AppConfig.TAG, "MSG_STATE_SWITCH_SERVER: target guid=$guid")
360407
stopCoreLoop()
361408
Thread.sleep(1000L) // Wait for core to fully release resources
362-
startCoreLoop(vpnInterface)
409+
Log.i(AppConfig.TAG, "MSG_STATE_SWITCH_SERVER: starting core loop after switch")
410+
val success = startCoreLoop(vpnInterface)
411+
Log.i(AppConfig.TAG, "MSG_STATE_SWITCH_SERVER: startCoreLoop result=$success")
363412
}
364413

365414
AppConfig.MSG_MEASURE_DELAY -> {

V2rayNG/app/src/main/java/com/kiktor/v2whitelist/service/V2RayVpnService.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,18 @@ class V2RayVpnService : VpnService(), ServiceControl {
110110
override fun startService() {
111111
val vpnInterface = mInterface
112112
if (vpnInterface == null) {
113-
Log.d(AppConfig.TAG, "Failed to create VPN interface")
113+
Log.e(AppConfig.TAG, "startService: VPN interface is null — VPN not established, stopping service")
114+
// Останавливаем сервис явно, чтобы не оставаться в неопределённом состоянии
115+
stopAllService()
114116
return
115117
}
118+
Log.i(AppConfig.TAG, "startService: VPN interface OK (fd=${vpnInterface.fd}), starting core loop")
116119
if (!V2RayServiceManager.startCoreLoop(vpnInterface)) {
117-
Log.e(AppConfig.TAG, "Failed to start V2Ray core loop")
120+
Log.e(AppConfig.TAG, "startService: startCoreLoop returned false, stopping service")
118121
stopAllService()
119122
return
120123
}
124+
Log.i(AppConfig.TAG, "startService: core loop started successfully")
121125
}
122126

123127
override fun stopService() {

0 commit comments

Comments
 (0)