Skip to content

Commit e1be94c

Browse files
全自动KPM挂载方案
1 parent 5b46add commit e1be94c

10 files changed

Lines changed: 545 additions & 0 deletions

File tree

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.lifecycle.LiveData
1313
import androidx.lifecycle.MutableLiveData
1414
import com.topjohnwu.superuser.CallbackList
1515
import me.bmax.apatch.ui.CrashHandleActivity
16+
import me.bmax.apatch.ui.component.KpmAutoLoadManager
1617
import me.bmax.apatch.util.APatchCli
1718
import me.bmax.apatch.util.APatchKeyHelper
1819
import me.bmax.apatch.util.Version
@@ -225,6 +226,24 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler {
225226
_apStateLiveData.postValue(State.ANDROIDPATCH_INSTALLED)
226227
}
227228
Log.d(TAG, "ap state: " + _apStateLiveData.value)
229+
230+
// 执行KPM自动加载 - 确保在合适的时机执行
231+
Log.d(TAG, "Preparing KPM auto-load...")
232+
// 调试配置状态
233+
KpmAutoLoadManager.debugConfigState()
234+
235+
// 在新的线程中执行,避免阻塞主流程
236+
thread {
237+
try {
238+
// 确保配置已经正确加载
239+
Thread.sleep(1000) // 等待更长时间确保所有初始化完成
240+
Log.d(TAG, "Executing KPM auto-load...")
241+
val result = KpmAutoLoadManager.autoLoadKpmModules()
242+
Log.d(TAG, "KPM auto-load result: $result")
243+
} catch (e: Exception) {
244+
Log.e(TAG, "KPM auto-load failed: ${e.message}", e)
245+
}
246+
}
228247

