From fdce3ea2c177bc6ea3d8656ba176fcf0f8230f02 Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Sat, 11 Apr 2026 00:31:28 +0300 Subject: [PATCH] fix(service): prevent concurrent operations with synchronization locks --- .../olc/handler/V2RayServiceManager.kt | 49 ++++++++-- .../java/xyz/zarazaex/olc/ui/MainActivity.kt | 94 +++++++++++++------ 2 files changed, 107 insertions(+), 36 deletions(-) diff --git a/V2rayNG/app/src/main/java/xyz/zarazaex/olc/handler/V2RayServiceManager.kt b/V2rayNG/app/src/main/java/xyz/zarazaex/olc/handler/V2RayServiceManager.kt index f5e04083..3c672c0d 100644 --- a/V2rayNG/app/src/main/java/xyz/zarazaex/olc/handler/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/java/xyz/zarazaex/olc/handler/V2RayServiceManager.kt @@ -30,6 +30,8 @@ object V2RayServiceManager { private val coreController: CoreController = V2RayNativeManager.newCoreController(CoreCallback()) private val mMsgReceive = ReceiveMessageHandler() private var currentConfig: ProfileItem? = null + private val operationLock = Any() + @Volatile private var isOperationInProgress = false var serviceControl: SoftReference? = null set(value) { @@ -57,13 +59,27 @@ object V2RayServiceManager { * @param guid The GUID of the server configuration to use (optional). */ fun startVService(context: Context, guid: String? = null) { - Log.i(AppConfig.TAG, "StartCore-Manager: startVService from ${context::class.java.simpleName}") - - if (guid != null) { - MmkvManager.setSelectServer(guid) + synchronized(operationLock) { + if (isOperationInProgress) { + Log.w(AppConfig.TAG, "StartCore-Manager: Operation already in progress") + return + } + isOperationInProgress = true } - startContextService(context) + try { + Log.i(AppConfig.TAG, "StartCore-Manager: startVService from ${context::class.java.simpleName}") + + if (guid != null) { + MmkvManager.setSelectServer(guid) + } + + startContextService(context) + } finally { + synchronized(operationLock) { + isOperationInProgress = false + } + } } /** @@ -71,8 +87,21 @@ object V2RayServiceManager { * @param context The context from which the service is stopped. */ fun stopVService(context: Context) { - //context.toast(R.string.toast_services_stop) - MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "") + synchronized(operationLock) { + if (isOperationInProgress) { + Log.w(AppConfig.TAG, "StartCore-Manager: Operation already in progress") + return + } + isOperationInProgress = true + } + + try { + MessageUtil.sendMsg2Service(context, AppConfig.MSG_STATE_STOP, "") + } finally { + synchronized(operationLock) { + isOperationInProgress = false + } + } } /** @@ -385,11 +414,17 @@ object V2RayServiceManager { AppConfig.MSG_STATE_STOP -> { Log.i(AppConfig.TAG, "StartCore-Manager: Stop service") + synchronized(operationLock) { + isOperationInProgress = false + } serviceControl.stopService() } AppConfig.MSG_STATE_RESTART -> { Log.i(AppConfig.TAG, "StartCore-Manager: Restart service") + synchronized(operationLock) { + isOperationInProgress = false + } serviceControl.stopService() Thread.sleep(500L) startVService(serviceControl.getService()) diff --git a/V2rayNG/app/src/main/java/xyz/zarazaex/olc/ui/MainActivity.kt b/V2rayNG/app/src/main/java/xyz/zarazaex/olc/ui/MainActivity.kt index e42c200b..58372ec6 100644 --- a/V2rayNG/app/src/main/java/xyz/zarazaex/olc/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/java/xyz/zarazaex/olc/ui/MainActivity.kt @@ -52,6 +52,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect val mainViewModel: MainViewModel by viewModels() private lateinit var groupPagerAdapter: GroupPagerAdapter private var tabMediator: TabLayoutMediator? = null + @Volatile private var isFabOperationInProgress = false private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { @@ -201,12 +202,24 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect } private fun handleFabAction() { + if (isFabOperationInProgress) { + return + } + isFabOperationInProgress = true + applyRunningState(isLoading = true, isRunning = false) - if (mainViewModel.isRunning.value == true) { - V2RayServiceManager.stopVService(this) - } else { - startV2RayWithPermission() + lifecycleScope.launch { + try { + if (mainViewModel.isRunning.value == true) { + V2RayServiceManager.stopVService(this@MainActivity) + } else { + startV2RayWithPermission() + } + } finally { + delay(1000) + isFabOperationInProgress = false + } } } @@ -220,29 +233,42 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect } private fun handleLiteAction() { - if (mainViewModel.isRunning.value == true) { - V2RayServiceManager.stopVService(this) + if (isFabOperationInProgress) { + return } - - showStatus("Обновление профилей...") - showLoading() - isLiteTesting = true - - lifecycleScope.launch(Dispatchers.IO) { - val result = mainViewModel.updateConfigViaSubAll() - delay(500L) - launch(Dispatchers.Main) { - if (result.configCount > 0) { - mainViewModel.reloadServerList() - showStatus("Обновлено ${result.configCount} профилей. Запуск теста...") - } else { - showStatus("Запуск теста...") + isFabOperationInProgress = true + + lifecycleScope.launch { + try { + if (mainViewModel.isRunning.value == true) { + V2RayServiceManager.stopVService(this@MainActivity) + delay(1000) } - hideLoading() - - delay(500L) - showStatus("Выполняется замер задержки. Ожидаем завершения...") - mainViewModel.testAllRealPing() + + showStatus("Обновление профилей...") + showLoading() + isLiteTesting = true + + launch(Dispatchers.IO) { + val result = mainViewModel.updateConfigViaSubAll() + delay(500L) + launch(Dispatchers.Main) { + if (result.configCount > 0) { + mainViewModel.reloadServerList() + showStatus("Обновлено ${result.configCount} профилей. Запуск теста...") + } else { + showStatus("Запуск теста...") + } + hideLoading() + + delay(500L) + showStatus("Выполняется замер задержки. Ожидаем завершения...") + mainViewModel.testAllRealPing() + } + } + } finally { + delay(1000) + isFabOperationInProgress = false } } } @@ -269,12 +295,22 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect } fun restartV2Ray() { - if (mainViewModel.isRunning.value == true) { - V2RayServiceManager.stopVService(this) + if (isFabOperationInProgress) { + return } + isFabOperationInProgress = true + lifecycleScope.launch { - delay(500) - startV2Ray() + try { + if (mainViewModel.isRunning.value == true) { + V2RayServiceManager.stopVService(this@MainActivity) + } + delay(1000) + startV2Ray() + } finally { + delay(500) + isFabOperationInProgress = false + } } }