VendettaManager/app/src/main/java/dev/beefers/vendetta/manager/installer/util/ManifestPatcher.kt

236 lines
10 KiB
Kotlin

package dev.beefers.vendetta.manager.installer.util
import android.Manifest
import android.os.Build
import pxb.android.axml.AxmlReader
import pxb.android.axml.AxmlVisitor
import pxb.android.axml.AxmlWriter
import pxb.android.axml.NodeVisitor
object ManifestPatcher {
private const val ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android"
private const val USES_CLEARTEXT_TRAFFIC = "usesCleartextTraffic"
private const val DEBUGGABLE = "debuggable"
private const val REQUEST_LEGACY_EXTERNAL_STORAGE = "requestLegacyExternalStorage"
private const val NETWORK_SECURITY_CONFIG = "networkSecurityConfig"
private const val LABEL = "label"
private const val PACKAGE = "package"
private const val COMPILE_SDK_VERSION = "compileSdkVersion"
private const val COMPILE_SDK_VERSION_CODENAME = "compileSdkVersionCodename"
fun patchManifest(
manifestBytes: ByteArray,
packageName: String,
appName: String,
debuggable: Boolean
): ByteArray {
val reader = AxmlReader(manifestBytes)
val writer = AxmlWriter()
reader.accept(object : AxmlVisitor(writer) {
override fun child(ns: String?, name: String?) =
object : ReplaceAttrsVisitor(
super.child(ns, name),
mapOf(
PACKAGE to packageName,
COMPILE_SDK_VERSION to 23,
COMPILE_SDK_VERSION_CODENAME to "6.0-2438415"
)
) {
private var addExternalStoragePerm = false
override fun child(ns: String?, name: String): NodeVisitor {
val nv = super.child(ns, name)
// Add MANAGE_EXTERNAL_STORAGE when necessary
if (addExternalStoragePerm) {
super
.child(null, "uses-permission")
.attr(
ANDROID_NAMESPACE,
"name",
android.R.attr.name,
TYPE_STRING,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
)
addExternalStoragePerm = false
}
return when (name) {
"uses-permission" -> object : NodeVisitor(nv) {
override fun attr(
ns: String?,
name: String?,
resourceId: Int,
type: Int,
value: Any?
) {
if (name != "maxSdkVersion") {
super.attr(ns, name, resourceId, type, value)
}
// Set the add external storage permission to be added after WRITE_EXTERNAL_STORAGE (which is after read)
if (name == "name" && value == Manifest.permission.READ_EXTERNAL_STORAGE) {
addExternalStoragePerm = true
}
}
}
"uses-sdk" -> object : NodeVisitor(nv) {
override fun attr(
ns: String?,
name: String?,
resourceId: Int,
type: Int,
value: Any?
) {
if (name == "targetSdkVersion") {
val version = if (Build.VERSION.SDK_INT >= 31) 30 else 28
super.attr(ns, name, resourceId, type, version)
} else {
super.attr(ns, name, resourceId, type, value)
}
}
}
"application" -> object : ReplaceAttrsVisitor(
nv,
mapOf(
LABEL to appName,
DEBUGGABLE to debuggable,
USES_CLEARTEXT_TRAFFIC to true,
REQUEST_LEGACY_EXTERNAL_STORAGE to true
)
) {
private var addDebuggable = debuggable
private var addLegacyStorage = true
private var addUseClearTextTraffic = true
override fun attr(
ns: String?,
name: String,
resourceId: Int,
type: Int,
value: Any?
) {
if (name == NETWORK_SECURITY_CONFIG) return
super.attr(ns, name, resourceId, type, value)
if (name == REQUEST_LEGACY_EXTERNAL_STORAGE) addLegacyStorage =
false
if (name == DEBUGGABLE) addDebuggable = false
if (name == USES_CLEARTEXT_TRAFFIC) addUseClearTextTraffic =
false
}
override fun child(ns: String?, name: String): NodeVisitor {
val visitor = super.child(ns, name)
return when (name) {
"activity" -> ReplaceAttrsVisitor(
visitor,
mapOf("label" to appName)
)
"provider" -> object : NodeVisitor(visitor) {
override fun attr(
ns: String?,
name: String,
resourceId: Int,
type: Int,
value: Any?
) {
super.attr(
ns,
name,
resourceId,
type,
if (name == "authorities") {
(value as String).replace(
"com.discord",
packageName
)
} else {
value
}
)
}
}
else -> visitor
}
}
override fun end() {
if (addLegacyStorage) super.attr(
ANDROID_NAMESPACE,
REQUEST_LEGACY_EXTERNAL_STORAGE,
-1,
TYPE_INT_BOOLEAN,
1
)
if (addDebuggable) super.attr(
ANDROID_NAMESPACE,
DEBUGGABLE,
-1,
TYPE_INT_BOOLEAN,
1
)
if (addUseClearTextTraffic) super.attr(
ANDROID_NAMESPACE,
USES_CLEARTEXT_TRAFFIC,
-1,
TYPE_INT_BOOLEAN,
1
)
super.end()
}
}
else -> nv
}
}
}
})
return writer.toByteArray()
}
fun renamePackage(
manifestBytes: ByteArray,
packageName: String
): ByteArray {
val reader = AxmlReader(manifestBytes)
val writer = AxmlWriter()
reader.accept(
object : AxmlVisitor(writer) {
override fun child(ns: String?, name: String?): ReplaceAttrsVisitor {
return ReplaceAttrsVisitor(
super.child(ns, name),
mapOf("package" to packageName)
)
}
}
)
return writer.toByteArray()
}
private open class ReplaceAttrsVisitor(
nv: NodeVisitor,
private val attrs: Map<String, Any>
) : NodeVisitor(nv) {
override fun attr(ns: String?, name: String, resourceId: Int, type: Int, value: Any?) {
val replace = attrs.containsKey(name)
val newValue = attrs[name]
super.attr(
ns,
name,
resourceId,
if (newValue is String) TYPE_STRING else type,
if (replace) newValue else value
)
}
}
}