mirror of
https://github.com/ReVanced/revanced-library.git
synced 2026-01-11 13:56:17 +00:00
chore: Lint code
This commit is contained in:
@@ -26,8 +26,9 @@ object ApkSigner {
|
||||
private val logger = Logger.getLogger(app.revanced.library.ApkSigner::class.java.name)
|
||||
|
||||
init {
|
||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
|
||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,14 +40,15 @@ object ApkSigner {
|
||||
*/
|
||||
fun newPrivateKeyCertificatePair(
|
||||
commonName: String = "ReVanced",
|
||||
validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24)
|
||||
validUntil: Date = Date(System.currentTimeMillis() + 356.days.inWholeMilliseconds * 24),
|
||||
): PrivateKeyCertificatePair {
|
||||
logger.fine("Creating certificate for $commonName")
|
||||
|
||||
// Generate a new key pair.
|
||||
val keyPair = KeyPairGenerator.getInstance("RSA").apply {
|
||||
initialize(4096)
|
||||
}.generateKeyPair()
|
||||
val keyPair =
|
||||
KeyPairGenerator.getInstance("RSA").apply {
|
||||
initialize(4096)
|
||||
}.generateKeyPair()
|
||||
|
||||
var serialNumber: BigInteger
|
||||
do serialNumber = BigInteger.valueOf(SecureRandom().nextLong())
|
||||
@@ -55,22 +57,22 @@ object ApkSigner {
|
||||
val name = X500Name("CN=$commonName")
|
||||
|
||||
// Create a new certificate.
|
||||
val certificate = JcaX509CertificateConverter().getCertificate(
|
||||
X509v3CertificateBuilder(
|
||||
name,
|
||||
serialNumber,
|
||||
Date(System.currentTimeMillis()),
|
||||
validUntil,
|
||||
Locale.ENGLISH,
|
||||
name,
|
||||
SubjectPublicKeyInfo.getInstance(keyPair.public.encoded)
|
||||
).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private))
|
||||
)
|
||||
val certificate =
|
||||
JcaX509CertificateConverter().getCertificate(
|
||||
X509v3CertificateBuilder(
|
||||
name,
|
||||
serialNumber,
|
||||
Date(System.currentTimeMillis()),
|
||||
validUntil,
|
||||
Locale.ENGLISH,
|
||||
name,
|
||||
SubjectPublicKeyInfo.getInstance(keyPair.public.encoded),
|
||||
).build(JcaContentSignerBuilder("SHA256withRSA").build(keyPair.private)),
|
||||
)
|
||||
|
||||
return PrivateKeyCertificatePair(keyPair.private, certificate)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a [PrivateKeyCertificatePair] from a keystore entry.
|
||||
*
|
||||
@@ -87,16 +89,18 @@ object ApkSigner {
|
||||
): PrivateKeyCertificatePair {
|
||||
logger.fine("Reading key and certificate pair from keystore entry $keyStoreEntryAlias")
|
||||
|
||||
if (!keyStore.containsAlias(keyStoreEntryAlias))
|
||||
if (!keyStore.containsAlias(keyStoreEntryAlias)) {
|
||||
throw IllegalArgumentException("Keystore does not contain alias $keyStoreEntryAlias")
|
||||
}
|
||||
|
||||
// Read the private key and certificate from the keystore.
|
||||
|
||||
val privateKey = try {
|
||||
keyStore.getKey(keyStoreEntryAlias, keyStoreEntryPassword.toCharArray()) as PrivateKey
|
||||
} catch (exception: UnrecoverableKeyException) {
|
||||
throw IllegalArgumentException("Invalid password for keystore entry $keyStoreEntryAlias")
|
||||
}
|
||||
val privateKey =
|
||||
try {
|
||||
keyStore.getKey(keyStoreEntryAlias, keyStoreEntryPassword.toCharArray()) as PrivateKey
|
||||
} catch (exception: UnrecoverableKeyException) {
|
||||
throw IllegalArgumentException("Invalid password for keystore entry $keyStoreEntryAlias")
|
||||
}
|
||||
|
||||
val certificate = keyStore.getCertificate(keyStoreEntryAlias) as X509Certificate
|
||||
|
||||
@@ -110,9 +114,7 @@ object ApkSigner {
|
||||
* @return The created keystore.
|
||||
* @see KeyStoreEntry
|
||||
*/
|
||||
fun newKeyStore(
|
||||
entries: List<KeyStoreEntry>
|
||||
): KeyStore {
|
||||
fun newKeyStore(entries: List<KeyStoreEntry>): KeyStore {
|
||||
logger.fine("Creating keystore")
|
||||
|
||||
return KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME).apply {
|
||||
@@ -124,7 +126,7 @@ object ApkSigner {
|
||||
entry.alias,
|
||||
entry.privateKeyCertificatePair.privateKey,
|
||||
entry.password.toCharArray(),
|
||||
arrayOf(entry.privateKeyCertificatePair.certificate)
|
||||
arrayOf(entry.privateKeyCertificatePair.certificate),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -140,10 +142,10 @@ object ApkSigner {
|
||||
fun newKeyStore(
|
||||
keyStoreOutputStream: OutputStream,
|
||||
keyStorePassword: String,
|
||||
entries: List<KeyStoreEntry>
|
||||
entries: List<KeyStoreEntry>,
|
||||
) = newKeyStore(entries).store(
|
||||
keyStoreOutputStream,
|
||||
keyStorePassword.toCharArray()
|
||||
keyStorePassword.toCharArray(),
|
||||
) // Save the keystore.
|
||||
|
||||
/**
|
||||
@@ -156,7 +158,7 @@ object ApkSigner {
|
||||
*/
|
||||
fun readKeyStore(
|
||||
keyStoreInputStream: InputStream,
|
||||
keyStorePassword: String?
|
||||
keyStorePassword: String?,
|
||||
): KeyStore {
|
||||
logger.fine("Reading keystore")
|
||||
|
||||
@@ -164,10 +166,11 @@ object ApkSigner {
|
||||
try {
|
||||
load(keyStoreInputStream, keyStorePassword?.toCharArray())
|
||||
} catch (exception: IOException) {
|
||||
if (exception.cause is UnrecoverableKeyException)
|
||||
if (exception.cause is UnrecoverableKeyException) {
|
||||
throw IllegalArgumentException("Invalid keystore password")
|
||||
else
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,20 +186,21 @@ object ApkSigner {
|
||||
fun newApkSignerBuilder(
|
||||
privateKeyCertificatePair: PrivateKeyCertificatePair,
|
||||
signer: String,
|
||||
createdBy: String
|
||||
createdBy: String,
|
||||
): ApkSigner.Builder {
|
||||
logger.fine(
|
||||
"Creating new ApkSigner " +
|
||||
"with $signer as signer and " +
|
||||
"$createdBy as Created-By attribute in the APK's manifest"
|
||||
"with $signer as signer and " +
|
||||
"$createdBy as Created-By attribute in the APK's manifest",
|
||||
)
|
||||
|
||||
// Create the signer config.
|
||||
val signerConfig = ApkSigner.SignerConfig.Builder(
|
||||
signer,
|
||||
privateKeyCertificatePair.privateKey,
|
||||
listOf(privateKeyCertificatePair.certificate)
|
||||
).build()
|
||||
val signerConfig =
|
||||
ApkSigner.SignerConfig.Builder(
|
||||
signer,
|
||||
privateKeyCertificatePair.privateKey,
|
||||
listOf(privateKeyCertificatePair.certificate),
|
||||
).build()
|
||||
|
||||
// Create the signer.
|
||||
return ApkSigner.Builder(listOf(signerConfig)).apply {
|
||||
@@ -227,10 +231,13 @@ object ApkSigner {
|
||||
) = newApkSignerBuilder(
|
||||
readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword),
|
||||
signer,
|
||||
createdBy
|
||||
createdBy,
|
||||
)
|
||||
|
||||
fun ApkSigner.Builder.signApk(input: File, output: File) {
|
||||
fun ApkSigner.Builder.signApk(
|
||||
input: File,
|
||||
output: File,
|
||||
) {
|
||||
logger.info("Signing ${input.name}")
|
||||
|
||||
setInputApk(input)
|
||||
@@ -250,7 +257,7 @@ object ApkSigner {
|
||||
class KeyStoreEntry(
|
||||
val alias: String,
|
||||
val password: String,
|
||||
val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair()
|
||||
val privateKeyCertificatePair: PrivateKeyCertificatePair = newPrivateKeyCertificatePair(),
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,11 @@ object ApkUtils {
|
||||
* @param outputFile The apk to write the new entries to.
|
||||
* @param patchedEntriesSource The result of the patcher to add the patched dex files and resources.
|
||||
*/
|
||||
fun copyAligned(apkFile: File, outputFile: File, patchedEntriesSource: PatcherResult) {
|
||||
fun copyAligned(
|
||||
apkFile: File,
|
||||
outputFile: File,
|
||||
patchedEntriesSource: PatcherResult,
|
||||
) {
|
||||
logger.info("Aligning ${apkFile.name}")
|
||||
|
||||
outputFile.toPath().deleteIfExists()
|
||||
@@ -30,13 +34,15 @@ object ApkUtils {
|
||||
ZipFile(outputFile).use { file ->
|
||||
patchedEntriesSource.dexFiles.forEach {
|
||||
file.addEntryCompressData(
|
||||
ZipEntry(it.name), it.stream.readBytes()
|
||||
ZipEntry(it.name),
|
||||
it.stream.readBytes(),
|
||||
)
|
||||
}
|
||||
|
||||
patchedEntriesSource.resourceFile?.let {
|
||||
file.copyEntriesFromFileAligned(
|
||||
ZipFile(it), ZipFile.apkZipEntryAlignment
|
||||
ZipFile(it),
|
||||
ZipFile.apkZipEntryAlignment,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -44,7 +50,8 @@ object ApkUtils {
|
||||
|
||||
// TODO: Fix copying resources that are not needed anymore.
|
||||
file.copyEntriesFromFileAligned(
|
||||
ZipFile(apkFile), ZipFile.apkZipEntryAlignment
|
||||
ZipFile(apkFile),
|
||||
ZipFile.apkZipEntryAlignment,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -62,26 +69,27 @@ object ApkUtils {
|
||||
signingOptions: SigningOptions,
|
||||
) {
|
||||
// Get the keystore from the file or create a new one.
|
||||
val keyStore = if (signingOptions.keyStore.exists()) {
|
||||
ApkSigner.readKeyStore(signingOptions.keyStore.inputStream(), signingOptions.keyStorePassword)
|
||||
} else {
|
||||
val entry = ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password)
|
||||
val keyStore =
|
||||
if (signingOptions.keyStore.exists()) {
|
||||
ApkSigner.readKeyStore(signingOptions.keyStore.inputStream(), signingOptions.keyStorePassword)
|
||||
} else {
|
||||
val entry = ApkSigner.KeyStoreEntry(signingOptions.alias, signingOptions.password)
|
||||
|
||||
// Create a new keystore with a new keypair and saves it.
|
||||
ApkSigner.newKeyStore(listOf(entry)).also { keyStore ->
|
||||
keyStore.store(
|
||||
signingOptions.keyStore.outputStream(),
|
||||
signingOptions.keyStorePassword?.toCharArray()
|
||||
)
|
||||
// Create a new keystore with a new keypair and saves it.
|
||||
ApkSigner.newKeyStore(listOf(entry)).also { keyStore ->
|
||||
keyStore.store(
|
||||
signingOptions.keyStore.outputStream(),
|
||||
signingOptions.keyStorePassword?.toCharArray(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApkSigner.newApkSignerBuilder(
|
||||
keyStore,
|
||||
signingOptions.alias,
|
||||
signingOptions.password,
|
||||
signingOptions.signer,
|
||||
signingOptions.signer
|
||||
signingOptions.signer,
|
||||
).signApk(apk, output)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
package app.revanced.library
|
||||
|
||||
|
||||
import app.revanced.library.Options.Patch.Option
|
||||
import app.revanced.patcher.PatchClass
|
||||
import app.revanced.patcher.PatchSet
|
||||
@@ -25,31 +24,37 @@ object Options {
|
||||
* @param prettyPrint Whether to pretty print the JSON.
|
||||
* @return The JSON string containing the options.
|
||||
*/
|
||||
fun serialize(patches: PatchSet, prettyPrint: Boolean = false): String = patches
|
||||
.filter { it.options.any() }
|
||||
.map { patch ->
|
||||
Patch(
|
||||
patch.name!!,
|
||||
patch.options.values.map { option ->
|
||||
val optionValue = try {
|
||||
option.value
|
||||
} catch (e: PatchOptionException) {
|
||||
logger.warning("Using default option value for the ${patch.name} patch: ${e.message}")
|
||||
option.default
|
||||
}
|
||||
fun serialize(
|
||||
patches: PatchSet,
|
||||
prettyPrint: Boolean = false,
|
||||
): String =
|
||||
patches
|
||||
.filter { it.options.any() }
|
||||
.map { patch ->
|
||||
Patch(
|
||||
patch.name!!,
|
||||
patch.options.values.map { option ->
|
||||
val optionValue =
|
||||
try {
|
||||
option.value
|
||||
} catch (e: PatchOptionException) {
|
||||
logger.warning("Using default option value for the ${patch.name} patch: ${e.message}")
|
||||
option.default
|
||||
}
|
||||
|
||||
Option(option.key, optionValue)
|
||||
Option(option.key, optionValue)
|
||||
},
|
||||
)
|
||||
}
|
||||
// See https://github.com/revanced/revanced-patches/pull/2434/commits/60e550550b7641705e81aa72acfc4faaebb225e7.
|
||||
.distinctBy { it.patchName }
|
||||
.let {
|
||||
if (prettyPrint) {
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(it)
|
||||
} else {
|
||||
mapper.writeValueAsString(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
// See https://github.com/revanced/revanced-patches/pull/2434/commits/60e550550b7641705e81aa72acfc4faaebb225e7.
|
||||
.distinctBy { it.patchName }
|
||||
.let {
|
||||
if (prettyPrint)
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(it)
|
||||
else
|
||||
mapper.writeValueAsString(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the options for the patches in the list.
|
||||
@@ -70,9 +75,10 @@ object Options {
|
||||
filter { it.options.any() }.let { patches ->
|
||||
if (patches.isEmpty()) return
|
||||
|
||||
val jsonPatches = deserialize(json).associate {
|
||||
it.patchName to it.options.associate { option -> option.key to option.value }
|
||||
}
|
||||
val jsonPatches =
|
||||
deserialize(json).associate {
|
||||
it.patchName to it.options.associate { option -> option.key to option.value }
|
||||
}
|
||||
|
||||
patches.forEach { patch ->
|
||||
jsonPatches[patch.name]?.let { jsonPatchOptions ->
|
||||
@@ -104,9 +110,8 @@ object Options {
|
||||
*/
|
||||
class Patch internal constructor(
|
||||
val patchName: String,
|
||||
val options: List<Option>
|
||||
val options: List<Option>,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Data class for patch option.
|
||||
*
|
||||
|
||||
@@ -14,7 +14,10 @@ object PatchUtils {
|
||||
* @param packageName The name of the compatible package.
|
||||
* @return The most common version of.
|
||||
*/
|
||||
fun getMostCommonCompatibleVersion(patches: PatchSet, packageName: String) = patches
|
||||
fun getMostCommonCompatibleVersion(
|
||||
patches: PatchSet,
|
||||
packageName: String,
|
||||
) = patches
|
||||
.mapNotNull {
|
||||
// Map all patches to their compatible packages with version constraints.
|
||||
it.compatiblePackages?.firstOrNull { compatiblePackage ->
|
||||
|
||||
@@ -29,15 +29,16 @@ import java.util.logging.Logger
|
||||
sealed class AdbManager private constructor(deviceSerial: String?) {
|
||||
protected val logger: Logger = Logger.getLogger(AdbManager::class.java.name)
|
||||
|
||||
protected val device = with(JadbConnection().devices) {
|
||||
if (isEmpty()) throw DeviceNotFoundException()
|
||||
protected val device =
|
||||
with(JadbConnection().devices) {
|
||||
if (isEmpty()) throw DeviceNotFoundException()
|
||||
|
||||
deviceSerial?.let {
|
||||
firstOrNull { it.serial == deviceSerial } ?: throw DeviceNotFoundException(deviceSerial)
|
||||
} ?: first().also {
|
||||
logger.warning("No device serial supplied. Using device with serial ${it.serial}")
|
||||
}
|
||||
}!!
|
||||
deviceSerial?.let {
|
||||
firstOrNull { it.serial == deviceSerial } ?: throw DeviceNotFoundException(deviceSerial)
|
||||
} ?: first().also {
|
||||
logger.warning("No device serial supplied. Using device with serial ${it.serial}")
|
||||
}
|
||||
}!!
|
||||
|
||||
init {
|
||||
logger.fine("Connected to ${device.serial}")
|
||||
@@ -70,8 +71,10 @@ sealed class AdbManager private constructor(deviceSerial: String?) {
|
||||
* @return The [AdbManager].
|
||||
* @throws DeviceNotFoundException If the device can not be found.
|
||||
*/
|
||||
fun getAdbManager(deviceSerial: String? = null, root: Boolean = false): AdbManager =
|
||||
if (root) RootAdbManager(deviceSerial) else UserAdbManager(deviceSerial)
|
||||
fun getAdbManager(
|
||||
deviceSerial: String? = null,
|
||||
root: Boolean = false,
|
||||
): AdbManager = if (root) RootAdbManager(deviceSerial) else UserAdbManager(deviceSerial)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +126,11 @@ sealed class AdbManager private constructor(deviceSerial: String?) {
|
||||
}
|
||||
|
||||
companion object Utils {
|
||||
private fun JadbDevice.run(command: String, with: String) = run(command.applyReplacement(with))
|
||||
private fun JadbDevice.run(
|
||||
command: String,
|
||||
with: String,
|
||||
) = run(command.applyReplacement(with))
|
||||
|
||||
private fun String.applyReplacement(with: String) = replace(PLACEHOLDER, with)
|
||||
}
|
||||
}
|
||||
@@ -162,9 +169,11 @@ sealed class AdbManager private constructor(deviceSerial: String?) {
|
||||
class Apk(val file: File, val packageName: String? = null)
|
||||
|
||||
class DeviceNotFoundException internal constructor(deviceSerial: String? = null) :
|
||||
Exception(deviceSerial?.let {
|
||||
"The device with the ADB device serial \"$deviceSerial\" can not be found"
|
||||
} ?: "No ADB device found")
|
||||
Exception(
|
||||
deviceSerial?.let {
|
||||
"The device with the ADB device serial \"$deviceSerial\" can not be found"
|
||||
} ?: "No ADB device found",
|
||||
)
|
||||
|
||||
class FailedToFindInstalledPackageException internal constructor(packageName: String) :
|
||||
Exception("Failed to find installed package \"$packageName\" because no activity was found")
|
||||
|
||||
@@ -5,8 +5,10 @@ import se.vidstige.jadb.RemoteFile
|
||||
import se.vidstige.jadb.ShellProcessBuilder
|
||||
import java.io.File
|
||||
|
||||
|
||||
internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder {
|
||||
internal fun JadbDevice.buildCommand(
|
||||
command: String,
|
||||
su: Boolean = true,
|
||||
): ShellProcessBuilder {
|
||||
if (su) return shellProcessBuilder("su -c \'$command\'")
|
||||
|
||||
val args = command.split(" ") as ArrayList<String>
|
||||
@@ -15,14 +17,19 @@ internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): Shell
|
||||
return shellProcessBuilder(cmd, *args.toTypedArray())
|
||||
}
|
||||
|
||||
internal fun JadbDevice.run(command: String, su: Boolean = true) =
|
||||
this.buildCommand(command, su).start()
|
||||
internal fun JadbDevice.run(
|
||||
command: String,
|
||||
su: Boolean = true,
|
||||
) = this.buildCommand(command, su).start()
|
||||
|
||||
internal fun JadbDevice.hasSu() =
|
||||
this.run("whoami", true).waitFor() == 0
|
||||
internal fun JadbDevice.hasSu() = this.run("whoami", true).waitFor() == 0
|
||||
|
||||
internal fun JadbDevice.push(file: File, targetFilePath: String) =
|
||||
push(file, RemoteFile(targetFilePath))
|
||||
internal fun JadbDevice.push(
|
||||
file: File,
|
||||
targetFilePath: String,
|
||||
) = push(file, RemoteFile(targetFilePath))
|
||||
|
||||
internal fun JadbDevice.createFile(targetFile: String, content: String) =
|
||||
push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))
|
||||
internal fun JadbDevice.createFile(
|
||||
targetFile: String,
|
||||
content: String,
|
||||
) = push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))
|
||||
|
||||
@@ -13,7 +13,8 @@ internal object Constants {
|
||||
internal const val RESTART = "am start -S $PLACEHOLDER"
|
||||
internal const val GET_INSTALLED_PATH = "pm path $PLACEHOLDER"
|
||||
|
||||
internal const val INSTALL_PATCHED_APK = "base_path=\"$PATCHED_APK_PATH\" && " +
|
||||
internal const val INSTALL_PATCHED_APK =
|
||||
"base_path=\"$PATCHED_APK_PATH\" && " +
|
||||
"mv $TMP_PATH ${'$'}base_path && " +
|
||||
"chmod 644 ${'$'}base_path && " +
|
||||
"chown system:system ${'$'}base_path && " +
|
||||
@@ -26,17 +27,17 @@ internal object Constants {
|
||||
|
||||
internal val MOUNT_SCRIPT =
|
||||
"""
|
||||
#!/system/bin/sh
|
||||
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
|
||||
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
|
||||
#!/system/bin/sh
|
||||
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
|
||||
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
|
||||
|
||||
until [ "${'$'}(getprop sys.boot_completed)" = 1 ]; do sleep 3; done
|
||||
until [ -d "/sdcard/Android" ]; do sleep 1; done
|
||||
until [ "${'$'}(getprop sys.boot_completed)" = 1 ]; do sleep 3; done
|
||||
until [ -d "/sdcard/Android" ]; do sleep 1; done
|
||||
|
||||
base_path="$PATCHED_APK_PATH"
|
||||
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
|
||||
base_path="$PATCHED_APK_PATH"
|
||||
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
|
||||
|
||||
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
|
||||
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
|
||||
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
|
||||
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ object Logger {
|
||||
/**
|
||||
* Rules for allowed loggers.
|
||||
*/
|
||||
private val allowedLoggersRules = arrayOf<String.() -> Boolean>(
|
||||
{ startsWith("app.revanced") }, // ReVanced loggers.
|
||||
{ this == "" } // Logs warnings when compiling resources (Logger in class brut.util.OS).
|
||||
)
|
||||
private val allowedLoggersRules =
|
||||
arrayOf<String.() -> Boolean>(
|
||||
{ startsWith("app.revanced") }, // ReVanced loggers.
|
||||
{ this == "" }, // Logs warnings when compiling resources (Logger in class brut.util.OS).
|
||||
)
|
||||
|
||||
private val rootLogger = java.util.logging.Logger.getLogger("")
|
||||
|
||||
@@ -48,13 +49,14 @@ object Logger {
|
||||
fun addHandler(
|
||||
publishHandler: (log: String, level: Level, loggerName: String?) -> Unit,
|
||||
flushHandler: () -> Unit,
|
||||
closeHandler: () -> Unit
|
||||
closeHandler: () -> Unit,
|
||||
) = object : Handler() {
|
||||
override fun publish(record: LogRecord) = publishHandler(
|
||||
formatter.format(record),
|
||||
record.level,
|
||||
record.loggerName
|
||||
)
|
||||
override fun publish(record: LogRecord) =
|
||||
publishHandler(
|
||||
formatter.format(record),
|
||||
record.level,
|
||||
record.loggerName,
|
||||
)
|
||||
|
||||
override fun flush() = flushHandler()
|
||||
|
||||
@@ -77,10 +79,11 @@ object Logger {
|
||||
}
|
||||
|
||||
log.toByteArray().let {
|
||||
if (level.intValue() > Level.WARNING.intValue())
|
||||
if (level.intValue() > Level.WARNING.intValue()) {
|
||||
System.err.write(it)
|
||||
else
|
||||
} else {
|
||||
System.out.write(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,25 +9,34 @@ internal fun UInt.toLittleEndian() =
|
||||
|
||||
internal fun UShort.toLittleEndian() = (this.toUInt() shl 16).toLittleEndian().toUShort()
|
||||
|
||||
internal fun UInt.toBigEndian() = (((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
|
||||
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)).toUInt()
|
||||
internal fun UInt.toBigEndian() =
|
||||
(
|
||||
((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
|
||||
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)
|
||||
).toUInt()
|
||||
|
||||
internal fun UShort.toBigEndian() = (this.toUInt() shl 16).toBigEndian().toUShort()
|
||||
|
||||
internal fun ByteBuffer.getUShort() = this.getShort().toUShort()
|
||||
|
||||
internal fun ByteBuffer.getUInt() = this.getInt().toUInt()
|
||||
|
||||
internal fun ByteBuffer.putUShort(ushort: UShort) = this.putShort(ushort.toShort())
|
||||
|
||||
internal fun ByteBuffer.putUInt(uint: UInt) = this.putInt(uint.toInt())
|
||||
|
||||
internal fun DataInput.readUShort() = this.readShort().toUShort()
|
||||
|
||||
internal fun DataInput.readUInt() = this.readInt().toUInt()
|
||||
|
||||
internal fun DataOutput.writeUShort(ushort: UShort) = this.writeShort(ushort.toInt())
|
||||
|
||||
internal fun DataOutput.writeUInt(uint: UInt) = this.writeInt(uint.toInt())
|
||||
|
||||
internal fun DataInput.readUShortLE() = this.readUShort().toBigEndian()
|
||||
|
||||
internal fun DataInput.readUIntLE() = this.readUInt().toBigEndian()
|
||||
|
||||
internal fun DataOutput.writeUShortLE(ushort: UShort) = this.writeUShort(ushort.toLittleEndian())
|
||||
|
||||
internal fun DataOutput.writeUIntLE(uint: UInt) = this.writeUInt(uint.toLittleEndian())
|
||||
|
||||
@@ -14,10 +14,11 @@ class ZipFile(file: File) : Closeable {
|
||||
private var entries: MutableList<ZipEntry> = mutableListOf()
|
||||
|
||||
// Open file for writing if it doesn't exist (because the intention is to write) or is writable.
|
||||
private val filePointer: RandomAccessFile = RandomAccessFile(
|
||||
file,
|
||||
if (!file.exists() || file.canWrite()) "rw" else "r"
|
||||
)
|
||||
private val filePointer: RandomAccessFile =
|
||||
RandomAccessFile(
|
||||
file,
|
||||
if (!file.exists() || file.canWrite()) "rw" else "r",
|
||||
)
|
||||
|
||||
private var centralDirectoryNeedsRewrite = false
|
||||
|
||||
@@ -28,8 +29,9 @@ class ZipFile(file: File) : Closeable {
|
||||
if (file.length() > 0) {
|
||||
val endRecord = findEndRecord()
|
||||
|
||||
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries)
|
||||
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries) {
|
||||
throw IllegalArgumentException("Multi-file archives are not supported")
|
||||
}
|
||||
|
||||
entries = readEntries(endRecord).toMutableList()
|
||||
}
|
||||
@@ -66,16 +68,17 @@ class ZipFile(file: File) : Closeable {
|
||||
for (i in 1..numberOfEntries) {
|
||||
add(
|
||||
ZipEntry.fromCDE(filePointer).also
|
||||
{
|
||||
//for some reason the local extra field can be different from the central one
|
||||
it.readLocalExtra(
|
||||
filePointer.channel.map(
|
||||
FileChannel.MapMode.READ_ONLY,
|
||||
it.localHeaderOffset.toLong() + 28,
|
||||
2
|
||||
{
|
||||
// for some reason the local extra field can be different from the central one
|
||||
it.readLocalExtra(
|
||||
filePointer.channel.map(
|
||||
FileChannel.MapMode.READ_ONLY,
|
||||
it.localHeaderOffset.toLong() + 28,
|
||||
2,
|
||||
),
|
||||
)
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,20 +92,24 @@ class ZipFile(file: File) : Closeable {
|
||||
|
||||
val entriesCount = entries.size.toUShort()
|
||||
|
||||
val endRecord = ZipEndRecord(
|
||||
0u,
|
||||
0u,
|
||||
entriesCount,
|
||||
entriesCount,
|
||||
filePointer.channel.position().toUInt() - centralDirectoryStartOffset,
|
||||
centralDirectoryStartOffset,
|
||||
""
|
||||
)
|
||||
val endRecord =
|
||||
ZipEndRecord(
|
||||
0u,
|
||||
0u,
|
||||
entriesCount,
|
||||
entriesCount,
|
||||
filePointer.channel.position().toUInt() - centralDirectoryStartOffset,
|
||||
centralDirectoryStartOffset,
|
||||
"",
|
||||
)
|
||||
|
||||
filePointer.channel.write(endRecord.toECD())
|
||||
}
|
||||
|
||||
private fun addEntry(entry: ZipEntry, data: ByteBuffer) {
|
||||
private fun addEntry(
|
||||
entry: ZipEntry,
|
||||
data: ByteBuffer,
|
||||
) {
|
||||
centralDirectoryNeedsRewrite = true
|
||||
|
||||
entry.localHeaderOffset = filePointer.channel.position().toUInt()
|
||||
@@ -113,7 +120,10 @@ class ZipFile(file: File) : Closeable {
|
||||
entries.add(entry)
|
||||
}
|
||||
|
||||
fun addEntryCompressData(entry: ZipEntry, data: ByteArray) {
|
||||
fun addEntryCompressData(
|
||||
entry: ZipEntry,
|
||||
data: ByteArray,
|
||||
) {
|
||||
val compressor = Deflater(compressionLevel, true)
|
||||
compressor.setInput(data)
|
||||
compressor.finish()
|
||||
@@ -138,7 +148,11 @@ class ZipFile(file: File) : Closeable {
|
||||
addEntry(entry, compressedBuffer)
|
||||
}
|
||||
|
||||
private fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) {
|
||||
private fun addEntryCopyData(
|
||||
entry: ZipEntry,
|
||||
data: ByteBuffer,
|
||||
alignment: Int? = null,
|
||||
) {
|
||||
alignment?.let {
|
||||
// Calculate where data would end up.
|
||||
val dataOffset = filePointer.filePointer + entry.LFHSize
|
||||
@@ -160,7 +174,7 @@ class ZipFile(file: File) : Closeable {
|
||||
return filePointer.channel.map(
|
||||
FileChannel.MapMode.READ_ONLY,
|
||||
entry.dataOffset.toLong(),
|
||||
entry.compressedSize.toLong()
|
||||
entry.compressedSize.toLong(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -170,7 +184,10 @@ class ZipFile(file: File) : Closeable {
|
||||
* @param file The file to copy entries from.
|
||||
* @param entryAlignment A function that returns the alignment for a given entry.
|
||||
*/
|
||||
fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) {
|
||||
fun copyEntriesFromFileAligned(
|
||||
file: ZipFile,
|
||||
entryAlignment: (entry: ZipEntry) -> Int?,
|
||||
) {
|
||||
for (entry in file.entries) {
|
||||
if (entries.any { it.fileName == entry.fileName }) continue // Skip duplicates
|
||||
|
||||
@@ -189,9 +206,13 @@ class ZipFile(file: File) : Closeable {
|
||||
private const val LIBRARY_ALIGNMENT = 4096
|
||||
|
||||
val apkZipEntryAlignment = { entry: ZipEntry ->
|
||||
if (entry.compression.toUInt() != 0u) null
|
||||
else if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT
|
||||
else DEFAULT_ALIGNMENT
|
||||
if (entry.compression.toUInt() != 0u) {
|
||||
null
|
||||
} else if (entry.fileName.endsWith(".so")) {
|
||||
LIBRARY_ALIGNMENT
|
||||
} else {
|
||||
DEFAULT_ALIGNMENT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ internal class ZipEndRecord(
|
||||
val centralDirectoryStartOffset: UInt,
|
||||
val fileComment: String,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val ECD_HEADER_SIZE = 22
|
||||
const val ECD_SIGNATURE = 0x06054b50u
|
||||
@@ -25,8 +24,9 @@ internal class ZipEndRecord(
|
||||
fun fromECD(input: DataInput): ZipEndRecord {
|
||||
val signature = input.readUIntLE()
|
||||
|
||||
if (signature != ECD_SIGNATURE)
|
||||
if (signature != ECD_SIGNATURE) {
|
||||
throw IllegalArgumentException("Input doesn't start with end record signature")
|
||||
}
|
||||
|
||||
val diskNumber = input.readUShortLE()
|
||||
val startingDiskNumber = input.readUShortLE()
|
||||
@@ -50,7 +50,7 @@ internal class ZipEndRecord(
|
||||
totalEntries,
|
||||
centralDirectorySize,
|
||||
centralDirectoryStartOffset,
|
||||
fileComment
|
||||
fileComment,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class ZipEntry private constructor(
|
||||
internal val fileName: String,
|
||||
internal val extraField: ByteArray,
|
||||
internal val fileComment: String,
|
||||
internal var localExtraField: ByteArray = ByteArray(0), //separate for alignment
|
||||
internal var localExtraField: ByteArray = ByteArray(0), // separate for alignment
|
||||
) {
|
||||
internal val LFHSize: Int
|
||||
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
|
||||
@@ -31,12 +31,12 @@ class ZipEntry private constructor(
|
||||
get() = localHeaderOffset + LFHSize.toUInt()
|
||||
|
||||
constructor(fileName: String) : this(
|
||||
0x1403u, //made by unix, version 20
|
||||
0x1403u, // made by unix, version 20
|
||||
0u,
|
||||
0u,
|
||||
0u,
|
||||
0x0821u, //seems to be static time google uses, no idea
|
||||
0x0221u, //same as above
|
||||
0x0821u, // seems to be static time google uses, no idea
|
||||
0x0221u, // same as above
|
||||
0u,
|
||||
0u,
|
||||
0u,
|
||||
@@ -46,21 +46,22 @@ class ZipEntry private constructor(
|
||||
0u,
|
||||
fileName,
|
||||
ByteArray(0),
|
||||
""
|
||||
"",
|
||||
)
|
||||
|
||||
companion object {
|
||||
internal const val CDE_HEADER_SIZE = 46
|
||||
internal const val CDE_SIGNATURE = 0x02014b50u
|
||||
|
||||
internal const val LFH_HEADER_SIZE = 30
|
||||
internal const val LFH_HEADER_SIZE = 30
|
||||
internal const val LFH_SIGNATURE = 0x04034b50u
|
||||
|
||||
internal fun fromCDE(input: DataInput): ZipEntry {
|
||||
val signature = input.readUIntLE()
|
||||
|
||||
if (signature != CDE_SIGNATURE)
|
||||
if (signature != CDE_SIGNATURE) {
|
||||
throw IllegalArgumentException("Input doesn't start with central directory entry signature")
|
||||
}
|
||||
|
||||
val version = input.readUShortLE()
|
||||
val versionNeeded = input.readUShortLE()
|
||||
@@ -97,8 +98,11 @@ class ZipEntry private constructor(
|
||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
flags = (flags and 0b1000u.inv()
|
||||
.toUShort()) //disable data descriptor flag as they are not used
|
||||
flags = (
|
||||
flags and
|
||||
0b1000u.inv()
|
||||
.toUShort()
|
||||
) // disable data descriptor flag as they are not used
|
||||
|
||||
return ZipEntry(
|
||||
version,
|
||||
@@ -121,7 +125,7 @@ class ZipEntry private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun readLocalExtra(buffer: ByteBuffer) {
|
||||
internal fun readLocalExtra(buffer: ByteBuffer) {
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN)
|
||||
localExtraField = ByteArray(buffer.getUShort().toInt())
|
||||
}
|
||||
@@ -129,8 +133,9 @@ class ZipEntry private constructor(
|
||||
internal fun toLFH(): ByteBuffer {
|
||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
||||
|
||||
val buffer = ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
|
||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
||||
val buffer =
|
||||
ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
|
||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
||||
|
||||
buffer.putUInt(LFH_SIGNATURE)
|
||||
buffer.putUShort(versionNeeded)
|
||||
|
||||
@@ -10,9 +10,10 @@ import kotlin.test.assertEquals
|
||||
internal object PatchUtilsTest {
|
||||
@Test
|
||||
fun `return 'a' because it is the most common version`() {
|
||||
val patches = arrayOf("a", "a", "c", "d", "a", "b", "c", "d", "a", "b", "c", "d")
|
||||
.map { version -> newPatch("some.package", version) }
|
||||
.toSet()
|
||||
val patches =
|
||||
arrayOf("a", "a", "c", "d", "a", "b", "c", "d", "a", "b", "c", "d")
|
||||
.map { version -> newPatch("some.package", version) }
|
||||
.toSet()
|
||||
|
||||
assertEqualsVersion("a", patches, "some.package")
|
||||
}
|
||||
@@ -31,19 +32,25 @@ internal object PatchUtilsTest {
|
||||
|
||||
@Test
|
||||
fun `return null because no patch compatible package is constrained to a version`() {
|
||||
val patches = setOf(
|
||||
newPatch("other.package"),
|
||||
newPatch("other.package"),
|
||||
)
|
||||
val patches =
|
||||
setOf(
|
||||
newPatch("other.package"),
|
||||
newPatch("other.package"),
|
||||
)
|
||||
|
||||
assertEqualsVersion(null, patches, "other.package")
|
||||
}
|
||||
|
||||
private fun assertEqualsVersion(
|
||||
expected: String?, patches: PatchSet, compatiblePackageName: String
|
||||
expected: String?,
|
||||
patches: PatchSet,
|
||||
compatiblePackageName: String,
|
||||
) = assertEquals(expected, PatchUtils.getMostCommonCompatibleVersion(patches, compatiblePackageName))
|
||||
|
||||
private fun newPatch(packageName: String, vararg versions: String) = object : BytecodePatch() {
|
||||
private fun newPatch(
|
||||
packageName: String,
|
||||
vararg versions: String,
|
||||
) = object : BytecodePatch() {
|
||||
init {
|
||||
// Set the compatible packages field to the supplied package name and versions reflectively,
|
||||
// because the setter is private but needed for testing.
|
||||
|
||||
Reference in New Issue
Block a user