From 49fe45c49edfece44bed77b57c3e0321d6a3bb0e Mon Sep 17 00:00:00 2001 From: eliotcougar Date: Sun, 16 Aug 2026 05:30:56 +0300 Subject: [PATCH] Recover MMKV data on storage errors (#6085) --- .../main/java/com/v2ray/ang/AngApplication.kt | 4 +- .../java/com/v2ray/ang/handler/MmkvManager.kt | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt index 82b909cc..0ded4d8e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/AngApplication.kt @@ -5,9 +5,9 @@ import android.content.Context import androidx.core.content.ContextCompat import androidx.work.Configuration import androidx.work.WorkManager -import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig.ANG_PACKAGE import com.v2ray.ang.handler.AppLocaleManager +import com.v2ray.ang.handler.MmkvManager import com.v2ray.ang.handler.SettingsManager import com.v2ray.ang.ui.compose.ThemeManager @@ -35,7 +35,7 @@ class AngApplication : Application() { override fun onCreate() { super.onCreate() - MMKV.initialize(this) + MmkvManager.initialize(this) AppLocaleManager.initialize(this) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt index 5ebd02f3..007178de 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt @@ -1,5 +1,7 @@ package com.v2ray.ang.handler +import android.content.Context +import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState @@ -7,9 +9,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.snapshotFlow import com.tencent.mmkv.MMKV +import com.tencent.mmkv.MMKVHandler +import com.tencent.mmkv.MMKVLogLevel +import com.tencent.mmkv.MMKVRecoverStrategic import com.v2ray.ang.AppConfig.DEFAULT_SUBSCRIPTION_ID import com.v2ray.ang.AppConfig.PREF_IS_BOOTED import com.v2ray.ang.AppConfig.PREF_ROUTING_RULESET +import com.v2ray.ang.AppConfig.TAG +import com.v2ray.ang.BuildConfig import com.v2ray.ang.dto.entities.AssetUrlCache import com.v2ray.ang.dto.entities.AssetUrlItem import com.v2ray.ang.dto.entities.ProfileItem @@ -41,6 +48,24 @@ object MmkvManager { private const val KEY_SUB_IDS = "SUB_IDS" private const val KEY_WEBDAV_CONFIG = "WEBDAV_CONFIG" + private val recoveryHandler = object : MMKVHandler { + override fun onMMKVCRCCheckFail(mmapID: String) = + recoverFromStorageError(mmapID, "CRC check") + + override fun onMMKVFileLengthError(mmapID: String) = + recoverFromStorageError(mmapID, "file length check") + + override fun wantLogRedirecting(): Boolean = false + + override fun mmkvLog( + level: MMKVLogLevel, + file: String, + line: Int, + function: String, + message: String + ) = Unit + } + private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) } private val profileFullStorage by lazy { MMKV.mmkvWithID(ID_PROFILE_FULL_CONFIG, MMKV.MULTI_PROCESS_MODE) } private val serverRawStorage by lazy { MMKV.mmkvWithID(ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) } @@ -51,6 +76,29 @@ object MmkvManager { //endregion + /** + * Initializes MMKV with best-effort recovery so a damaged store is not silently discarded. + */ + fun initialize(context: Context) { + val logLevel = if (BuildConfig.DEBUG) { + MMKVLogLevel.LevelDebug + } else { + MMKVLogLevel.LevelInfo + } + MMKV.initialize( + context, + context.filesDir.resolve("mmkv").absolutePath, + null, + logLevel, + recoveryHandler + ) + } + + private fun recoverFromStorageError(mmapID: String, error: String): MMKVRecoverStrategic { + Log.e(TAG, "MMKV $error failed for $mmapID; attempting data recovery") + return MMKVRecoverStrategic.OnErrorRecover + } + //region Server /**