Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: merge branch dev to main #213

Merged
merged 11 commits into from
Aug 14, 2023
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# [13.0.0-dev.3](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0-dev.2...v13.0.0-dev.3) (2023-08-14)


### Bug Fixes

* decode in correct order ([8fb2f2d](https://github.com/ReVanced/revanced-patcher/commit/8fb2f2dc1d3b9b1e9fd13b39485985d2886d52ae))
* only enable logging for ReVanced ([783ccf8](https://github.com/ReVanced/revanced-patcher/commit/783ccf8529f5d16aa463982da6977328306232bb))

# [13.0.0-dev.2](https://github.com/ReVanced/revanced-patcher/compare/v13.0.0-dev.1...v13.0.0-dev.2) (2023-08-12)


### Bug Fixes

* disable correct loggers ([c2d89c6](https://github.com/ReVanced/revanced-patcher/commit/c2d89c622e06e58e5042e1a00ef67cee8a246e53))
* get framework ids to compile resources ([f2cb7ee](https://github.com/ReVanced/revanced-patcher/commit/f2cb7ee7dffa573c31df497cf235a3f5d120f91f))
* set package metadata correctly ([02d6ff1](https://github.com/ReVanced/revanced-patcher/commit/02d6ff15fe87c2352de29749610e9d72db8ba418))

# [13.0.0-dev.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.1...v13.0.0-dev.1) (2023-08-11)


* build(Needs bump)!: Bump dependencies ([d5f89a9](https://github.com/ReVanced/revanced-patcher/commit/d5f89a903f019c199bdb27a50287124fc4b4978e))


### BREAKING CHANGES

* This bump updates smali, a crucial dependency

## [12.1.1](https://github.com/ReVanced/revanced-patcher/compare/v12.1.0...v12.1.1) (2023-08-03)


Expand Down
19 changes: 11 additions & 8 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@ val githubPassword: String = project.findProperty("gpr.key") as? String ?: Syste

repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
credentials {
username = githubUsername
password = githubPassword
google()
listOf("multidexlib2", "apktool").forEach { repo ->
maven {
url = uri("https://maven.pkg.github.com/revanced/$repo")
credentials {
username = githubUsername
password = githubPassword
}
}
}
}

dependencies {
implementation("xpp3:xpp3:1.1.4c")
implementation("app.revanced:smali:2.5.3-a3836654")
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
implementation("app.revanced:apktool-lib:2.8.2-1")
implementation("com.android.tools.smali:smali:3.0.3")
implementation("app.revanced:multidexlib2:3.0.3.r2")
implementation("app.revanced:apktool-lib:2.8.2-3")

implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.22")
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 12.1.1
version = 13.0.0-dev.3
224 changes: 115 additions & 109 deletions src/main/kotlin/app/revanced/patcher/Patcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@ import app.revanced.patcher.patch.*
import brut.androlib.AaptInvoker
import brut.androlib.ApkDecoder
import brut.androlib.Config
import brut.androlib.apk.ApkInfo
import brut.androlib.apk.UsesFramework
import brut.androlib.res.Framework
import brut.androlib.res.ResourcesDecoder
import brut.androlib.res.decoder.AndroidManifestResourceParser
import brut.androlib.res.decoder.ResAttrDecoder
import brut.androlib.res.decoder.XmlPullStreamDecoder
import brut.androlib.res.util.ExtMXSerializer
import brut.androlib.res.util.ExtXmlSerializer
import brut.androlib.res.xml.ResXmlPatcher
import brut.directory.ExtFile
import com.android.tools.smali.dexlib2.Opcodes
import com.android.tools.smali.dexlib2.iface.DexFile
import com.android.tools.smali.dexlib2.writer.io.MemoryDataStore
import lanchon.multidexlib2.BasicDexFileNamer
import lanchon.multidexlib2.DexIO
import lanchon.multidexlib2.MultiDexIO
import org.jf.dexlib2.Opcodes
import org.jf.dexlib2.iface.DexFile
import org.jf.dexlib2.writer.io.MemoryDataStore
import java.io.Closeable
import java.io.File
import java.io.OutputStream
import java.nio.file.Files
import java.util.logging.Level
import java.util.logging.LogManager

internal val NAMER = BasicDexFileNamer()

Expand All @@ -37,23 +38,42 @@ internal val NAMER = BasicDexFileNamer()
* @param options The options for the patcher.
*/
class Patcher(private val options: PatcherOptions) {
val context: PatcherContext

private val logger = options.logger

private val opcodes: Opcodes

private var resourceDecodingMode = ResourceDecodingMode.MANIFEST_ONLY

private var mergeIntegrations = false
val context: PatcherContext

private val config = Config.getDefaultConfig().apply {
useAapt2 = true
aaptPath = options.aaptPath
frameworkDirectory = options.frameworkDirectory
}

init {
// Disable unwanted logging.
LogManager.getLogManager().let { manager ->
manager.getLogger("").level = Level.OFF // Disable root logger.
// Enable only ReVanced logging.
manager.loggerNames
.toList()
.filter { it.startsWith("app.revanced") }
.map { manager.getLogger(it) }
.forEach { it.level = Level.INFO }
}

logger.info("Reading dex files")

// read dex files
val dexFile = MultiDexIO.readDexFile(true, options.inputFile, NAMER, null, null)

// get the opcodes
opcodes = dexFile.opcodes

// finally create patcher context
context = PatcherContext(dexFile.classes.toMutableList(), File(options.resourceCacheDirectory))

Expand Down Expand Up @@ -84,61 +104,61 @@ class Patcher(private val options: PatcherOptions) {
fun save(): PatcherResult {
var resourceFile: File? = null

when (resourceDecodingMode) {
ResourceDecodingMode.FULL -> {
logger.info("Compiling resources")

val cacheDirectory = ExtFile(options.resourceCacheDirectory)
val aaptFile = cacheDirectory.resolve("aapt_temp_file").also {
Files.deleteIfExists(it.toPath())
}.also { resourceFile = it }

try {
AaptInvoker(
config,
context.packageMetadata.apkInfo
).invokeAapt(
aaptFile,
cacheDirectory.resolve("AndroidManifest.xml").also {
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it)
},
cacheDirectory.resolve("res"),
null,
null,
context.packageMetadata.apkInfo.usesFramework.let { usesFramework ->
usesFramework.ids.map { id ->
Framework(config).getFrameworkApk(id, usesFramework.tag)
}.toTypedArray()
}
)
} finally {
cacheDirectory.close()
}
if (resourceDecodingMode == ResourceDecodingMode.FULL) {
logger.info("Compiling resources")

val cacheDirectory = ExtFile(options.resourceCacheDirectory)
val aaptFile = cacheDirectory.resolve("aapt_temp_file").also {
Files.deleteIfExists(it.toPath())
}.also { resourceFile = it }

try {
AaptInvoker(
config,
context.packageMetadata.apkInfo
).invokeAapt(
aaptFile,
cacheDirectory.resolve("AndroidManifest.xml").also {
ResXmlPatcher.fixingPublicAttrsInProviderAttributes(it)
},
cacheDirectory.resolve("res"),
null,
null,
context.packageMetadata.apkInfo.usesFramework.let { usesFramework ->
usesFramework.ids.map { id ->
Framework(config).getFrameworkApk(id, usesFramework.tag)
}.toTypedArray()
}
)
} finally {
cacheDirectory.close()
}
else -> logger.info("Not compiling resources because resource patching is not required")
}

logger.trace("Creating new dex file")
val newDexFile = object : DexFile {
override fun getClasses() = context.bytecodeContext.classes.also { it.replaceClasses() }
override fun getOpcodes() = [email protected]
}

// write modified dex files
logger.info("Writing modified dex files")
val dexFiles = mutableMapOf<String, MemoryDataStore>()
MultiDexIO.writeDexFile(
true, -1, // core count
dexFiles, NAMER, newDexFile, DexIO.DEFAULT_MAX_DEX_POOL_SIZE, null
)

return PatcherResult(
dexFiles.map {
app.revanced.patcher.util.dex.DexFile(it.key, it.value.readAt(0))
},
context.packageMetadata.apkInfo.doNotCompress?.toList(),
resourceFile
)

return mutableMapOf<String, MemoryDataStore>().apply {
MultiDexIO.writeDexFile(
true,
-1, // Defaults to amount of available cores.
this,
NAMER,
object : DexFile {
override fun getClasses() = context.bytecodeContext.classes.also { it.replaceClasses() }
override fun getOpcodes() = [email protected]
},
DexIO.DEFAULT_MAX_DEX_POOL_SIZE,
null
)
}.let { dexFiles ->
PatcherResult(
dexFiles.map {
app.revanced.patcher.util.dex.DexFile(it.key, it.value.readAt(0))
},
context.packageMetadata.apkInfo.doNotCompress?.toList(),
resourceFile
)
}
}

/**
Expand Down Expand Up @@ -178,73 +198,59 @@ class Patcher(private val options: PatcherOptions) {
* @param mode The [ResourceDecodingMode] to use when decoding.
*/
private fun decodeResources(mode: ResourceDecodingMode) {
val extInputFile = ExtFile(options.inputFile)
try {
val resourcesDecoder = ResourcesDecoder(config, extInputFile)
val apkInfo = ApkInfo(ExtFile(options.inputFile)).also { context.packageMetadata.apkInfo = it }

when (mode) {
ResourceDecodingMode.FULL -> {
val outDir = File(options.resourceCacheDirectory)
if (outDir.exists()) {
logger.info("Deleting existing resource cache directory")
if (!outDir.deleteRecursively()) logger.error("Failed to delete existing resource cache directory")
}
// Needed to record uncompressed files.
val apkDecoder = ApkDecoder(config, apkInfo)

outDir.mkdirs()
// Needed to decode resources.
val resourcesDecoder = ResourcesDecoder(config, apkInfo)

try {
when (mode) {
ResourceDecodingMode.FULL -> {
logger.info("Decoding resources")

resourcesDecoder.decodeManifest(outDir)
val outDir = options.recreateResourceCacheDirectory()

resourcesDecoder.decodeResources(outDir)
resourcesDecoder.decodeManifest(outDir)

context.packageMetadata.also {
it.apkInfo = resourcesDecoder.apkInfo
}.apkInfo.doNotCompress = ApkDecoder(config, extInputFile).recordUncompressedFiles(
context.packageMetadata.apkInfo, resourcesDecoder.resFileMapping
)
apkDecoder.recordUncompressedFiles(resourcesDecoder.resFileMapping)

apkInfo.usesFramework = UsesFramework().apply {
ids = resourcesDecoder.resTable.listFramePackages().map { it.id }
}
}
ResourceDecodingMode.MANIFEST_ONLY -> {
logger.info("Decoding AndroidManifest.xml only, because resources are not needed")

// Instead of using resourceDecoder.decodeManifest which decodes the whole file
// use the XmlPullStreamDecoder in order to get necessary information from the manifest
// used below.
XmlPullStreamDecoder(AndroidManifestResourceParser().apply {
attrDecoder = ResAttrDecoder().apply { this.resTable = resourcesDecoder.resTable }
}, ExtMXSerializer().apply {
setProperty(
ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION, " "
)
setProperty(
ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR,
System.getProperty("line.separator")
)
setProperty(
ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING,
"utf-8"
)
setDisabledAttrEscape(true)
}
logger.info("Decoding app manifest")

// Decode manually instead of using resourceDecoder.decodeManifest
// because it does not support decoding to an OutputStream.
XmlPullStreamDecoder(
AndroidManifestResourceParser(resourcesDecoder.resTable),
resourcesDecoder.resXmlSerializer
).decodeManifest(
extInputFile.directory.getFileInput("AndroidManifest.xml"),
apkInfo.apkFile.directory.getFileInput("AndroidManifest.xml"),
// Older Android versions do not support OutputStream.nullOutputStream()
object : OutputStream() { override fun write(b: Int) { /* do nothing */ } }
object : OutputStream() {
override fun write(b: Int) { /* do nothing */
}
}
)
}
}

// Get the package name and version from the manifest using the XmlPullStreamDecoder.
// XmlPullStreamDecoder.decodeManifest() sets metadata.apkInfo.
context.packageMetadata.let { metadata ->
metadata.apkInfo = resourcesDecoder.apkInfo

metadata.packageName = resourcesDecoder.resTable.currentResPackage.name
resourcesDecoder.apkInfo.versionInfo.let {
metadata.packageVersion = it.versionName ?: it.versionCode
// Get the package name and version from the manifest using the XmlPullStreamDecoder.
// XmlPullStreamDecoder.decodeManifest() sets metadata.apkInfo.
context.packageMetadata.let { metadata ->
metadata.packageName = resourcesDecoder.resTable.packageRenamed
apkInfo.versionInfo.let {
metadata.packageVersion = it.versionName ?: it.versionCode
}
}
}
}
} finally {
extInputFile.close()
apkInfo.apkFile.close()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/app/revanced/patcher/PatcherContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import app.revanced.patcher.data.*
import app.revanced.patcher.logging.Logger
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.ClassMerger.merge
import org.jf.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.ClassDef
import java.io.File

data class PatcherContext(
Expand Down
13 changes: 12 additions & 1 deletion src/main/kotlin/app/revanced/patcher/PatcherOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,15 @@ data class PatcherOptions(
internal val aaptPath: String? = null,
internal val frameworkDirectory: String? = null,
internal val logger: Logger = NopLogger
)
) {
fun recreateResourceCacheDirectory() = File(resourceCacheDirectory).also {
if (it.exists()) {
logger.info("Deleting existing resource cache directory")

if (!it.deleteRecursively())
logger.error("Failed to delete existing resource cache directory")
}

it.mkdirs()
}
}
Loading