refactor: combine new dex files into one step
This commit is contained in:
parent
30add80afb
commit
67decbe8e5
17 changed files with 188 additions and 98 deletions
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Juby210 & zt
|
||||
* Licensed under the Open Software License version 3.0
|
||||
*/
|
||||
|
||||
package com.aliucord.manager.network.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BuildInfo(
|
||||
@SerialName("versionCode")
|
||||
val discordVersionCode: String,
|
||||
@SerialName("versionName")
|
||||
val discordVersionName: String,
|
||||
@SerialName("aliucordHash")
|
||||
val injectorVersion: String,
|
||||
)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Juby210 & zt
|
||||
* Licensed under the Open Software License version 3.0
|
||||
*/
|
||||
|
||||
package com.aliucord.manager.network.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Version(
|
||||
val versionCode: String,
|
||||
val versionName: String,
|
||||
val aliucordHash: String,
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.aliucord.manager.network.service
|
||||
|
||||
import com.aliucord.manager.network.dto.Version
|
||||
import com.aliucord.manager.network.dto.BuildInfo
|
||||
import com.aliucord.manager.network.utils.ApiResponse
|
||||
import io.ktor.client.request.url
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -10,7 +10,7 @@ class AliucordGithubService(
|
|||
val github: GithubService,
|
||||
val http: HttpService,
|
||||
) {
|
||||
suspend fun getDataJson(): ApiResponse<Version> {
|
||||
suspend fun getDataJson(): ApiResponse<BuildInfo> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
http.request {
|
||||
url("https://raw.githubusercontent.com/$ORG/$MAIN_REPO/builds/data.json")
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ data class InstallMetadata(
|
|||
val customManager: Boolean,
|
||||
|
||||
/**
|
||||
* Aliuhook release version injected into the APK.
|
||||
* Version of the Aliucord release build that was injected into the APK.
|
||||
*/
|
||||
val aliuhookVersion: String,
|
||||
|
||||
/**
|
||||
* Short commit hash of the commit the injector was built from.
|
||||
* Version of the injector build that was injected into the APK.
|
||||
*/
|
||||
val injectorCommitHash: String,
|
||||
val injectorVersion: String,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ class KotlinPatchRunner(options: PatchOptions) : StepRunner() {
|
|||
// Patch
|
||||
ReplaceIconStep(options),
|
||||
PatchManifestStep(options),
|
||||
AddInjectorStep(),
|
||||
AddAliuhookStep(),
|
||||
ReorganizeDexStep(),
|
||||
AddAliuhookLibsStep(),
|
||||
SaveMetadataStep(options),
|
||||
|
||||
// Install
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.aliucord.manager.patcher.steps.base
|
||||
|
||||
interface IDexProvider {
|
||||
/**
|
||||
* The priority of the .dex files supplied by [getDexFiles].
|
||||
* Higher number leads to a higher overwrite priority.
|
||||
* .dex files already included in the APK have a priority of `0`.
|
||||
*/
|
||||
val dexPriority: Int
|
||||
|
||||
/**
|
||||
* The amount of files returned by [getDexFiles]
|
||||
*/
|
||||
val dexCount: Int
|
||||
|
||||
/**
|
||||
* Any dex files to be added into the APK.
|
||||
*/
|
||||
fun getDexFiles(): List<ByteArray>
|
||||
}
|
||||
|
|
@ -7,14 +7,18 @@ import com.aliucord.manager.manager.PathManager
|
|||
import com.aliucord.manager.network.utils.getOrThrow
|
||||
import com.aliucord.manager.patcher.StepRunner
|
||||
import com.aliucord.manager.patcher.steps.base.DownloadStep
|
||||
import com.aliucord.manager.patcher.steps.base.IDexProvider
|
||||
import com.aliucord.manager.patcher.steps.patch.ReorganizeDexStep
|
||||
import com.github.diamondminer88.zip.ZipReader
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
/**
|
||||
* Download a packaged AAR of the latest Aliuhook build from the Aliucord maven.
|
||||
* Provides [ReorganizeDexStep] with the dex through the [IDexProvider] implementation.
|
||||
*/
|
||||
@Stable
|
||||
class DownloadAliuhookStep : DownloadStep(), KoinComponent {
|
||||
class DownloadAliuhookStep : DownloadStep(), IDexProvider, KoinComponent {
|
||||
private val paths: PathManager by inject()
|
||||
private val maven: AliucordMavenRepository by inject()
|
||||
|
||||
|
|
@ -33,4 +37,15 @@ class DownloadAliuhookStep : DownloadStep(), KoinComponent {
|
|||
|
||||
super.execute(container)
|
||||
}
|
||||
|
||||
override val dexPriority = 0
|
||||
override val dexCount = 1
|
||||
override fun getDexFiles(): List<ByteArray> {
|
||||
val dexBytes = ZipReader(targetFile).use { zip ->
|
||||
zip.openEntry("classes.dex")?.read()
|
||||
?: throw IllegalStateException("No prebuilt classes.dex in downloaded aliuhook build")
|
||||
}
|
||||
|
||||
return listOf(dexBytes)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,36 +3,36 @@ package com.aliucord.manager.patcher.steps.download
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.manager.PathManager
|
||||
import com.aliucord.manager.network.dto.Version
|
||||
import com.aliucord.manager.patcher.StepRunner
|
||||
import com.aliucord.manager.patcher.steps.base.DownloadStep
|
||||
import com.aliucord.manager.patcher.steps.base.IDexProvider
|
||||
import com.aliucord.manager.patcher.steps.patch.ReorganizeDexStep
|
||||
import com.aliucord.manager.patcher.steps.prepare.FetchInfoStep
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
/**
|
||||
* Download a compiled dex file to be injected into the APK as the first `classes.dex` to override an entry point class.
|
||||
* Provides [ReorganizeDexStep] with the dex through the [IDexProvider] implementation.
|
||||
*/
|
||||
@Stable
|
||||
class DownloadInjectorStep : DownloadStep(), KoinComponent {
|
||||
class DownloadInjectorStep : DownloadStep(), IDexProvider, KoinComponent {
|
||||
private val paths: PathManager by inject()
|
||||
|
||||
/**
|
||||
* Populated from a dependency step ([FetchInfoStep]).
|
||||
* This is used as cache invalidation (ref: [Version.aliucordHash])
|
||||
* This is populated right before the download starts (ref: [execute])
|
||||
*/
|
||||
lateinit var aliucordHash: String
|
||||
lateinit var targetVersion: String
|
||||
private set
|
||||
|
||||
override val localizedName = R.string.patch_step_dl_injector
|
||||
override val targetUrl = URL
|
||||
override val targetFile
|
||||
get() = paths.cachedInjectorDex(aliucordHash)
|
||||
get() = paths.cachedInjectorDex(targetVersion)
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
aliucordHash = container
|
||||
.getStep<FetchInfoStep>()
|
||||
.data.aliucordHash
|
||||
targetVersion = container.getStep<FetchInfoStep>()
|
||||
.data.injectorVersion
|
||||
|
||||
super.execute(container)
|
||||
}
|
||||
|
|
@ -43,4 +43,8 @@ class DownloadInjectorStep : DownloadStep(), KoinComponent {
|
|||
|
||||
const val URL = "https://raw.githubusercontent.com/$ORG/$MAIN_REPO/builds/Injector.dex"
|
||||
}
|
||||
|
||||
override val dexCount = 1
|
||||
override val dexPriority = 10
|
||||
override fun getDexFiles() = listOf(targetFile.readBytes())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@ import androidx.compose.runtime.Stable
|
|||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.manager.PathManager
|
||||
import com.aliucord.manager.patcher.steps.base.DownloadStep
|
||||
import com.aliucord.manager.patcher.steps.base.IDexProvider
|
||||
import com.aliucord.manager.patcher.steps.patch.ReorganizeDexStep
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
/**
|
||||
* Download the most recent available Kotlin stdlib build that is supported.
|
||||
* Provides [ReorganizeDexStep] with the dex through the [IDexProvider] implementation.
|
||||
*/
|
||||
@Stable
|
||||
class DownloadKotlinStep : DownloadStep(), KoinComponent {
|
||||
class DownloadKotlinStep : DownloadStep(), IDexProvider, KoinComponent {
|
||||
private val paths: PathManager by inject()
|
||||
|
||||
override val localizedName = R.string.patch_step_dl_kotlin
|
||||
|
|
@ -24,4 +27,8 @@ class DownloadKotlinStep : DownloadStep(), KoinComponent {
|
|||
|
||||
const val URL = "https://raw.githubusercontent.com/$ORG/$MAIN_REPO/main/installer/android/app/src/main/assets/kotlin/classes.dex"
|
||||
}
|
||||
|
||||
override val dexCount = 1
|
||||
override val dexPriority = -1
|
||||
override fun getDexFiles() = listOf(targetFile.readBytes())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.os.Build
|
|||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.patcher.StepRunner
|
||||
import com.aliucord.manager.patcher.steps.StepGroup
|
||||
import com.aliucord.manager.patcher.steps.base.IDexProvider
|
||||
import com.aliucord.manager.patcher.steps.base.Step
|
||||
import com.aliucord.manager.patcher.steps.download.CopyDependenciesStep
|
||||
import com.aliucord.manager.patcher.steps.download.DownloadAliuhookStep
|
||||
|
|
@ -12,23 +13,18 @@ import com.github.diamondminer88.zip.ZipWriter
|
|||
import org.koin.core.component.KoinComponent
|
||||
|
||||
/**
|
||||
* Add the Aliuhook library's native libs along with dex
|
||||
* Add the Aliuhook library's native libs.
|
||||
* The dex is handled by [ReorganizeDexStep] through the [IDexProvider] implementation of [DownloadAliuhookStep].
|
||||
*/
|
||||
class AddAliuhookStep : Step(), KoinComponent {
|
||||
private val currentDeviceArch = Build.SUPPORTED_ABIS.first()
|
||||
|
||||
class AddAliuhookLibsStep : Step(), KoinComponent {
|
||||
override val group = StepGroup.Patch
|
||||
override val localizedName = R.string.patch_step_add_aliuhook
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
val currentDeviceArch = Build.SUPPORTED_ABIS.first()
|
||||
val apk = container.getStep<CopyDependenciesStep>().patchedApk
|
||||
val aliuhook = container.getStep<DownloadAliuhookStep>().targetFile
|
||||
|
||||
// Find the amount of .dex files in the apk
|
||||
val dexCount = ZipReader(apk).use {
|
||||
it.entryNames.count { name -> name.endsWith(".dex") }
|
||||
}
|
||||
|
||||
ZipWriter(apk, /* append = */ true).use { patchedApk ->
|
||||
ZipReader(aliuhook).use { aliuhook ->
|
||||
for (libFile in arrayOf("libaliuhook.so", "libc++_shared.so", "liblsplant.so")) {
|
||||
|
|
@ -37,11 +33,6 @@ class AddAliuhookStep : Step(), KoinComponent {
|
|||
|
||||
patchedApk.writeEntry("lib/$currentDeviceArch/$libFile", bytes)
|
||||
}
|
||||
|
||||
val aliuhookDex = aliuhook.openEntry("classes.dex")?.read()
|
||||
?: throw IllegalStateException("No classes.dex in aliuhook aar")
|
||||
|
||||
patchedApk.writeEntry("classes${dexCount + 1}.dex", aliuhookDex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package com.aliucord.manager.patcher.steps.patch
|
||||
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.patcher.StepRunner
|
||||
import com.aliucord.manager.patcher.steps.StepGroup
|
||||
import com.aliucord.manager.patcher.steps.base.Step
|
||||
import com.aliucord.manager.patcher.steps.download.*
|
||||
import com.github.diamondminer88.zip.ZipReader
|
||||
import com.github.diamondminer88.zip.ZipWriter
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
/**
|
||||
* Reorder the existing dex files to add the Aliucord injector as the first `classes.dex` file.
|
||||
*/
|
||||
class AddInjectorStep : Step(), KoinComponent {
|
||||
override val group = StepGroup.Patch
|
||||
override val localizedName = R.string.patch_step_add_injector
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
val apk = container.getStep<CopyDependenciesStep>().patchedApk
|
||||
val injector = container.getStep<DownloadInjectorStep>().targetFile
|
||||
val kotlinStdlib = container.getStep<DownloadKotlinStep>().targetFile
|
||||
|
||||
val (dexCount, firstDexBytes) = ZipReader(apk).use {
|
||||
Pair(
|
||||
// Find the amount of .dex files in apk
|
||||
it.entryNames.count { name -> name.endsWith(".dex") },
|
||||
|
||||
// Get the first dex
|
||||
it.openEntry("classes.dex")?.read()
|
||||
?: throw IllegalStateException("No classes.dex in base apk")
|
||||
)
|
||||
}
|
||||
|
||||
ZipWriter(apk, /* append = */ true).use {
|
||||
// Move copied dex to end of dex list
|
||||
it.deleteEntry("classes.dex")
|
||||
it.writeEntry("classes${dexCount + 1}.dex", firstDexBytes)
|
||||
|
||||
// Add Kotlin & Aliucord's dex
|
||||
it.writeEntry("classes.dex", injector.readBytes())
|
||||
it.writeEntry("classes${dexCount + 2}.dex", kotlinStdlib.readBytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package com.aliucord.manager.patcher.steps.patch
|
||||
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.manager.PathManager
|
||||
import com.aliucord.manager.patcher.StepRunner
|
||||
import com.aliucord.manager.patcher.steps.StepGroup
|
||||
import com.aliucord.manager.patcher.steps.base.IDexProvider
|
||||
import com.aliucord.manager.patcher.steps.base.Step
|
||||
import com.aliucord.manager.patcher.steps.download.CopyDependenciesStep
|
||||
import com.github.diamondminer88.zip.ZipReader
|
||||
import com.github.diamondminer88.zip.ZipWriter
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class ReorganizeDexStep : Step(), KoinComponent {
|
||||
private val paths: PathManager by inject()
|
||||
|
||||
override val group = StepGroup.Patch
|
||||
override val localizedName = R.string.patch_step_reorganize_dex
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
val apk = container.getStep<CopyDependenciesStep>().patchedApk
|
||||
val dexProviders = container.steps
|
||||
.filterIsInstance<IDexProvider>()
|
||||
.sortedByDescending { it.dexPriority }
|
||||
val priorityDexCount = dexProviders
|
||||
.filter { it.dexPriority > 0 }
|
||||
.sumOf { it.dexCount }
|
||||
|
||||
var dexCount = 0
|
||||
|
||||
ZipReader(apk).use { zip ->
|
||||
// Count the amount of dex files currently in the apk
|
||||
dexCount = zip.entryNames.count { it.endsWith(".dex") }
|
||||
|
||||
// Copy all the dex files that need to be moved out of the apk
|
||||
for (idx in 0..<priorityDexCount) {
|
||||
// Not enough dex files to move
|
||||
if (idx + 1 > dexCount) break
|
||||
|
||||
val bytes = zip.openEntry(getDexName(idx))!!.read()
|
||||
val file = paths.patchingWorkingDir().resolve(getDexName(idx))
|
||||
file.writeBytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
ZipWriter(apk, /* append = */ true).use { zip ->
|
||||
// Delete all the old dex files from the apk
|
||||
for (idx in 0..<priorityDexCount) {
|
||||
// Not enough dex files to move
|
||||
if (idx + 1 > dexCount) break
|
||||
|
||||
zip.deleteEntry(getDexName(idx))
|
||||
}
|
||||
|
||||
// Copy all of the high priority dex files to the apk
|
||||
var idx = 0
|
||||
for (dexProvider in dexProviders) {
|
||||
if (dexProvider.dexPriority <= 0) continue
|
||||
|
||||
for (dexBytes in dexProvider.getDexFiles()) {
|
||||
zip.writeEntry(getDexName(idx++), dexBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy back the dex files that were moved out of the apk
|
||||
for (idx in 0..<priorityDexCount) {
|
||||
// Not enough dex files to move
|
||||
if (idx + 1 > dexCount) break
|
||||
|
||||
val file = paths.patchingWorkingDir().resolve(getDexName(idx))
|
||||
val bytes = file.readBytes()
|
||||
zip.writeEntry(getDexName(dexCount + idx), bytes)
|
||||
}
|
||||
|
||||
dexCount += idx
|
||||
|
||||
// Copy the rest of the injected dex files
|
||||
for (dexProvider in dexProviders) {
|
||||
if (dexProvider.dexPriority > 0) continue
|
||||
|
||||
for (dexBytes in dexProvider.getDexFiles()) {
|
||||
zip.writeEntry(getDexName(dexCount++), dexBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun getDexName(idx: Int) = "classes${if (idx == 0) "" else (idx + 1)}.dex"
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ class SaveMetadataStep(private val options: PatchOptions) : Step() {
|
|||
managerVersionName = BuildConfig.VERSION_NAME,
|
||||
customManager = IS_CUSTOM_BUILD,
|
||||
aliuhookVersion = aliuhook.targetVersion,
|
||||
injectorCommitHash = injector.aliucordHash,
|
||||
injectorVersion = injector.targetVersion,
|
||||
)
|
||||
|
||||
ZipWriter(apk, /* append = */ true).use {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class DowngradeCheckStep(private val options: PatchOptions) : Step(), KoinCompon
|
|||
|
||||
val targetVersion = container
|
||||
.getStep<FetchInfoStep>()
|
||||
.data.versionCode.toIntOrNull()
|
||||
.data.discordVersionCode.toIntOrNull()
|
||||
?: throw IllegalArgumentException("Invalid remote target Discord version")
|
||||
|
||||
if (currentVersion > targetVersion) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.aliucord.manager.patcher.steps.prepare
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.network.dto.Version
|
||||
import com.aliucord.manager.network.dto.BuildInfo
|
||||
import com.aliucord.manager.network.service.AliucordGithubService
|
||||
import com.aliucord.manager.network.utils.getOrThrow
|
||||
import com.aliucord.manager.patcher.StepRunner
|
||||
|
|
@ -21,7 +21,8 @@ class FetchInfoStep : Step(), KoinComponent {
|
|||
/**
|
||||
* Fetched data about the latest Aliucord commit and supported Discord version.
|
||||
*/
|
||||
lateinit var data: Version
|
||||
lateinit var data: BuildInfo
|
||||
private set
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
data = github.getDataJson().getOrThrow()
|
||||
|
|
|
|||
|
|
@ -147,11 +147,11 @@ class HomeModel(
|
|||
private fun fetchSupportedVersion() = screenModelScope.launchBlock {
|
||||
github.getDataJson().fold(
|
||||
success = {
|
||||
val versionCode = it.versionCode.toIntOrNull() ?: return@fold
|
||||
val versionCode = it.discordVersionCode.toIntOrNull() ?: return@fold
|
||||
|
||||
supportedVersion = DiscordVersion.Existing(
|
||||
type = DiscordVersion.parseVersionType(versionCode),
|
||||
name = it.versionName.split("-")[0].trim(),
|
||||
name = it.discordVersionName.split("-")[0].trim(),
|
||||
code = versionCode,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@
|
|||
<string name="patch_step_patch_icon">Patching app icon</string>
|
||||
<string name="patch_step_add_injector">Adding Aliucord injector</string>
|
||||
<string name="patch_step_add_aliuhook">Adding Aliuhook library</string>
|
||||
<string name="patch_step_reorganize_dex">Adding .dex files to APK</string>
|
||||
<string name="patch_step_save_metadata">Store metadata</string>
|
||||
<string name="patch_step_alignment">Aligning APKs</string>
|
||||
<string name="patch_step_signing">Signing APKs</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue