mirror of
https://github.com/revanced/revanced-cli.git
synced 2025-01-07 09:45:50 +01:00
feat: Add function to get the most common compatible version
This adds a function to get the version that is most common for a given package name in a supplied set of patches.
This commit is contained in:
parent
3846f721ca
commit
77d91735ff
@ -1,3 +1,30 @@
|
|||||||
|
public final class app/revanced/lib/ApkSigner {
|
||||||
|
public static final field INSTANCE Lapp/revanced/lib/ApkSigner;
|
||||||
|
public final fun newApkSignerBuilder (Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
|
||||||
|
public final fun newApkSignerBuilder (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
|
||||||
|
public final fun newKeyStore (Ljava/util/List;)Ljava/security/KeyStore;
|
||||||
|
public final fun newKeystore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/List;)V
|
||||||
|
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;
|
||||||
|
public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/lib/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;
|
||||||
|
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;
|
||||||
|
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
|
||||||
|
public final fun signApk (Lcom/android/apksig/ApkSigner$Builder;Ljava/io/File;Ljava/io/File;)V
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/lib/ApkSigner$KeyStoreEntry {
|
||||||
|
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;)V
|
||||||
|
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
|
public final fun getAlias ()Ljava/lang/String;
|
||||||
|
public final fun getPassword ()Ljava/lang/String;
|
||||||
|
public final fun getPrivateKeyCertificatePair ()Lapp/revanced/lib/ApkSigner$PrivateKeyCertificatePair;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/lib/ApkSigner$PrivateKeyCertificatePair {
|
||||||
|
public fun <init> (Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V
|
||||||
|
public final fun getCertificate ()Ljava/security/cert/X509Certificate;
|
||||||
|
public final fun getPrivateKey ()Ljava/security/PrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/lib/ApkUtils {
|
public final class app/revanced/lib/ApkUtils {
|
||||||
public static final field INSTANCE Lapp/revanced/lib/ApkUtils;
|
public static final field INSTANCE Lapp/revanced/lib/ApkUtils;
|
||||||
public final fun copyAligned (Ljava/io/File;Ljava/io/File;Lapp/revanced/patcher/PatcherResult;)V
|
public final fun copyAligned (Ljava/io/File;Ljava/io/File;Lapp/revanced/patcher/PatcherResult;)V
|
||||||
@ -33,6 +60,11 @@ public final class app/revanced/lib/Options$Patch$Option {
|
|||||||
public final fun getValue ()Ljava/lang/Object;
|
public final fun getValue ()Ljava/lang/Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/lib/PatchUtils {
|
||||||
|
public static final field INSTANCE Lapp/revanced/lib/PatchUtils;
|
||||||
|
public final fun getMostCommonCompatibleVersion (Ljava/util/Set;Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class app/revanced/lib/adb/AdbManager {
|
public abstract class app/revanced/lib/adb/AdbManager {
|
||||||
public static final field Companion Lapp/revanced/lib/adb/AdbManager$Companion;
|
public static final field Companion Lapp/revanced/lib/adb/AdbManager$Companion;
|
||||||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||||
@ -86,33 +118,6 @@ public final class app/revanced/lib/logging/Logger {
|
|||||||
public static synthetic fun setFormat$default (Lapp/revanced/lib/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V
|
public static synthetic fun setFormat$default (Lapp/revanced/lib/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/lib/signing/ApkSigner {
|
|
||||||
public static final field INSTANCE Lapp/revanced/lib/signing/ApkSigner;
|
|
||||||
public final fun newApkSignerBuilder (Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
|
|
||||||
public final fun newApkSignerBuilder (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/android/apksig/ApkSigner$Builder;
|
|
||||||
public final fun newKeyStore (Ljava/util/List;)Ljava/security/KeyStore;
|
|
||||||
public final fun newKeystore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/List;)V
|
|
||||||
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
|
|
||||||
public static synthetic fun newPrivateKeyCertificatePair$default (Lapp/revanced/lib/signing/ApkSigner;Ljava/lang/String;Ljava/util/Date;ILjava/lang/Object;)Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
|
|
||||||
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
|
|
||||||
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
|
|
||||||
public final fun signApk (Lcom/android/apksig/ApkSigner$Builder;Ljava/io/File;Ljava/io/File;)V
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/lib/signing/ApkSigner$KeyStoreEntry {
|
|
||||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;)V
|
|
||||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
|
||||||
public final fun getAlias ()Ljava/lang/String;
|
|
||||||
public final fun getPassword ()Ljava/lang/String;
|
|
||||||
public final fun getPrivateKeyCertificatePair ()Lapp/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/lib/signing/ApkSigner$PrivateKeyCertificatePair {
|
|
||||||
public fun <init> (Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V
|
|
||||||
public final fun getCertificate ()Ljava/security/cert/X509Certificate;
|
|
||||||
public final fun getPrivateKey ()Ljava/security/PrivateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/lib/zip/ZipFile : java/io/Closeable {
|
public final class app/revanced/lib/zip/ZipFile : java/io/Closeable {
|
||||||
public static final field ApkZipFile Lapp/revanced/lib/zip/ZipFile$ApkZipFile;
|
public static final field ApkZipFile Lapp/revanced/lib/zip/ZipFile$ApkZipFile;
|
||||||
public fun <init> (Ljava/io/File;)V
|
public fun <init> (Ljava/io/File;)V
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package app.revanced.lib.signing
|
package app.revanced.lib
|
||||||
|
|
||||||
import com.android.apksig.ApkSigner
|
import com.android.apksig.ApkSigner
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -18,9 +18,12 @@ import java.util.*
|
|||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
|
||||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
/**
|
||||||
|
* Utility class for writing or reading keystore files and entries as well as signing APK files.
|
||||||
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
object ApkSigner {
|
object ApkSigner {
|
||||||
private val logger = Logger.getLogger(app.revanced.lib.signing.ApkSigner::class.java.name)
|
private val logger = Logger.getLogger(app.revanced.lib.ApkSigner::class.java.name)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
|
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
|
||||||
@ -67,6 +70,39 @@ object ApkSigner {
|
|||||||
return PrivateKeyCertificatePair(keyPair.private, certificate)
|
return PrivateKeyCertificatePair(keyPair.private, certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a [PrivateKeyCertificatePair] from a keystore entry.
|
||||||
|
*
|
||||||
|
* @param keyStore The keystore to read the entry from.
|
||||||
|
* @param keyStoreEntryAlias The alias of the key store entry to read.
|
||||||
|
* @param keyStoreEntryPassword The password for recovering the signing key.
|
||||||
|
* @return The read [PrivateKeyCertificatePair].
|
||||||
|
* @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid.
|
||||||
|
*/
|
||||||
|
fun readKeyCertificatePair(
|
||||||
|
keyStore: KeyStore,
|
||||||
|
keyStoreEntryAlias: String,
|
||||||
|
keyStoreEntryPassword: String,
|
||||||
|
): PrivateKeyCertificatePair {
|
||||||
|
logger.fine("Reading key and certificate pair from keystore entry $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 certificate = keyStore.getCertificate(keyStoreEntryAlias) as X509Certificate
|
||||||
|
|
||||||
|
return PrivateKeyCertificatePair(privateKey, certificate)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new keystore with a new keypair.
|
* Create a new keystore with a new keypair.
|
||||||
*
|
*
|
||||||
@ -167,38 +203,6 @@ object ApkSigner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a [PrivateKeyCertificatePair] from a keystore entry.
|
|
||||||
*
|
|
||||||
* @param keyStore The keystore to read the entry from.
|
|
||||||
* @param keyStoreEntryAlias The alias of the key store entry to read.
|
|
||||||
* @param keyStoreEntryPassword The password for recovering the signing key.
|
|
||||||
* @return The read [PrivateKeyCertificatePair].
|
|
||||||
* @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid.
|
|
||||||
*/
|
|
||||||
fun readKeyCertificatePair(
|
|
||||||
keyStore: KeyStore,
|
|
||||||
keyStoreEntryAlias: String,
|
|
||||||
keyStoreEntryPassword: String,
|
|
||||||
): PrivateKeyCertificatePair {
|
|
||||||
logger.fine("Reading key and certificate pair from keystore entry $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 certificate = keyStore.getCertificate(keyStoreEntryAlias) as X509Certificate
|
|
||||||
|
|
||||||
return PrivateKeyCertificatePair(privateKey, certificate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new [ApkSigner.Builder].
|
* Create a new [ApkSigner.Builder].
|
||||||
*
|
*
|
@ -1,7 +1,6 @@
|
|||||||
package app.revanced.lib
|
package app.revanced.lib
|
||||||
|
|
||||||
import app.revanced.lib.signing.ApkSigner
|
import app.revanced.lib.ApkSigner.signApk
|
||||||
import app.revanced.lib.signing.ApkSigner.signApk
|
|
||||||
import app.revanced.lib.zip.ZipFile
|
import app.revanced.lib.zip.ZipFile
|
||||||
import app.revanced.lib.zip.structures.ZipEntry
|
import app.revanced.lib.zip.structures.ZipEntry
|
||||||
import app.revanced.patcher.PatcherResult
|
import app.revanced.patcher.PatcherResult
|
||||||
@ -9,6 +8,9 @@ import java.io.File
|
|||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
import kotlin.io.path.deleteIfExists
|
import kotlin.io.path.deleteIfExists
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility functions for working with apks.
|
||||||
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
object ApkUtils {
|
object ApkUtils {
|
||||||
private val logger = Logger.getLogger(ApkUtils::class.java.name)
|
private val logger = Logger.getLogger(ApkUtils::class.java.name)
|
||||||
|
28
revanced-lib/src/main/kotlin/app/revanced/lib/PatchUtils.kt
Normal file
28
revanced-lib/src/main/kotlin/app/revanced/lib/PatchUtils.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package app.revanced.lib
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchSet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility functions for working with patches.
|
||||||
|
*/
|
||||||
|
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
|
object PatchUtils {
|
||||||
|
/**
|
||||||
|
* Get the version that is most common for [packageName] in the supplied set of [patches].
|
||||||
|
*
|
||||||
|
* @param patches The set of patches to check.
|
||||||
|
* @param packageName The name of the compatible package.
|
||||||
|
* @return The most common version of.
|
||||||
|
*/
|
||||||
|
fun getMostCommonCompatibleVersion(patches: PatchSet, packageName: String) = patches
|
||||||
|
.mapNotNull {
|
||||||
|
// Map all patches to their compatible packages with version constraints.
|
||||||
|
it.compatiblePackages?.firstOrNull { compatiblePackage ->
|
||||||
|
compatiblePackage.name == packageName && compatiblePackage.versions?.isNotEmpty() == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flatMap { it.versions!! }
|
||||||
|
.groupingBy { it }
|
||||||
|
.eachCount()
|
||||||
|
.maxByOrNull { it.value }?.key
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patcher.options
|
package app.revanced.lib
|
||||||
|
|
||||||
import app.revanced.lib.Options
|
|
||||||
import app.revanced.lib.Options.setOptions
|
import app.revanced.lib.Options.setOptions
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
@ -11,16 +10,6 @@ import org.junit.jupiter.api.Order
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestMethodOrder
|
import org.junit.jupiter.api.TestMethodOrder
|
||||||
|
|
||||||
|
|
||||||
object PatchOptionsTestPatch : BytecodePatch(name = "PatchOptionsTestPatch") {
|
|
||||||
var key1 by stringPatchOption("key1", null, "title1", "description1")
|
|
||||||
var key2 by booleanPatchOption("key2", true, "title2", "description2")
|
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
|
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
|
||||||
internal object PatchOptionsTest {
|
internal object PatchOptionsTest {
|
||||||
private var patches = setOf(PatchOptionsTestPatch)
|
private var patches = setOf(PatchOptionsTestPatch)
|
||||||
@ -36,8 +25,8 @@ internal object PatchOptionsTest {
|
|||||||
fun loadOptionsTest() {
|
fun loadOptionsTest() {
|
||||||
patches.setOptions(CHANGED_JSON)
|
patches.setOptions(CHANGED_JSON)
|
||||||
|
|
||||||
assert(PatchOptionsTestPatch.key1 == "test")
|
assert(PatchOptionsTestPatch.option1 == "test")
|
||||||
assert(PatchOptionsTestPatch.key2 == false)
|
assert(PatchOptionsTestPatch.option2 == false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val SERIALIZED_JSON =
|
private const val SERIALIZED_JSON =
|
||||||
@ -45,4 +34,13 @@ internal object PatchOptionsTest {
|
|||||||
|
|
||||||
private const val CHANGED_JSON =
|
private const val CHANGED_JSON =
|
||||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2\",\"value\":false}]}]"
|
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2\",\"value\":false}]}]"
|
||||||
|
|
||||||
|
object PatchOptionsTestPatch : BytecodePatch(name = "PatchOptionsTestPatch") {
|
||||||
|
var option1 by stringPatchOption("key1", null, "title1", "description1")
|
||||||
|
var option2 by booleanPatchOption("key2", true, "title2", "description2")
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package app.revanced.lib
|
||||||
|
|
||||||
|
import app.revanced.patcher.PatchSet
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
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()
|
||||||
|
|
||||||
|
assertEqualsVersion("a", patches, "some.package")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `return null because no patches were supplied`() {
|
||||||
|
assertEqualsVersion(null, emptySet<BytecodePatch>(), "some.package")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `return null because no patch is compatible with the supplied package name`() {
|
||||||
|
val patches = setOf(newPatch("other.package", "a"))
|
||||||
|
|
||||||
|
assertEqualsVersion(null, patches, "other.package")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `return null because no patch compatible package is constrained to a version`() {
|
||||||
|
val patches = setOf(
|
||||||
|
newPatch("other.package"),
|
||||||
|
newPatch("other.package"),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEqualsVersion(null, patches, "other.package")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertEqualsVersion(
|
||||||
|
expected: String?, patches: PatchSet, compatiblePackageName: String
|
||||||
|
) = assertEquals(expected, PatchUtils.getMostCommonCompatibleVersion(patches, compatiblePackageName))
|
||||||
|
|
||||||
|
private fun newPatch(packageName: String, vararg versions: String) = object : BytecodePatch(
|
||||||
|
compatiblePackages = setOf(CompatiblePackage(packageName, versions.toSet()))
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user