Skip to content

Commit 5d7f9af

Browse files
fix: prevent black screen crash loop on reboot when keystore decryption fails
1 parent 54c4907 commit 5d7f9af

2 files changed

Lines changed: 73 additions & 25 deletions

File tree

app/src/main/java/me/bmax/apatch/APatchApp.kt

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
118118
const val PREF_BLOCK_KERNELPATCH_UPDATE = "block_kernelpatch_update"
119119
const val PREF_BLOCK_ANDROIDPATCH_UPDATE = "block_androidpatch_update"
120120
private const val SHOW_BACKUP_WARN = "show_backup_warning"
121+
private const val CRASH_COUNT_KEY = "fp_crash_count"
122+
private const val CRASH_TIMESTAMP_KEY = "fp_crash_timestamp"
123+
private const val CRASH_LOOP_THRESHOLD = 2
124+
private const val CRASH_WINDOW_MS = 30_000L
121125
lateinit var sharedPreferences: SharedPreferences
122126
var isSignatureValid = true // removed signature check, always valid
123127

@@ -222,17 +226,20 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
222226
var superKey: String = ""
223227
set(value) {
224228
field = value
225-
val ready = BuildConfig.DEBUG_FAKE_ROOT || Natives.nativeReady(value)
226-
_kpStateLiveData.value =
227-
if (ready) State.KERNELPATCH_INSTALLED else State.UNKNOWN_STATE
228-
_apStateLiveData.value =
229-
if (ready) State.ANDROIDPATCH_NOT_INSTALLED else State.UNKNOWN_STATE
230-
Log.d(TAG, "state: " + _kpStateLiveData.value)
231-
if (!ready) return
229+
// Run entire init chain on a background thread to avoid blocking main thread
230+
thread(name = "superkey-init") {
231+
val ready = BuildConfig.DEBUG_FAKE_ROOT || Natives.nativeReady(value)
232+
_kpStateLiveData.postValue(
233+
if (ready) State.KERNELPATCH_INSTALLED else State.UNKNOWN_STATE
234+
)
235+
_apStateLiveData.postValue(
236+
if (ready) State.ANDROIDPATCH_NOT_INSTALLED else State.UNKNOWN_STATE
237+
)
238+
Log.d(TAG, "state: " + _kpStateLiveData.value)
239+
if (!ready) return@thread
232240

233-
APatchKeyHelper.writeSPSuperKey(value)
241+
APatchKeyHelper.writeSPSuperKey(value)
234242

235-
thread {
236243
val rc = BuildConfig.DEBUG_FAKE_ROOT || Natives.su(0, null)
237244
if (!rc) {
238245
Log.e(TAG, "Native.su failed")
@@ -243,8 +250,6 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
243250
APatchCli.refresh()
244251

245252
// KernelPatch version
246-
//val buildV = Version.buildKPVUInt()
247-
//val installedV = Version.installedKPVUInt()
248253
//use build time to check update
249254
val buildV = Version.getKpImg()
250255
val installedV = Version.installedKPTime()
@@ -258,10 +263,8 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
258263

259264
if (buildV != installedV) {
260265
if (isBlocked) {
261-
// 如果屏蔽更新,将状态设置为已安装而不是需要更新
262266
_kpStateLiveData.postValue(State.KERNELPATCH_INSTALLED)
263267
} else {
264-
// 正常显示更新提示
265268
_kpStateLiveData.postValue(State.KERNELPATCH_NEED_UPDATE)
266269
}
267270
}
@@ -293,8 +296,6 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
293296
_apStateLiveData.postValue(State.ANDROIDPATCH_NOT_INSTALLED)
294297
}
295298
Log.d(TAG, "ap state: " + _apStateLiveData.value)
296-
297-
return@thread
298299
}
299300
}
300301
}
@@ -332,12 +333,26 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
332333
APatchKeyHelper.setSharedPreferences(sharedPreferences)
333334
me.bmax.apatch.util.LauncherIconUtils.applySaved(this)
334335
Log.d(TAG, "Reading superKey...")
335-
val savedKey = APatchKeyHelper.readSPSuperKey()
336+
val savedKey = try {
337+
APatchKeyHelper.readSPSuperKey()
338+
} catch (e: Exception) {
339+
Log.e(TAG, "Failed to read superKey from SharedPreferences", e)
340+
null
341+
}
336342
if (!savedKey.isNullOrEmpty()) {
337343
superKey = savedKey
338344
} else {
339345
val keyFile = java.io.File("/data/adb/folk_superkey")
340-
superKey = if (keyFile.exists()) keyFile.readText().trim() else "su"
346+
superKey = if (keyFile.exists()) {
347+
try {
348+
keyFile.readText().trim()
349+
} catch (e: Exception) {
350+
Log.e(TAG, "Failed to read folk_superkey file", e)
351+
"su"
352+
}
353+
} else {
354+
"su"
355+
}
341356
}
342357
Log.d(TAG, "superKey read completed, length=${superKey.length}")
343358

