Improve V2Ray config/error handling and startup
Add richer error reporting and simplify control flow across core components. Key changes: - Add errorMessage to ConfigResult and populate it on failures in CoreConfigManager.getV2rayConfig/getV2rayConfig4Speedtest. - Replace many nullable/boolean error returns in CoreConfigManager with explicit errors (error()/exceptions) and non-null return types; tighten validation (invalid servers, missing assets, parsing errors) and simplify inbound/dns/routing/outbound setup logic. - Make getInbounds/getRouting/getDns/getOutbounds/getCustomLocalDns/getRoutingUserRule/getBalance non-failing (throw on error) and remove silent try/catch patterns; improve logging and fallback behaviors. - CoreServiceManager: wrap startContextService calls with try/catch to surface errors to UI; startContextService now throws on invalid state/config and moves startup loop into doStartCoreLoop which registers receivers, starts core loop, and reports detailed failures to UI. - Show notification when VPN service starts (CoreVpnService) to ensure foreground visibility. - MainViewModel: display detailed start failure message from intent when available instead of only a generic toast. These changes improve diagnostics, reduce silent failures, and centralize error handling during startup.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -62,7 +62,13 @@ object CoreServiceManager {
|
||||
context.toast(R.string.app_tile_first_use)
|
||||
return false
|
||||
}
|
||||
startContextService(context)
|
||||
try {
|
||||
startContextService(context)
|
||||
} catch (e: Exception) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: ${e.message}", e)
|
||||
context.toast(e.message ?: e.javaClass.simpleName)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -78,7 +84,12 @@ object CoreServiceManager {
|
||||
MmkvManager.setSelectServer(guid)
|
||||
}
|
||||
|
||||
startContextService(context)
|
||||
try {
|
||||
startContextService(context)
|
||||
} catch (e: Exception) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: ${e.message}", e)
|
||||
context.toast(e.message ?: e.javaClass.simpleName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +117,11 @@ object CoreServiceManager {
|
||||
* Starts the context service for V2Ray.
|
||||
* Chooses between VPN service or Proxy-only service based on user settings.
|
||||
* @param context The context from which the service is started.
|
||||
* @throws IllegalStateException if the core is already running, no server is selected,
|
||||
* server config cannot be decoded, or server configuration is invalid.
|
||||
* @throws Exception if the foreground service fails to start.
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
private fun startContextService(context: Context) {
|
||||
if (coreController.isRunning) {
|
||||
LogUtil.w(AppConfig.TAG, "StartCore-Manager: Core already running")
|
||||
@@ -114,16 +129,16 @@ object CoreServiceManager {
|
||||
}
|
||||
|
||||
val guid = MmkvManager.getSelectServer()
|
||||
if (guid == null) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: No server selected")
|
||||
return
|
||||
}
|
||||
?: run {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: No server selected")
|
||||
error(context.getString(R.string.app_tile_first_use))
|
||||
}
|
||||
|
||||
val config = MmkvManager.decodeServerConfig(guid)
|
||||
if (config == null) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to decode server config")
|
||||
return
|
||||
}
|
||||
?: run {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to decode server config")
|
||||
error(context.getString(R.string.toast_config_file_invalid))
|
||||
}
|
||||
|
||||
if (config.configType != EConfigType.CUSTOM
|
||||
&& config.configType != EConfigType.POLICYGROUP
|
||||
@@ -132,13 +147,14 @@ object CoreServiceManager {
|
||||
&& !Utils.isPureIpAddress(config.server.orEmpty())
|
||||
) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Invalid server configuration")
|
||||
return
|
||||
error(context.getString(R.string.toast_config_file_invalid))
|
||||
}
|
||||
|
||||
// refresh socks port when enabled dynamic socks port
|
||||
SettingsManager.refreshRuntimeSocksPort()
|
||||
|
||||
// val result = V2rayConfigUtil.getV2rayConfig(context, guid)
|
||||
// if (!result.status) return
|
||||
// if (!result.status) error(result.errorMessage.ifBlank { "Failed to get V2Ray config" })
|
||||
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_PROXY_SHARING)) {
|
||||
context.toast(R.string.toast_warning_pref_proxysharing_short)
|
||||
@@ -155,11 +171,7 @@ object CoreServiceManager {
|
||||
Intent(context.applicationContext, CoreProxyOnlyService::class.java)
|
||||
}
|
||||
|
||||
try {
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
} catch (e: Exception) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to start service", e)
|
||||
}
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,36 +191,35 @@ object CoreServiceManager {
|
||||
return false
|
||||
}
|
||||
|
||||
val guid = MmkvManager.getSelectServer()
|
||||
if (guid == null) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: No server selected")
|
||||
try {
|
||||
doStartCoreLoop(service, vpnInterface)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
val message = e.message?.takeUnless { it.isBlank() } ?: e.javaClass.simpleName
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: $message", e)
|
||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, message)
|
||||
NotificationManager.cancelNotification()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val config = MmkvManager.decodeServerConfig(guid)
|
||||
if (config == null) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to decode server config")
|
||||
return false
|
||||
}
|
||||
@Throws(Exception::class)
|
||||
private fun doStartCoreLoop(service: Service, vpnInterface: ParcelFileDescriptor?) {
|
||||
val guid = MmkvManager.getSelectServer() ?: error("No server selected")
|
||||
val config = MmkvManager.decodeServerConfig(guid) ?: error("Failed to decode server config")
|
||||
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-Manager: Starting core loop for ${config.remarks}")
|
||||
val result = CoreConfigManager.getV2rayConfig(service, guid)
|
||||
LogUtil.d(AppConfig.TAG, result.content)
|
||||
if (!result.status) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to get V2Ray config")
|
||||
return false
|
||||
error(result.errorMessage.ifBlank { "Failed to get V2Ray config" })
|
||||
}
|
||||
|
||||
try {
|
||||
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
|
||||
mFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||
mFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||
ContextCompat.registerReceiver(service, mMsgReceive, mFilter, Utils.receiverFlags())
|
||||
} catch (e: Exception) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to register receiver", e)
|
||||
return false
|
||||
}
|
||||
val mFilter = IntentFilter(AppConfig.BROADCAST_ACTION_SERVICE)
|
||||
mFilter.addAction(Intent.ACTION_SCREEN_ON)
|
||||
mFilter.addAction(Intent.ACTION_SCREEN_OFF)
|
||||
mFilter.addAction(Intent.ACTION_USER_PRESENT)
|
||||
ContextCompat.registerReceiver(service, mMsgReceive, mFilter, Utils.receiverFlags())
|
||||
|
||||
currentConfig = config
|
||||
var tunFd = vpnInterface?.fd ?: 0
|
||||
@@ -216,30 +227,16 @@ object CoreServiceManager {
|
||||
tunFd = 0
|
||||
}
|
||||
|
||||
try {
|
||||
NotificationManager.showNotification(currentConfig)
|
||||
coreController.startLoop(result.content, tunFd)
|
||||
} catch (e: Exception) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to start core loop", e)
|
||||
return false
|
||||
NotificationManager.showNotification(currentConfig)
|
||||
coreController.startLoop(result.content, tunFd)
|
||||
|
||||
if (!coreController.isRunning) {
|
||||
error("Core failed to start")
|
||||
}
|
||||
|
||||
if (coreController.isRunning == false) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Core failed to start")
|
||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_FAILURE, "")
|
||||
NotificationManager.cancelNotification()
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
|
||||
NotificationManager.startSpeedNotification(currentConfig)
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-Manager: Core started successfully")
|
||||
} catch (e: Exception) {
|
||||
LogUtil.e(AppConfig.TAG, "StartCore-Manager: Failed to complete startup", e)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
MessageUtil.sendMsg2UI(service, AppConfig.MSG_STATE_START_SUCCESS, "")
|
||||
NotificationManager.startSpeedNotification(currentConfig)
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-Manager: Core started successfully")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,7 +403,7 @@ object CoreServiceManager {
|
||||
//LogUtil.d(AppConfig.TAG, "ProcessFinder: Find $network connection from $srcIP:$srcPort to $destIP:$destPort, uid=$uid,${PackageUidResolver.uidToPackageName(uid.toString())}")
|
||||
|
||||
uid
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
-1L
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@ data class ConfigResult(
|
||||
var status: Boolean,
|
||||
var guid: String? = null,
|
||||
var content: String = "",
|
||||
var errorMessage: String = "",
|
||||
)
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ class CoreVpnService : VpnService(), ServiceControl {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
LogUtil.i(AppConfig.TAG, "StartCore-VPN: Service command received")
|
||||
NotificationManager.showNotification(null)
|
||||
setupVpnService()
|
||||
startService()
|
||||
return START_STICKY
|
||||
|
||||
@@ -457,7 +457,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
}
|
||||
|
||||
AppConfig.MSG_STATE_START_FAILURE -> {
|
||||
getApplication<AngApplication>().toastError(R.string.toast_services_failure)
|
||||
val errorMessage = intent.getStringExtra("content")
|
||||
if (!errorMessage.isNullOrBlank()) {
|
||||
getApplication<AngApplication>().toastError(errorMessage)
|
||||
} else {
|
||||
getApplication<AngApplication>().toastError(R.string.toast_services_failure)
|
||||
}
|
||||
isRunning.value = false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user