GKMSPatch/manager/src/main/java/org/lsposed/npatch/Patcher.kt

84 lines
3.5 KiB
Kotlin

package org.lsposed.npatch
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.lsposed.npatch.config.Configs
import org.lsposed.npatch.config.MyKeyStore
import org.lsposed.npatch.share.Constants
import org.lsposed.npatch.share.PatchConfig
import org.lsposed.patch.NPatch
import org.lsposed.patch.util.Logger
import java.io.File
import java.io.IOException
object Patcher {
class Options(
val newPackageName: String,
private val injectDex: Boolean,
private val config: PatchConfig,
private val apkPaths: List<String>,
private val embeddedModules: List<String>?
) {
fun toStringArray(): Array<String> {
return buildList {
add("-o"); add(lspApp.tmpApkDir.absolutePath)
add("-p"); add(config.newPackage)
if (config.debuggable) add("-d")
add("-l"); add(config.sigBypassLevel.toString())
if (config.useManager) add("--manager")
if (config.overrideVersionCode) add("-r")
if (Configs.detailPatchLogs) add("-v")
embeddedModules?.forEach {
add("-m"); add(it)
}
if (config.injectProvider) add("--provider")
if(injectDex) add("--injectdex")
if (config.useMicroG) add("--useMicroG")
if (!MyKeyStore.useDefault) {
addAll(arrayOf("-k", MyKeyStore.file.path, Configs.keyStorePassword, Configs.keyStoreAlias, Configs.keyStoreAliasPassword))
}
addAll(apkPaths)
}.toTypedArray()
}
}
suspend fun patch(logger: Logger, options: Options) {
withContext(Dispatchers.IO) {
NPatch(logger, *options.toStringArray()).doCommandLine()
val uri = Configs.storageDirectory?.toUri()
?: throw IOException("Uri is null")
val root = DocumentFile.fromTreeUri(lspApp, uri)
?: throw IOException("DocumentFile is null")
root.listFiles().forEach {
if (it.name?.endsWith(Constants.PATCH_FILE_SUFFIX) == true) it.delete()
}
lspApp.targetApkFiles?.clear()
val apkFileList = arrayListOf<File>()
lspApp.tmpApkDir.walk()
.filter { it.isFile && it.name.endsWith(Constants.PATCH_FILE_SUFFIX) }
.forEach { tempApkFile ->
val cachedApkFile = File(lspApp.externalCacheDir, tempApkFile.name)
if (tempApkFile.renameTo(cachedApkFile).not()) {
tempApkFile.copyTo(cachedApkFile, overwrite = true)
tempApkFile.delete()
}
apkFileList.add(cachedApkFile)
val finalFile = root.createFile("application/vnd.android.package-archive", cachedApkFile.name)
?: throw IOException("無法建立輸出檔案: ${cachedApkFile.name}")
lspApp.contentResolver.openOutputStream(finalFile.uri)?.use { output ->
cachedApkFile.inputStream().use { input ->
input.copyTo(output)
}
} ?: throw IOException("Unable to open an output stream: ${finalFile.uri}")
}
lspApp.targetApkFiles = apkFileList
logger.i("Patched files are saved to ${root.uri.lastPathSegment}")
}
}
}