diff --git a/app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt b/app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt index 2379b1a..f38305d 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt @@ -202,7 +202,7 @@ class SagerNet : Application(), var underlyingNetwork: Network? = null - var appVersionNameForDisplay = lazy { + var appVersionNameForDisplay = { var n = BuildConfig.VERSION_NAME if (isPreview) { n += " " + BuildConfig.PRE_VERSION_NAME @@ -213,7 +213,7 @@ class SagerNet : Application(), n += " DEBUG" } n - } + }() } } diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt index 4d69504..e760983 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt @@ -105,6 +105,10 @@ class BaseService { override fun getProfileName(): String = data?.proxy?.displayProfileName ?: "Idle" override fun registerCallback(cb: ISagerNetServiceCallback, id: Int) { + if (id == SagerConnection.CONNECTION_ID_RESTART_BG) { + Runtime.getRuntime().exit(0) + return + } if (!callbackIdMap.contains(cb)) { callbacks.register(cb) } diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt index f944058..5b860b3 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt @@ -3,24 +3,21 @@ package io.nekohasekai.sagernet.bg import android.system.ErrnoException import android.system.Os import android.system.OsConstants -import android.text.TextUtils import io.nekohasekai.sagernet.ktx.Logs import java.io.File import java.io.IOException +import androidx.core.text.isDigitsOnly object Executable { private val EXECUTABLES = setOf( - "libtrojan.so", - "libtrojan-go.so", - "libnaive.so", - "libtuic.so", - "libhysteria.so" + "libtrojan.so", "libtrojan-go.so", "libnaive.so", "libtuic.so", "libhysteria.so" ) fun killAll(alsoKillBg: Boolean = false) { - for (process in File("/proc").listFiles { _, name -> TextUtils.isDigitsOnly(name) } - ?: return) { - val exe = File(try { + // kill bg may fail + for (process in File("/proc").listFiles { _, name -> name.isDigitsOnly() } ?: return) { + val exe = File( + try { File(process, "cmdline").inputStream().bufferedReader().use { it.readText() } diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt index ab7d5a4..97ff4b8 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt @@ -32,6 +32,9 @@ class SagerConnection( const val CONNECTION_ID_TILE = 1 const val CONNECTION_ID_MAIN_ACTIVITY_FOREGROUND = 2 const val CONNECTION_ID_MAIN_ACTIVITY_BACKGROUND = 3 + const val CONNECTION_ID_RESTART_BG = 4 + + var restartingApp = false } interface Callback { @@ -124,7 +127,7 @@ class SagerConnection( } catch (e: RemoteException) { e.printStackTrace() } - callback!!.onServiceConnected(service) + callback?.onServiceConnected(service) } override fun onServiceDisconnected(name: ComponentName?) { @@ -137,7 +140,9 @@ class SagerConnection( override fun binderDied() { service = null callbackRegistered = false - callback?.also { runOnMainDispatcher { it.onBinderDied() } } + if (!restartingApp) { + callback?.also { runOnMainDispatcher { it.onBinderDied() } } + } } private fun unregisterCallback() { @@ -149,7 +154,7 @@ class SagerConnection( callbackRegistered = false } - fun connect(context: Context, callback: Callback) { + fun connect(context: Context, callback: Callback?) { if (connectionActive) return connectionActive = true check(this.callback == null) diff --git a/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt b/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt index 5b3a0f8..3e176c8 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt @@ -20,6 +20,8 @@ import androidx.activity.result.ActivityResultLauncher import androidx.annotation.AttrRes import androidx.annotation.ColorRes import androidx.core.content.ContextCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -30,10 +32,10 @@ import com.jakewharton.processphoenix.ProcessPhoenix import io.nekohasekai.sagernet.BuildConfig import io.nekohasekai.sagernet.R import io.nekohasekai.sagernet.SagerNet -import io.nekohasekai.sagernet.bg.Executable +import io.nekohasekai.sagernet.aidl.ISagerNetService +import io.nekohasekai.sagernet.bg.BaseService +import io.nekohasekai.sagernet.bg.SagerConnection import io.nekohasekai.sagernet.database.DataStore -import io.nekohasekai.sagernet.database.SagerDatabase -import io.nekohasekai.sagernet.database.preference.PublicDatabase import io.nekohasekai.sagernet.ui.MainActivity import io.nekohasekai.sagernet.ui.ThemedActivity import kotlinx.coroutines.Dispatchers @@ -201,7 +203,7 @@ val shortAnimTime by lazy { fun View.crossFadeFrom(other: View) { clearAnimation() other.clearAnimation() - if (visibility == View.VISIBLE && other.visibility == View.GONE) return + if (isVisible && other.isGone) return alpha = 0F visibility = View.VISIBLE animate().alpha(1F).duration = shortAnimTime @@ -256,10 +258,24 @@ fun triggerFullRestart(ctx: Context) { runOnDefaultDispatcher { SagerNet.stopService() delay(500) - Executable.killAll(true) - runOnMainDispatcher { + SagerConnection.restartingApp = true + val connection = SagerConnection(SagerConnection.CONNECTION_ID_RESTART_BG) + connection.connect(ctx, RestartCallback { ProcessPhoenix.triggerRebirth(ctx, Intent(ctx, MainActivity::class.java)) - } + }) + } +} + +private class RestartCallback(val callback: () -> Unit) : SagerConnection.Callback { + override fun stateChanged( + state: BaseService.State, + profileName: String?, + msg: String? + ) { + } + + override fun onServiceConnected(service: ISagerNetService) { + callback() } } diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt index bb921d5..f6afc4b 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt @@ -80,7 +80,7 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) { MaterialAboutActionItem.Builder() .icon(R.drawable.ic_baseline_update_24) .text(R.string.app_version) - .subText(SagerNet.appVersionNameForDisplay.value) + .subText(SagerNet.appVersionNameForDisplay) .setOnClickAction { requireContext().launchCustomTab( "https://github.com/MatsuriDayo/NekoBoxForAndroid/releases" @@ -251,7 +251,7 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) { .setMessage( context.getString( R.string.update_dialog_message, - SagerNet.appVersionNameForDisplay.value, + SagerNet.appVersionNameForDisplay, releaseName ) ) diff --git a/nb4a.properties b/nb4a.properties index 5c52ff2..8bd0af2 100644 --- a/nb4a.properties +++ b/nb4a.properties @@ -1,4 +1,4 @@ PACKAGE_NAME=moe.nb4a VERSION_NAME=1.3.9 -PRE_VERSION_NAME=pre-1.4.0-20250904-2 +PRE_VERSION_NAME=pre-1.4.0-20250905-1 VERSION_CODE=43