新增调试信息

This commit is contained in:
2025-10-27 22:15:25 +08:00
parent ae62457d8c
commit 04642cb2f0
5479 changed files with 683397 additions and 3450 deletions
+4 -1
View File
@@ -32,12 +32,15 @@
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<application
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".Application"
android:banner="@mipmap/ic_banner"
android:icon="@mipmap/ic_launcher"
android:label="BearVPN"
android:roundIcon="@mipmap/ic_launcher_round"
android:extractNativeLibs="true"
tools:targetApi="31">
<meta-data
@@ -142,38 +142,42 @@ class BoxService(
private var activeProfileName = ""
private suspend fun startService(delayStart: Boolean = false) {
try {
Log.d(TAG, "starting service")
// 暂时禁用通知显示
// withContext(Dispatchers.Main) {
// notification.show(activeProfileName, R.string.status_starting)
// }
Log.d(TAG, "🚀 [步骤1] 开始启动服务")
val selectedConfigPath = Settings.activeConfigPath
Log.d(TAG, "📂 [步骤2] 配置路径: $selectedConfigPath")
if (selectedConfigPath.isBlank()) {
Log.e(TAG, "❌ [步骤2] 配置路径为空!")
stopAndAlert(Alert.EmptyConfiguration)
return
}
activeProfileName = Settings.activeProfileName
Log.d(TAG, "📝 [步骤3] Profile名称: $activeProfileName")
val configOptions = Settings.configOptions
Log.d(TAG, "⚙️ [步骤4] 配置选项长度: ${configOptions.length}")
if (configOptions.isBlank()) {
Log.e(TAG, "❌ [步骤4] 配置选项为空!")
stopAndAlert(Alert.EmptyConfiguration)
return
}
Log.d(TAG, "🔨 [步骤5] 开始构建配置...")
val content = try {
Mobile.buildConfig(selectedConfigPath, configOptions)
} catch (e: Exception) {
Log.w(TAG, e)
Log.e(TAG, "❌ [步骤5] buildConfig失败: ${e.message}", e)
Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
stopAndAlert(Alert.EmptyConfiguration)
return
}
Log.d(TAG, "✅ [步骤5] 配置构建成功, 内容长度: ${content.length}")
if (Settings.debugMode) {
File(workingDir, "current-config.json").writeText(content)
}
File(workingDir, "current-config.json").writeText(content)
Log.d(TAG, "💾 [步骤6] 配置已保存")
Log.d(TAG, "🔔 [步骤7] 显示通知...")
withContext(Dispatchers.Main) {
notification.show(activeProfileName, R.string.status_starting)
binder.broadcast {
@@ -181,31 +185,57 @@ class BoxService(
}
}
Log.d(TAG, "🌐 [步骤8] 启动网络监听...")
DefaultNetworkMonitor.start()
Libbox.registerLocalDNSTransport(LocalResolver)
Libbox.setMemoryLimit(!Settings.disableMemoryLimit)
Log.d(TAG, "✅ [步骤8] 网络监听已启动")
Log.d(TAG, "🎯 [步骤9] 调用 Libbox.newService()...")
val newService = try {
Libbox.newService(content, platformInterface)
} catch (e: Exception) {
Log.e(TAG, "❌ [步骤9] Libbox.newService() 失败!")
Log.e(TAG, "❌ 错误信息: ${e.message}")
Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
Log.e(TAG, "❌ 堆栈跟踪: ${e.stackTraceToString()}")
stopAndAlert(Alert.CreateService, e.message)
return
}
Log.d(TAG, "✅ [步骤9] Libbox.newService() 成功!")
if (delayStart) {
Log.d(TAG, "⏳ [步骤10] 延迟1秒...")
delay(1000L)
}
newService.start()
Log.d(TAG, "▶️ [步骤11] 调用 newService.start()...")
try {
newService.start()
Log.d(TAG, "✅ [步骤11] newService.start() 成功!")
} catch (e: Exception) {
Log.e(TAG, "❌ [步骤11] newService.start() 失败!")
Log.e(TAG, "❌ 错误信息: ${e.message}")
Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
Log.e(TAG, "❌ 堆栈跟踪: ${e.stackTraceToString()}")
throw e
}
boxService = newService
commandServer?.setService(boxService)
status.postValue(Status.Started)
Log.d(TAG, "📊 [步骤12] 状态已设置为 Started")
withContext(Dispatchers.Main) {
notification.show(activeProfileName, R.string.status_started)
}
notification.start()
Log.d(TAG, "🎉 [完成] 服务启动成功!")
} catch (e: Exception) {
Log.e(TAG, "❌ [致命错误] startService异常!")
Log.e(TAG, "❌ 错误信息: ${e.message}")
Log.e(TAG, "❌ 异常类型: ${e.javaClass.name}")
Log.e(TAG, "❌ 堆栈跟踪: ${e.stackTraceToString()}")
stopAndAlert(Alert.StartService, e.message)
return
}
@@ -335,8 +365,10 @@ class BoxService(
try {
startCommandServer()
} catch (e: Exception) {
stopAndAlert(Alert.StartCommandServer, e.message)
return@launch
// CommandServer 启动失败不是致命错误
// 在 Android 12+ SELinux 环境下,chown 操作可能被拒绝
// 但这不影响 VPN 核心功能,因此记录警告但继续启动服务
Log.w(TAG, "CommandServer failed to start (non-fatal): ${e.message}")
}
startService()
}
@@ -0,0 +1,382 @@
package com.hiddify.hiddify.bg
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.os.PowerManager
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import com.hiddify.hiddify.Application
import com.hiddify.hiddify.R
import com.hiddify.hiddify.Settings
import com.hiddify.hiddify.constant.Action
import com.hiddify.hiddify.constant.Alert
import com.hiddify.hiddify.constant.Status
import go.Seq
import io.nekohasekai.libbox.BoxService
import io.nekohasekai.libbox.CommandServer
import io.nekohasekai.libbox.CommandServerHandler
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.libbox.PlatformInterface
import io.nekohasekai.libbox.SystemProxyStatus
import io.nekohasekai.mobile.Mobile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
class BoxService(
private val service: Service,
private val platformInterface: PlatformInterface
) : CommandServerHandler {
companion object {
private const val TAG = "A/BoxService"
private var initializeOnce = false
private lateinit var workingDir: File
private fun initialize() {
if (initializeOnce) return
val baseDir = Application.application.filesDir
baseDir.mkdirs()
workingDir = Application.application.getExternalFilesDir(null) ?: return
workingDir.mkdirs()
val tempDir = Application.application.cacheDir
tempDir.mkdirs()
Log.d(TAG, "base dir: ${baseDir.path}")
Log.d(TAG, "working dir: ${workingDir.path}")
Log.d(TAG, "temp dir: ${tempDir.path}")
// 创建数据库目录 (数据库使用相对路径 ./data,所以在workingDir下创建)
val dataDir = File(workingDir, "data")
dataDir.mkdirs()
Log.d(TAG, "database dir: ${dataDir.path}")
// 确保数据库目录有读写权限
if (!dataDir.canRead() || !dataDir.canWrite()) {
Log.w(TAG, "database dir permission issue, trying to fix")
dataDir.setReadable(true, false)
dataDir.setWritable(true, false)
}
Mobile.setup(baseDir.path, workingDir.path, tempDir.path, false)
Libbox.redirectStderr(File(workingDir, "stderr.log").path)
initializeOnce = true
return
}
fun parseConfig(path: String, tempPath: String, debug: Boolean): String {
return try {
Mobile.parse(path, tempPath, debug)
""
} catch (e: Exception) {
Log.w(TAG, e)
e.message ?: "invalid config"
}
}
fun buildConfig(path: String, options: String): String {
return Mobile.buildConfig(path, options)
}
fun start() {
val intent = runBlocking {
withContext(Dispatchers.IO) {
Intent(Application.application, Settings.serviceClass())
}
}
ContextCompat.startForegroundService(Application.application, intent)
}
fun stop() {
Application.application.sendBroadcast(
Intent(Action.SERVICE_CLOSE).setPackage(
Application.application.packageName
)
)
}
fun reload() {
Application.application.sendBroadcast(
Intent(Action.SERVICE_RELOAD).setPackage(
Application.application.packageName
)
)
}
}
var fileDescriptor: ParcelFileDescriptor? = null
private val status = MutableLiveData(Status.Stopped)
private val binder = ServiceBinder(status)
private val notification = ServiceNotification(status, service)
private var boxService: BoxService? = null
private var commandServer: CommandServer? = null
private var receiverRegistered = false
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Action.SERVICE_CLOSE -> {
stopService()
}
Action.SERVICE_RELOAD -> {
serviceReload()
}
PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
serviceUpdateIdleMode()
}
}
}
}
}
private fun startCommandServer() {
val commandServer =
CommandServer(this, 300)
commandServer.start()
this.commandServer = commandServer
}
private var activeProfileName = ""
private suspend fun startService(delayStart: Boolean = false) {
try {
Log.d(TAG, "starting service")
// 暂时禁用通知显示
// withContext(Dispatchers.Main) {
// notification.show(activeProfileName, R.string.status_starting)
// }
val selectedConfigPath = Settings.activeConfigPath
if (selectedConfigPath.isBlank()) {
stopAndAlert(Alert.EmptyConfiguration)
return
}
activeProfileName = Settings.activeProfileName
val configOptions = Settings.configOptions
if (configOptions.isBlank()) {
stopAndAlert(Alert.EmptyConfiguration)
return
}
val content = try {
Mobile.buildConfig(selectedConfigPath, configOptions)
} catch (e: Exception) {
Log.w(TAG, e)
stopAndAlert(Alert.EmptyConfiguration)
return
}
// ✅ 始终保存完整配置以便调试
try {
File(workingDir, "current-config.json").writeText(content)
Log.d(TAG, "✅ 完整配置已保存到: ${workingDir}/current-config.json")
Log.d(TAG, "📄 配置长度: ${content.length} 字符")
Log.d(TAG, "📄 配置前1000字符:\n${content.substring(0, minOf(1000, content.length))}")
} catch (e: Exception) {
Log.w(TAG, "保存配置文件失败: $e")
}
withContext(Dispatchers.Main) {
notification.show(activeProfileName, R.string.status_starting)
binder.broadcast {
it.onServiceResetLogs(listOf())
}
}
DefaultNetworkMonitor.start()
Libbox.registerLocalDNSTransport(LocalResolver)
Libbox.setMemoryLimit(!Settings.disableMemoryLimit)
val newService = try {
Libbox.newService(content, platformInterface)
} catch (e: Exception) {
stopAndAlert(Alert.CreateService, e.message)
return
}
if (delayStart) {
delay(1000L)
}
newService.start()
boxService = newService
commandServer?.setService(boxService)
status.postValue(Status.Started)
withContext(Dispatchers.Main) {
notification.show(activeProfileName, R.string.status_started)
}
notification.start()
} catch (e: Exception) {
stopAndAlert(Alert.StartService, e.message)
return
}
}
override fun serviceReload() {
notification.close()
status.postValue(Status.Starting)
val pfd = fileDescriptor
if (pfd != null) {
pfd.close()
fileDescriptor = null
}
commandServer?.setService(null)
boxService?.apply {
runCatching {
close()
}.onFailure {
writeLog("service: error when closing: $it")
}
Seq.destroyRef(refnum)
}
boxService = null
runBlocking {
startService(true)
}
}
override fun getSystemProxyStatus(): SystemProxyStatus {
val status = SystemProxyStatus()
if (service is VPNService) {
status.available = service.systemProxyAvailable
status.enabled = service.systemProxyEnabled
}
return status
}
override fun setSystemProxyEnabled(isEnabled: Boolean) {
serviceReload()
}
@RequiresApi(Build.VERSION_CODES.M)
private fun serviceUpdateIdleMode() {
if (Application.powerManager.isDeviceIdleMode) {
boxService?.pause()
} else {
boxService?.wake()
}
}
private fun stopService() {
if (status.value != Status.Started) return
status.value = Status.Stopping
if (receiverRegistered) {
service.unregisterReceiver(receiver)
receiverRegistered = false
}
notification.close()
GlobalScope.launch(Dispatchers.IO) {
val pfd = fileDescriptor
if (pfd != null) {
pfd.close()
fileDescriptor = null
}
commandServer?.setService(null)
boxService?.apply {
runCatching {
close()
}.onFailure {
writeLog("service: error when closing: $it")
}
Seq.destroyRef(refnum)
}
boxService = null
Libbox.registerLocalDNSTransport(null)
DefaultNetworkMonitor.stop()
commandServer?.apply {
close()
Seq.destroyRef(refnum)
}
commandServer = null
Settings.startedByUser = false
withContext(Dispatchers.Main) {
status.value = Status.Stopped
service.stopSelf()
}
}
}
override fun postServiceClose() {
// Not used on Android
}
private suspend fun stopAndAlert(type: Alert, message: String? = null) {
Settings.startedByUser = false
withContext(Dispatchers.Main) {
if (receiverRegistered) {
service.unregisterReceiver(receiver)
receiverRegistered = false
}
notification.close()
binder.broadcast { callback ->
callback.onServiceAlert(type.ordinal, message)
}
status.value = Status.Stopped
}
}
fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (status.value != Status.Stopped) return Service.START_NOT_STICKY
status.value = Status.Starting
if (!receiverRegistered) {
ContextCompat.registerReceiver(service, receiver, IntentFilter().apply {
addAction(Action.SERVICE_CLOSE)
addAction(Action.SERVICE_RELOAD)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
}
}, ContextCompat.RECEIVER_NOT_EXPORTED)
receiverRegistered = true
}
GlobalScope.launch(Dispatchers.IO) {
Settings.startedByUser = true
initialize()
try {
startCommandServer()
} catch (e: Exception) {
stopAndAlert(Alert.StartCommandServer, e.message)
return@launch
}
startService()
}
return Service.START_NOT_STICKY
}
fun onBind(intent: Intent): IBinder {
return binder
}
fun onDestroy() {
binder.close()
}
fun onRevoke() {
stopService()
}
fun writeLog(message: String) {
binder.broadcast {
it.onServiceWriteLog(message)
}
}
}
@@ -47,17 +47,19 @@ class VPNService : VpnService(), PlatformInterfaceWrapper {
}
override fun autoDetectInterfaceControl(fd: Int) {
protect(fd)
Log.d(TAG, "🛡️ autoDetectInterfaceControl 被调用, fd=$fd")
val result = protect(fd)
Log.d(TAG, "🛡️ protect(fd=$fd) 返回: $result")
}
var systemProxyAvailable = false
var systemProxyEnabled = false
fun addIncludePackage(builder: Builder, packageName: String) {
if (packageName == this.packageName) {
if (packageName == this.packageName) {
Log.d("VpnService","Cannot include myself: $packageName")
return
}
try {
try {
Log.d("VpnService","Including $packageName")
builder.addAllowedApplication(packageName)
} catch (e: NameNotFoundException) {
@@ -75,6 +77,10 @@ class VPNService : VpnService(), PlatformInterfaceWrapper {
override fun openTun(options: TunOptions): Int {
if (prepare(this) != null) error("android: missing vpn permission")
Log.d(TAG, "🔧 openTun 被调用")
Log.d(TAG, " MTU: ${options.mtu}")
Log.d(TAG, " AutoRoute: ${options.autoRoute}")
val builder = Builder()
.setSession("sing-box")
.setMtu(options.mtu)
@@ -128,50 +134,87 @@ class VPNService : VpnService(), PlatformInterfaceWrapper {
}
} else {
val inet4RouteAddress = options.inet4RouteRange
Log.d(TAG, "📍 Android <13: 配置IPv4路由")
if (inet4RouteAddress.hasNext()) {
Log.d(TAG, "✅ 找到IPv4路由配置,开始添加...")
while (inet4RouteAddress.hasNext()) {
val address = inet4RouteAddress.next()
Log.d(TAG, " 添加路由: ${address.address()}/${address.prefix()}")
builder.addRoute(address.address(), address.prefix())
}
} else {
// ⚠️ 如果没有指定路由,添加默认路由
Log.w(TAG, "⚠️ 没有IPv4路由配置,添加默认路由 0.0.0.0/0")
builder.addRoute("0.0.0.0", 0)
}
val inet6RouteAddress = options.inet6RouteRange
if (inet6RouteAddress.hasNext()) {
Log.d(TAG, "✅ 找到IPv6路由配置,开始添加...")
while (inet6RouteAddress.hasNext()) {
val address = inet6RouteAddress.next()
Log.d(TAG, " 添加IPv6路由: ${address.address()}/${address.prefix()}")
builder.addRoute(address.address(), address.prefix())
}
}
}
Log.d(TAG, "📱 包过滤配置:")
Log.d(TAG, " perAppProxyEnabled: ${Settings.perAppProxyEnabled}")
Log.d(TAG, " 当前应用包名: $packageName")
if (Settings.perAppProxyEnabled) {
val appList = Settings.perAppProxyList
Log.d(TAG, " ✅ 使用应用级代理,模式: ${Settings.perAppProxyMode}")
Log.d(TAG, " 📋 应用列表数量: ${appList.size}")
if (Settings.perAppProxyMode == PerAppProxyMode.INCLUDE) {
Log.d(TAG, " 🔵 INCLUDE模式 - 只有以下应用使用VPN:")
appList.forEach {
Log.d(TAG, " - $it")
addIncludePackage(builder,it)
}
Log.d(TAG, " - $packageName (本应用)")
addIncludePackage(builder,packageName)
} else {
Log.d(TAG, " 🔴 EXCLUDE模式 - 以下应用不使用VPN:")
appList.forEach {
Log.d(TAG, " - $it")
addExcludePackage(builder,it)
}
//addExcludePackage(builder,packageName)
// 不排除本应用,让libcore能正常工作
// Log.d(TAG, " ⚠️ 本应用也排除VPN")
// addExcludePackage(builder,packageName)
Log.d(TAG, " ️ 本应用不在排除列表")
}
} else {
Log.d(TAG, " ⚙️ 使用配置文件的包过滤规则")
val includePackage = options.includePackage
if (includePackage.hasNext()) {
Log.d(TAG, " 🔵 INCLUDE列表:")
while (includePackage.hasNext()) {
addIncludePackage(builder,includePackage.next())
val pkg = includePackage.next()
Log.d(TAG, " - $pkg")
addIncludePackage(builder,pkg)
}
} else {
Log.d(TAG, " ️ 无INCLUDE规则")
}
val excludePackage = options.excludePackage
if (excludePackage.hasNext()) {
Log.d(TAG, " 🔴 EXCLUDE列表:")
while (excludePackage.hasNext()) {
addExcludePackage(builder,excludePackage.next())
val pkg = excludePackage.next()
Log.d(TAG, " - $pkg")
addExcludePackage(builder,pkg)
}
} else {
Log.d(TAG, " ️ 无EXCLUDE规则")
}
//addExcludePackage(builder,packageName)
// 不排除本应用,让libcore能正常工作
// Log.d(TAG, " ⚠️ 始终排除本应用: $packageName")
// addExcludePackage(builder,packageName)
}
}