Skip to content

Commit

Permalink
chore: Merge branch dev to main (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
oSumAtrIX authored Nov 27, 2023
2 parents b6c09d4 + cdf94ff commit 98b7b34
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 12 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# [1.4.0-dev.2](https://github.com/ReVanced/revanced-library/compare/v1.4.0-dev.1...v1.4.0-dev.2) (2023-11-27)


### Bug Fixes

* Differentiate no package compatibility to any version compatibility ([762b7e3](https://github.com/ReVanced/revanced-library/commit/762b7e3bc01e2ca33dfcdbb1b5028d60ef6e0a48))
* Sort the version maps by the most common version ([e4be6db](https://github.com/ReVanced/revanced-library/commit/e4be6dbccd86700ffafe7cd8395e845bbd3d5138))


### Features

* Allow getting most common compatible versions for all packages ([96845ba](https://github.com/ReVanced/revanced-library/commit/96845ba265e6dc208c7ac96f5e58734209cd1720))

# [1.4.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v1.3.0...v1.4.0-dev.1) (2023-11-27)


### Features

* Add `PatchUtils#getMostCommonCompatibleVersions` utility function ([c5f3536](https://github.com/ReVanced/revanced-library/commit/c5f3536cbb6997766076595dc0b2b5d2e861ca73))

# [1.3.0](https://github.com/ReVanced/revanced-library/compare/v1.2.0...v1.3.0) (2023-11-26)


Expand Down
3 changes: 3 additions & 0 deletions api/revanced-library.api
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public final class app/revanced/library/Options$Patch$Option {
public final class app/revanced/library/PatchUtils {
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
public final fun getMostCommonCompatibleVersion (Ljava/util/Set;Ljava/lang/String;)Ljava/lang/String;
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
}

public abstract class app/revanced/library/adb/AdbManager {
Expand All @@ -87,6 +89,7 @@ public final class app/revanced/library/adb/AdbManager$Companion {
}

public final class app/revanced/library/adb/AdbManager$DeviceNotFoundException : java/lang/Exception {
public fun <init> ()V
}

public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception {
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 = 1.3.0
version = 1.4.0-dev.2
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ bcpkix-jdk18on = "1.76"
jackson-module-kotlin = "2.14.3"
jadb = "1.2.1"
kotlin-reflect = "1.9.10"
kotlin-test = "1.9.10"
kotlin-test = "1.9.20"
revanced-patcher = "19.0.0"
binary-compatibility-validator = "0.13.2"

Expand Down
62 changes: 62 additions & 0 deletions src/main/kotlin/app/revanced/library/PatchUtils.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package app.revanced.library

import app.revanced.patcher.PatchSet
import app.revanced.patcher.patch.Patch

typealias PackageName = String
typealias Version = String
typealias Count = Int

typealias VersionMap = LinkedHashMap<Version, Count>
typealias PackageNameMap = Map<PackageName, VersionMap>

/**
* Utility functions for working with patches.
Expand All @@ -14,6 +22,13 @@ object PatchUtils {
* @param packageName The name of the compatible package.
* @return The most common version of.
*/
@Deprecated(
"Use getMostCommonCompatibleVersions instead.",
ReplaceWith(
"getMostCommonCompatibleVersions(patches, setOf(packageName))" +
".entries.firstOrNull()?.value?.keys?.firstOrNull()",
),
)
fun getMostCommonCompatibleVersion(
patches: PatchSet,
packageName: String,
Expand All @@ -28,4 +43,51 @@ object PatchUtils {
.groupingBy { it }
.eachCount()
.maxByOrNull { it.value }?.key

/**
* Get the count of versions for each compatible package from a supplied set of [patches] ordered by the most common version.
*
* @param patches The set of patches to check.
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
* @param countUnusedPatches Whether to count patches that are not used.
* @return A map of package names to a map of versions to their count.
*/
fun getMostCommonCompatibleVersions(
patches: PatchSet,
packageNames: Set<String>? = null,
countUnusedPatches: Boolean = false,
): PackageNameMap =
buildMap {
fun filterWantedPackages(compatiblePackages: Iterable<Patch.CompatiblePackage>): Iterable<Patch.CompatiblePackage> {
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
return compatiblePackages.filter { it.name in wantedPackages }
}

patches
.filter { it.use || countUnusedPatches }
.flatMap { it.compatiblePackages ?: emptyList() }
.let(::filterWantedPackages)
.forEach { compatiblePackage ->
if (compatiblePackage.versions?.isEmpty() == true) {
return@forEach
}

val versionMap = getOrPut(compatiblePackage.name) { linkedMapOf() }

compatiblePackage.versions?.let { versions ->
versions.forEach { version ->
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
}
}
}

// Sort the version maps by the most common version.
forEach { (packageName, versionMap) ->
this[packageName] =
versionMap
.asIterable()
.sortedWith(compareByDescending { it.value })
.associate { it.key to it.value } as VersionMap
}
}
}
8 changes: 4 additions & 4 deletions src/main/kotlin/app/revanced/library/adb/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ internal object Constants {
"chcon u:object_r:apk_data_file:s0 ${'$'}base_path"

internal const val UMOUNT =
"grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d \" \" -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done"
"grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d ' ' -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done"

internal const val INSTALL_MOUNT = "mv $TMP_PATH $MOUNT_PATH && chmod +x $MOUNT_PATH"

internal val MOUNT_SCRIPT =
"""
#!/system/bin/sh
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
MAGISKTMP="$( magisk --path )" || MAGISKTMP=/sbin
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
until [ "${'$'}(getprop sys.boot_completed)" = 1 ]; do sleep 3; 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' )
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
Expand Down
140 changes: 134 additions & 6 deletions src/test/kotlin/app/revanced/library/PatchUtilsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,106 @@ import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

internal object PatchUtilsTest {
private val patches =
arrayOf(
newPatch("some.package", setOf("a")),
newPatch("some.package", setOf("a", "b"), use = false),
newPatch("some.package", setOf("a", "b", "c"), use = false),
newPatch("some.other.package", setOf("b"), use = false),
newPatch("some.other.package", setOf("b", "c")),
newPatch("some.other.package", setOf("b", "c", "d")),
newPatch("some.other.other.package"),
newPatch("some.other.other.package", setOf("a")),
newPatch("some.other.other.package", setOf("b")),
newPatch("some.other.other.other.package", use = false),
newPatch("some.other.other.other.package", use = false),
).toSet()

@Test
fun `empty because package is incompatible with any version`() {
assertEqualsVersions(
expected = emptyMap(),
patches = setOf(newPatch("some.package", emptySet(), use = true)),
compatiblePackageNames = setOf("some.package"),
)
}

@Test
fun `empty list of versions because package is unconstrained to any version`() {
assertEqualsVersions(
expected = mapOf("some.package" to linkedMapOf()),
patches = setOf(newPatch("some.package")),
compatiblePackageNames = setOf("some.package"),
countUnusedPatches = true,
)
}

@Test
fun `empty because no known package was supplied`() {
assertEqualsVersions(
expected = emptyMap(),
patches,
compatiblePackageNames = setOf("unknown.package"),
)
}

@Test
fun `common versions correctly ordered for each package`() {
fun assertEqualsExpected(compatiblePackageNames: Set<String>?) =
assertEqualsVersions(
expected =
mapOf(
"some.package" to linkedMapOf("a" to 3, "b" to 2, "c" to 1),
"some.other.package" to linkedMapOf("b" to 3, "c" to 2, "d" to 1),
"some.other.other.package" to linkedMapOf("a" to 1, "b" to 1),
"some.other.other.other.package" to linkedMapOf(),
),
patches,
compatiblePackageNames,
countUnusedPatches = true,
)

assertEqualsExpected(
compatiblePackageNames =
setOf(
"some.package",
"some.other.package",
"some.other.other.package",
"some.other.other.other.package",
),
)

assertEqualsExpected(
compatiblePackageNames = null,
)
}

@Test
fun `common versions correctly ordered for each package without counting unused patches`() {
assertEqualsVersions(
expected =
mapOf(
"some.package" to linkedMapOf("a" to 1),
"some.other.package" to linkedMapOf("b" to 2, "c" to 2, "d" to 1),
"some.other.other.package" to linkedMapOf("a" to 1, "b" to 1),
),
patches,
compatiblePackageNames =
setOf(
"some.package",
"some.other.package",
"some.other.other.package",
"some.other.other.other.package",
),
countUnusedPatches = false,
)
}

@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) }
.map { version -> newPatch("some.package", setOf(version)) }
.toSet()

assertEqualsVersion("a", patches, "some.package")
Expand All @@ -25,13 +120,13 @@ internal object PatchUtilsTest {

@Test
fun `return null because no patch is compatible with the supplied package name`() {
val patches = setOf(newPatch("some.package", "a"))
val patches = setOf(newPatch("some.package", setOf("a")))

assertEqualsVersion(null, patches, "other.package")
}

@Test
fun `return null because no patch compatible package is constrained to a version`() {
fun `return null because no compatible package is constrained to a version`() {
val patches =
setOf(
newPatch("other.package"),
Expand All @@ -41,25 +136,58 @@ internal object PatchUtilsTest {
assertEqualsVersion(null, patches, "other.package")
}

private fun assertEqualsVersions(
expected: PackageNameMap,
patches: PatchSet,
compatiblePackageNames: Set<String>?,
countUnusedPatches: Boolean = false,
) = assertEquals(
expected,
PatchUtils.getMostCommonCompatibleVersions(patches, compatiblePackageNames, countUnusedPatches),
)

private fun assertEqualsVersion(
expected: String?,
patches: PatchSet,
compatiblePackageName: String,
) = assertEquals(expected, PatchUtils.getMostCommonCompatibleVersion(patches, compatiblePackageName))
) {
// Test both the deprecated and the new method.

@Suppress("DEPRECATION")
assertEquals(
expected,
PatchUtils.getMostCommonCompatibleVersion(patches, compatiblePackageName),
)

assertEquals(
expected,
PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName))
.entries.firstOrNull()?.value?.keys?.firstOrNull(),
)
}

private fun newPatch(
packageName: String,
vararg versions: String,
versions: Set<String>? = null,
use: Boolean = true,
) = 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.
val compatiblePackagesField = Patch::class.java.getDeclaredField("compatiblePackages")

compatiblePackagesField.isAccessible = true
compatiblePackagesField.set(this, setOf(CompatiblePackage(packageName, versions.toSet())))
compatiblePackagesField.set(this, setOf(CompatiblePackage(packageName, versions?.toSet())))

val useField = Patch::class.java.getDeclaredField("use")

useField.isAccessible = true
useField.set(this, use)
}

override fun execute(context: BytecodeContext) {}

// Needed to make the patches unique.
override fun equals(other: Any?) = false
}
}

0 comments on commit 98b7b34

Please sign in to comment.