@@ -368,6 +383,12 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
368383
MusicManager.init(this)
369384

370385
Log.d(TAG, "APApplication onCreate completed")
386+
387+
// Reset crash counter on successful initialization
388+
sharedPreferences.edit()
389+
.remove(CRASH_COUNT_KEY)
390+
.remove(CRASH_TIMESTAMP_KEY)
391+
.apply()
371392
}
372393

373394
fun getBackupWarningState(): Boolean {
@@ -390,12 +411,31 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler, ImageLoade
390411
val exceptionMessage = Log.getStackTraceString(e)
391412
val threadName = t.name
392413
Log.e(TAG, "Error on thread $threadName:\n $exceptionMessage")
393-
val intent = Intent(this, CrashHandleActivity::class.java).apply {
394-
putExtra("exception_message", exceptionMessage)
395-
putExtra("thread", threadName)
396-
flags = Intent.FLAG_ACTIVITY_NEW_TASK
414+
415+
val now = System.currentTimeMillis()
416+
val prefs = getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
417+
val lastCrashTime = prefs.getLong(CRASH_TIMESTAMP_KEY, 0L)
418+
val crashCount = if (now - lastCrashTime < CRASH_WINDOW_MS) {
419+
prefs.getInt(CRASH_COUNT_KEY, 0) + 1
420+
} else {
421+
1
422+
}
423+
prefs.edit()
424+
.putInt(CRASH_COUNT_KEY, crashCount)
425+
.putLong(CRASH_TIMESTAMP_KEY, now)
426+
.commit()
427+
428+
if (crashCount <= CRASH_LOOP_THRESHOLD) {
429+
val intent = Intent(this, CrashHandleActivity::class.java).apply {
430+
putExtra("exception_message", exceptionMessage)
431+
putExtra("thread", threadName)
432+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
433+
}
434+
startActivity(intent)
435+
} else {
436+
Log.e(TAG, "Crash loop detected ($crashCount crashes in ${CRASH_WINDOW_MS}ms window). " +
437+
"Skipping CrashHandleActivity to prevent infinite loop.")
397438
}
398-
startActivity(intent)
399439
exitProcess(10)
400440
}
401441
}

app/src/main/java/me/bmax/apatch/util/APatchKeyHelper.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,19 @@ public static String readSPSuperKey() {
135135
if (!encKey.isEmpty()) {
136136
Log.d(TAG, "Decrypting superKey");
137137
String decryptedKey = decrypt(encKey);
138-
Log.d(TAG, "SuperKey decrypted successfully, length: " + decryptedKey.length());
139-
return decryptedKey;
138+
if (decryptedKey != null) {
139+
Log.d(TAG, "SuperKey decrypted successfully, length: " + decryptedKey.length());
140+
return decryptedKey;
141+
} else {
142+
Log.w(TAG, "SuperKey decryption failed, clearing corrupted encrypted key from SharedPreferences");
143+
prefs.edit()
144+
.remove(SUPER_KEY_ENC)
145+
.remove(SUPER_KEY_IV)
146+
.apply();
147+
}
140148
}
141149

142-
Log.d(TAG, "Encrypted key is empty, trying deprecated key");
150+
Log.d(TAG, "Trying deprecated plaintext key");
143151
@Deprecated()
144152
String key = prefs.getString(SUPER_KEY, "");
145153
if (!key.isEmpty()) {

0 commit comments

Comments
 (0)