229248
return@thread
230249
}
@@ -270,6 +289,11 @@ class APApplication : Application(), Thread.UncaughtExceptionHandler {
270289
Log.d(TAG, "Reading superKey...")
271290
superKey = APatchKeyHelper.readSPSuperKey()
272291
Log.d(TAG, "superKey read completed, length=${superKey.length}")
292+
293+
// 加载KPM自动加载配置 - 确保在superKey设置之前加载
294+
Log.d(TAG, "Loading KPM auto-load configuration...")
295+
val kpmConfig = KpmAutoLoadManager.loadConfig(this)
296+
Log.d(TAG, "KPM config loaded: enabled=${kpmConfig.enabled}, paths=${kpmConfig.kpmPaths.size}")
273297

274298
Log.d(TAG, "Initializing OkHttpClient...")
275299
okhttpClient =
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package me.bmax.apatch.ui.component
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import androidx.compose.runtime.mutableStateOf
6+
import androidx.compose.ui.graphics.Color
7+
import me.bmax.apatch.APApplication
8+
import me.bmax.apatch.apApp
9+
import org.json.JSONArray
10+
import org.json.JSONObject
11+
import java.io.File
12+
import java.io.FileReader
13+
import java.io.FileWriter
14+
import java.io.IOException
15+
16+
/**
17+
* KPM自动加载配置数据类
18+
*/
19+
data class KpmAutoLoadConfig(
20+
val enabled: Boolean = false,
21+
val kpmPaths: List<String> = emptyList()
22+
)
23+
24+
/**
25+
* KPM自动加载配置管理器
26+
*/
27+
object KpmAutoLoadManager {
28+
private const val TAG = "KpmAutoLoadManager"
29+
private const val CONFIG_FILE_NAME = "kpm_autoload_config.json"
30+
31+
// 当前配置状态
32+
var isEnabled = mutableStateOf(false)
33+
private set
34+
var kpmPaths = mutableStateOf<List<String>>(emptyList())
35+
private set
36+
37+
/**
38+
* 加载配置文件
39+
*/
40+
fun loadConfig(context: Context): KpmAutoLoadConfig {
41+
return try {
42+
val configFile = File(context.filesDir, CONFIG_FILE_NAME)
43+
if (!configFile.exists()) {
44+
Log.d(TAG, "配置文件不存在,使用默认配置")
45+
return KpmAutoLoadConfig()
46+
}
47+
48+
val reader = FileReader(configFile)
49+
val jsonContent = reader.readText()
50+
reader.close()
51+
52+
val config = parseConfigFromJson(jsonContent) ?: KpmAutoLoadConfig()
53+
isEnabled.value = config.enabled
54+
kpmPaths.value = config.kpmPaths
55+
Log.d(TAG, "配置加载成功: enabled=${config.enabled}, kpmPaths=${config.kpmPaths}")
56+
config
57+
} catch (e: Exception) {
58+
Log.e(TAG, "加载配置失败: ${e.message}", e)
59+
val defaultConfig = KpmAutoLoadConfig()
60+
isEnabled.value = defaultConfig.enabled
61+
kpmPaths.value = defaultConfig.kpmPaths
62+
defaultConfig
63+
}
64+
}
65+
66+
/**
67+
* 保存配置文件
68+
*/
69+
fun saveConfig(context: Context, config: KpmAutoLoadConfig): Boolean {
70+
return try {
71+
val configFile = File(context.filesDir, CONFIG_FILE_NAME)
72+
val jsonContent = getConfigJson(config)
73+
val writer = FileWriter(configFile)
74+
writer.write(jsonContent)
75+
writer.close()
76+
77+
// 更新状态
78+
isEnabled.value = config.enabled
79+
kpmPaths.value = config.kpmPaths
80+
Log.d(TAG, "配置保存成功: enabled=${config.enabled}, kpmPaths=${config.kpmPaths}")
81+
true
82+
} catch (e: IOException) {
83+
Log.e(TAG, "保存配置失败: ${e.message}", e)
84+
false
85+
}
86+
}
87+
88+
/**
89+
* 获取配置的JSON字符串
90+
*/
91+
fun getConfigJson(): String {
92+
return getConfigJson(KpmAutoLoadConfig(isEnabled.value, kpmPaths.value))
93+
}
94+
95+
/**
96+
* 获取指定配置的JSON字符串
97+
*/
98+
fun getConfigJson(config: KpmAutoLoadConfig): String {
99+
val jsonObject = JSONObject()
100+
jsonObject.put("enabled", config.enabled)
101+
102+
val pathsArray = JSONArray()
103+
config.kpmPaths.forEach { path ->
104+
pathsArray.put(path)
105+
}
106+
jsonObject.put("kpmPaths", pathsArray)
107+
108+
return jsonObject.toString(2) // 使用缩进格式化
109+
}
110+
111+
/**
112+
* 从JSON字符串解析配置
113+
*/
114+
fun parseConfigFromJson(jsonString: String): KpmAutoLoadConfig? {
115+
return try {
116+
val jsonObject = JSONObject(jsonString)
117+
val enabled = jsonObject.optBoolean("enabled", false)
118+
119+
val kpmPaths = mutableListOf<String>()
120+
val pathsArray = jsonObject.optJSONArray("kpmPaths")
121+
if (pathsArray != null) {
122+
for (i in 0 until pathsArray.length()) {
123+
pathsArray.optString(i)?.let { path ->
124+
if (path.isNotEmpty()) {
125+
kpmPaths.add(path)
126+
}
127+
}
128+
}
129+
}
130+
131+
KpmAutoLoadConfig(enabled, kpmPaths)
132+
} catch (e: Exception) {
133+
Log.e(TAG, "解析JSON失败: ${e.message}", e)
134+
null
135+
}
136+
}
137+
138+
/**
139+
* 在应用启动时自动加载KPM模块
140+
*/
141+
fun autoLoadKpmModules(): Boolean {
142+
Log.d(TAG, "开始检查KPM自动加载配置...")
143+
Log.d(TAG, "当前状态: enabled=${isEnabled.value}, kpmPaths=${kpmPaths.value}")
144+
145+
if (!isEnabled.value || kpmPaths.value.isEmpty()) {
146+
Log.d(TAG, "自动加载未启用或没有配置KPM路径,跳过加载")
147+
Log.d(TAG, "enabled=${isEnabled.value}, paths empty=${kpmPaths.value.isEmpty()}")
148+
return false
149+
}
150+
151+
var successCount = 0
152+
var failCount = 0
153+
154+
kpmPaths.value.forEach { path ->
155+
try {
156+
Log.d(TAG, "尝试加载KPM: $path")
157+
// 检查文件是否存在
158+
val file = java.io.File(path)
159+
if (!file.exists()) {
160+
Log.e(TAG, "KPM文件不存在: $path")
161+
failCount++
162+
return@forEach
163+
}
164+
165+
val rc = me.bmax.apatch.Natives.loadKernelPatchModule(path, "")
166+
if (rc == 0L) {
167+
successCount++
168+
Log.d(TAG, "KPM加载成功: $path")
169+
} else {
170+
failCount++
171+
Log.e(TAG, "KPM加载失败: $path, rc=$rc")
172+
}
173+
} catch (e: Exception) {
174+
failCount++
175+
Log.e(TAG, "KPM加载异常: $path, ${e.message}", e)
176+
}
177+
}
178+
179+
Log.i(TAG, "KPM自动加载完成: 成功=$successCount, 失败=$failCount")
180+
return successCount > 0
181+
}
182+
183+
/**
184+
* 调试方法:检查当前配置状态
185+
*/
186+
fun debugConfigState() {
187+
Log.d(TAG, "=== KPM配置状态调试 ===")
188+
Log.d(TAG, "enabled=${isEnabled.value}")
189+
Log.d(TAG, "kpmPaths size=${kpmPaths.value.size}")
190+
kpmPaths.value.forEachIndexed { index, path ->
191+
Log.d(TAG, "路径[$index]: $path")
192+
}
193+
Log.d(TAG, "=== 调试结束 ===")
194+
}
195+
}

app/src/main/java/me/bmax/apatch/ui/screen/KPM.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
2828
import androidx.compose.material.ExperimentalMaterialApi
2929
import androidx.compose.material.icons.Icons
3030
import androidx.compose.material.icons.filled.Download
31+
import androidx.compose.material.icons.filled.Settings
3132
import androidx.compose.material3.AlertDialogDefaults
3233
import androidx.compose.material3.BasicAlertDialog
3334
import androidx.compose.material3.Button
@@ -76,6 +77,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
7677
import com.ramcosta.composedestinations.annotation.Destination
7778
import com.ramcosta.composedestinations.annotation.RootGraph
7879
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
80+
import com.ramcosta.composedestinations.generated.destinations.KpmAutoLoadConfigScreenDestination
7981
import com.ramcosta.composedestinations.generated.destinations.OnlineKPMScreenDestination
8082
import com.ramcosta.composedestinations.generated.destinations.PatchesDestination
8183
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -496,6 +498,14 @@ private fun TopBar(navigator: DestinationsNavigator) {
496498
TopAppBar(
497499
title = { Text(stringResource(R.string.kpm)) },
498500
actions = {
501+
IconButton(onClick = {
502+
navigator.navigate(KpmAutoLoadConfigScreenDestination)
503+
}) {
504+
Icon(
505+
imageVector = Icons.Filled.Settings,
506+
contentDescription = stringResource(R.string.kpm_autoload_title)
507+
)
508+
}
499509
IconButton(onClick = {
500510
navigator.navigate(OnlineKPMScreenDestination)
501511
}) {

0 commit comments

Comments
 (0)