mirror of
https://github.com/ReVanced/revanced-library.git
synced 2026-01-11 13:56:17 +00:00
Compare commits
1 Commits
v3.0.2
...
feat/netwo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42de60e95b |
149
build.gradle.kts
149
build.gradle.kts
@@ -1,143 +1,30 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
`maven-publish`
|
||||
signing
|
||||
alias(libs.plugins.kotlin.jvm) apply false
|
||||
alias(libs.plugins.kotlin.multiplatform) apply false
|
||||
alias(libs.plugins.kotlin.serialization) apply false
|
||||
alias(libs.plugins.android.library) apply false
|
||||
alias(libs.plugins.binary.compatibility.validator) apply false
|
||||
alias(libs.plugins.ktor) apply false
|
||||
}
|
||||
|
||||
group = "app.revanced"
|
||||
|
||||
// Because access to the project is necessary to authenticate with GitHub,
|
||||
// the following block must be placed in the root build.gradle.kts file
|
||||
// instead of the settings.gradle.kts file inside the dependencyResolutionManagement block.
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
google()
|
||||
maven {
|
||||
// A repository must be specified for some reason. "registry" is a dummy.
|
||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||
credentials {
|
||||
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
|
||||
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
}
|
||||
|
||||
publishLibraryVariants("release")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidMain.dependencies {
|
||||
implementation(libs.libsu.nio)
|
||||
implementation(libs.libsu.service)
|
||||
implementation(libs.core.ktx)
|
||||
}
|
||||
|
||||
commonMain.dependencies {
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.jadb) // Fork with Shell v2 support.
|
||||
implementation(libs.bcpkix.jdk15on)
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
implementation(libs.apkzlib)
|
||||
implementation(libs.apksig)
|
||||
implementation(libs.guava)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.kotlin.test.junit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.library"
|
||||
compileSdk = 34
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
publishing {
|
||||
subprojects {
|
||||
// Because access to the project is necessary to authenticate with GitHub,
|
||||
// the following block must be placed in the root build.gradle.kts file
|
||||
// instead of the settings.gradle.kts file inside the dependencyResolutionManagement block.
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
google()
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-library")
|
||||
// A repository must be specified for some reason. "registry" is a dummy.
|
||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
create<MavenPublication>("revanced-library-publication") {
|
||||
version = project.version.toString()
|
||||
|
||||
pom {
|
||||
name = "ReVanced Library"
|
||||
description = "Library containing common utilities for ReVanced"
|
||||
url = "https://revanced.app"
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = "GNU General Public License v3.0"
|
||||
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = "ReVanced"
|
||||
name = "ReVanced"
|
||||
email = "contact@revanced.app"
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = "scm:git:git://github.com/revanced/revanced-library.git"
|
||||
developerConnection = "scm:git:git@github.com:revanced/revanced-library.git"
|
||||
url = "https://github.com/revanced/revanced-library"
|
||||
}
|
||||
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
|
||||
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications["revanced-library-publication"])
|
||||
}
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
jackson-module-kotlin = "2.15.0"
|
||||
jadb = "1.2.1"
|
||||
kotlin = "1.9.22"
|
||||
ktor-client = "2.3.10"
|
||||
ktor-server-test-host = "2.3.9"
|
||||
revanced-patcher = "19.3.1"
|
||||
binary-compatibility-validator = "0.14.0"
|
||||
android = "8.3.0"
|
||||
android = "8.3.2"
|
||||
bcpkix-jdk15on = "1.70"
|
||||
guava = "33.0.0-jre"
|
||||
libsu = "5.2.2"
|
||||
core-ktx = "1.12.0"
|
||||
ktor = "2.3.9"
|
||||
koin = "3.5.3"
|
||||
logback = "1.4.14"
|
||||
|
||||
[libraries]
|
||||
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" }
|
||||
@@ -24,9 +29,26 @@ libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu"
|
||||
libsu-nio = { module = "com.github.topjohnwu.libsu:nio", version.ref = "libsu" }
|
||||
libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" }
|
||||
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
|
||||
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
|
||||
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor-client" }
|
||||
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor-client" }
|
||||
ktor-server-conditional-headers = { module = "io.ktor:ktor-server-conditional-headers" }
|
||||
ktor-server-core = { module = "io.ktor:ktor-server-core" }
|
||||
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation" }
|
||||
ktor-server-auth = { module = "io.ktor:ktor-server-auth" }
|
||||
ktor-server-auth-jwt = { module = "io.ktor:ktor-server-auth-jwt" }
|
||||
ktor-server-cors = { module = "io.ktor:ktor-server-cors" }
|
||||
ktor-server-caching-headers = { module = "io.ktor:ktor-server-caching-headers" }
|
||||
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common" }
|
||||
ktor-server-netty = { module = "io.ktor:ktor-server-netty" }
|
||||
ktor-server-websockets = { module = "io.ktor:ktor-server-websockets" }
|
||||
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json" }
|
||||
koin-ktor = { module = "io.insert-koin:koin-ktor", version.ref = "koin" }
|
||||
|
||||
[plugins]
|
||||
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
|
||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||
android-library = { id = "com.android.library", version.ref = "android" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
|
||||
143
library-networking/api/library-networking.api
Normal file
143
library-networking/api/library-networking.api
Normal file
@@ -0,0 +1,143 @@
|
||||
public final class app/revanced/library/networking/Server {
|
||||
public final fun start ()Lio/ktor/server/engine/ApplicationEngine;
|
||||
public final fun stop ()V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/Server$DependenciesConfiguration {
|
||||
public fun <init> (Lapp/revanced/library/networking/configuration/repository/StorageRepository;Lapp/revanced/library/networking/configuration/repository/PatchSetRepository;Lapp/revanced/library/networking/configuration/repository/AppRepository;Lapp/revanced/library/networking/configuration/repository/InstallerRepository;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/Server$SecurityConfiguration {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/Server$SerializersConfiguration {
|
||||
public fun <init> ()V
|
||||
public fun <init> (Ljava/util/Map;)V
|
||||
public synthetic fun <init> (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/ServerBuilder {
|
||||
public fun <init> ()V
|
||||
public final fun configureDependencies (Lkotlin/jvm/functions/Function1;)Lapp/revanced/library/networking/ServerBuilder;
|
||||
public final fun configureSecurity (Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/networking/ServerBuilder;
|
||||
public final fun configureSerializers (Lkotlin/jvm/functions/Function1;)Lapp/revanced/library/networking/ServerBuilder;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/ServerBuilder$DependenciesConfigurationBuilder {
|
||||
public final fun build ()Lapp/revanced/library/networking/Server$DependenciesConfiguration;
|
||||
public final fun configureAppRepository (Lapp/revanced/library/networking/configuration/repository/AppRepository;)Lapp/revanced/library/networking/ServerBuilder$DependenciesConfigurationBuilder;
|
||||
public final fun configureInstallerRepository (Lapp/revanced/library/networking/configuration/repository/InstallerRepository;)Lapp/revanced/library/networking/ServerBuilder$DependenciesConfigurationBuilder;
|
||||
public final fun configurePatchSetRepository (Lapp/revanced/library/networking/configuration/repository/PatchSetRepository;)Lapp/revanced/library/networking/ServerBuilder$DependenciesConfigurationBuilder;
|
||||
public final fun configureStorageRepository (Lapp/revanced/library/networking/configuration/repository/StorageRepository;)Lapp/revanced/library/networking/ServerBuilder$DependenciesConfigurationBuilder;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/ServerBuilder$SerializersConfigurationBuilder {
|
||||
public final fun build ()Lapp/revanced/library/networking/Server$SerializersConfiguration;
|
||||
public final fun configurePatchOptionSerializers ([Lkotlin/Pair;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/ServerKt {
|
||||
public static final fun main ()V
|
||||
public static synthetic fun main ([Ljava/lang/String;)V
|
||||
public static final fun server (Ljava/lang/String;ILio/ktor/server/engine/ApplicationEngineFactory;Lkotlin/jvm/functions/Function1;)Lapp/revanced/library/networking/Server;
|
||||
public static synthetic fun server$default (Ljava/lang/String;ILio/ktor/server/engine/ApplicationEngineFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/library/networking/Server;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/configuration/SerializationKt {
|
||||
public static final fun configureSerialization (Lio/ktor/server/application/Application;Lapp/revanced/library/networking/Server$SerializersConfiguration;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/networking/configuration/repository/AppRepository {
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/networking/configuration/repository/InstallerRepository {
|
||||
public fun <init> ()V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/networking/configuration/repository/PatchSetRepository {
|
||||
public fun <init> (Lapp/revanced/library/networking/configuration/repository/StorageRepository;)V
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/networking/configuration/repository/StorageRepository {
|
||||
public fun <init> (Ljava/io/File;Ljava/io/File;Ljava/io/File;Ljava/io/File;)V
|
||||
public synthetic fun <init> (Ljava/io/File;Ljava/io/File;Ljava/io/File;Ljava/io/File;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public final fun getAaptBinaryPath ()Ljava/io/File;
|
||||
public final fun getKeystoreFilePath ()Ljava/io/File;
|
||||
public final fun getOutputFilePath ()Ljava/io/File;
|
||||
public final fun getTemporaryFilesPath ()Ljava/io/File;
|
||||
}
|
||||
|
||||
public class app/revanced/library/networking/models/App {
|
||||
public static final field Companion Lapp/revanced/library/networking/models/App$Companion;
|
||||
public synthetic fun <init> (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
public static final synthetic fun write$Self (Lapp/revanced/library/networking/models/App;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/App$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Lapp/revanced/library/networking/models/App$$serializer;
|
||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lapp/revanced/library/networking/models/App;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lapp/revanced/library/networking/models/App;)V
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/App$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch {
|
||||
public static final field Companion Lapp/revanced/library/networking/models/Patch$Companion;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public static final field INSTANCE Lapp/revanced/library/networking/models/Patch$$serializer;
|
||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lapp/revanced/library/networking/models/Patch;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lapp/revanced/library/networking/models/Patch;)V
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch$Companion {
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch$KeyValuePatchOption {
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)V
|
||||
public final fun getKey ()Ljava/lang/String;
|
||||
public final fun getValue ()Ljava/lang/Object;
|
||||
public final fun getValueType ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch$PatchOption {
|
||||
public static final field Companion Lapp/revanced/library/networking/models/Patch$PatchOption$Companion;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch$PatchOption$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
|
||||
public synthetic fun <init> (Lkotlinx/serialization/KSerializer;)V
|
||||
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lapp/revanced/library/networking/models/Patch$PatchOption;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lapp/revanced/library/networking/models/Patch$PatchOption;)V
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/Patch$PatchOption$Companion {
|
||||
public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/networking/models/PatchBundle {
|
||||
public final fun getPatchBundleFile ()Ljava/io/File;
|
||||
public final fun getPatchBundleIntegrationsFile ()Ljava/io/File;
|
||||
}
|
||||
|
||||
97
library-networking/build.gradle.kts
Normal file
97
library-networking/build.gradle.kts
Normal file
@@ -0,0 +1,97 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
alias(libs.plugins.ktor)
|
||||
`maven-publish`
|
||||
signing
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":library"))
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.cio)
|
||||
implementation(libs.ktor.server.core)
|
||||
implementation(libs.ktor.server.content.negotiation)
|
||||
implementation(libs.ktor.server.auth)
|
||||
implementation(libs.ktor.server.auth.jwt)
|
||||
implementation(libs.ktor.server.cors)
|
||||
implementation(libs.ktor.server.caching.headers)
|
||||
implementation(libs.ktor.server.host.common)
|
||||
implementation(libs.ktor.server.netty)
|
||||
implementation(libs.ktor.server.conditional.headers)
|
||||
implementation(libs.ktor.server.websockets)
|
||||
implementation(libs.ktor.serialization.kotlinx.json)
|
||||
implementation(libs.koin.ktor)
|
||||
implementation(libs.logback.classic)
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
expand("projectVersion" to project.version)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-library")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
create<MavenPublication>("revanced-library-networking-publication") {
|
||||
version = project.version.toString()
|
||||
|
||||
pom {
|
||||
name = "ReVanced Networking Library"
|
||||
description = "Library to interface to common utilities for ReVanced over a network."
|
||||
url = "https://revanced.app"
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = "GNU General Public License v3.0"
|
||||
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = "ReVanced"
|
||||
name = "ReVanced"
|
||||
email = "contact@revanced.app"
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = "scm:git:git://github.com/revanced/revanced-library.git"
|
||||
developerConnection = "scm:git:git@github.com:revanced/revanced-library.git"
|
||||
url = "https://github.com/revanced/revanced-library"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications["revanced-library-networking-publication"])
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package app.revanced.library.networking
|
||||
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.util.pipeline.*
|
||||
|
||||
internal val PipelineContext<*, ApplicationCall>.parameters get() = call.parameters
|
||||
@@ -0,0 +1,281 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package app.revanced.library.networking
|
||||
|
||||
import app.revanced.library.installation.installer.AdbInstaller
|
||||
import app.revanced.library.networking.configuration.configureDependencies
|
||||
import app.revanced.library.networking.configuration.configureHTTP
|
||||
import app.revanced.library.networking.configuration.configureSecurity
|
||||
import app.revanced.library.networking.configuration.configureSerialization
|
||||
import app.revanced.library.networking.configuration.repository.AppRepository
|
||||
import app.revanced.library.networking.configuration.repository.InstallerRepository
|
||||
import app.revanced.library.networking.configuration.repository.PatchSetRepository
|
||||
import app.revanced.library.networking.configuration.repository.StorageRepository
|
||||
import app.revanced.library.networking.configuration.routing.configureRouting
|
||||
import app.revanced.library.networking.models.App
|
||||
import app.revanced.library.networking.models.PatchBundle
|
||||
import app.revanced.patcher.PatchBundleLoader
|
||||
import app.revanced.patcher.PatchSet
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
import io.ktor.server.engine.*
|
||||
import io.ktor.server.netty.*
|
||||
import java.io.File
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* A server.
|
||||
*
|
||||
* @param host The host.
|
||||
* @param port The port.
|
||||
* @param engineFactory The engine factory.
|
||||
* @param securityConfiguration The security configuration.
|
||||
* @param dependenciesConfiguration The dependencies configuration.
|
||||
* @param serializersConfiguration The serializers configuration.
|
||||
*/
|
||||
class Server internal constructor(
|
||||
host: String,
|
||||
port: Int,
|
||||
engineFactory: ApplicationEngineFactory<*, *>,
|
||||
securityConfiguration: SecurityConfiguration,
|
||||
dependenciesConfiguration: DependenciesConfiguration,
|
||||
serializersConfiguration: SerializersConfiguration,
|
||||
) {
|
||||
private val applicationEngine = embeddedServer(engineFactory, port, host) {
|
||||
configureHTTP(allowedHost = host)
|
||||
configureSecurity(securityConfiguration)
|
||||
configureDependencies(dependenciesConfiguration)
|
||||
configureSerialization(serializersConfiguration)
|
||||
configureRouting()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server and blocks the current thread.
|
||||
*/
|
||||
fun start() = applicationEngine.start(wait = true)
|
||||
|
||||
/**
|
||||
* Stops the server.
|
||||
*/
|
||||
fun stop() = applicationEngine.stop()
|
||||
|
||||
/**
|
||||
* The security configuration.
|
||||
*
|
||||
* @property username The username.
|
||||
* @property password The password.
|
||||
*/
|
||||
class SecurityConfiguration(
|
||||
internal val username: String,
|
||||
internal val password: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* The dependencies configuration.
|
||||
*
|
||||
* @property storageRepository The storage repository.
|
||||
* @property patchSetRepository The patch set repository.
|
||||
* @property appRepository The app repository.
|
||||
* @property installerRepository The installer repository.
|
||||
*/
|
||||
class DependenciesConfiguration(
|
||||
internal val storageRepository: StorageRepository,
|
||||
internal val patchSetRepository: PatchSetRepository,
|
||||
internal val appRepository: AppRepository,
|
||||
internal val installerRepository: InstallerRepository,
|
||||
)
|
||||
|
||||
/**
|
||||
* The serializers configuration.
|
||||
*
|
||||
* @property patchOptionValueTypes A map of [PatchOption.valueType] to [KType] to add serializers for patch options
|
||||
* additional to the default ones.
|
||||
*/
|
||||
class SerializersConfiguration(
|
||||
internal val patchOptionValueTypes: Map<String, KType> = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A server builder.
|
||||
*
|
||||
* @property host The host.
|
||||
* @property port The port.
|
||||
* @property engineFactory The engine factory.
|
||||
* @property securityConfiguration The security configuration.
|
||||
* @property dependenciesConfiguration The dependencies configuration.
|
||||
*/
|
||||
class ServerBuilder internal constructor(
|
||||
private val host: String = "localhost",
|
||||
private val port: Int = 8080,
|
||||
private val engineFactory: ApplicationEngineFactory<*, *> = Netty,
|
||||
) {
|
||||
private lateinit var securityConfiguration: Server.SecurityConfiguration
|
||||
private lateinit var dependenciesConfiguration: Server.DependenciesConfiguration
|
||||
private var serializersConfiguration = Server.SerializersConfiguration()
|
||||
|
||||
/**
|
||||
* Configures the security.
|
||||
*
|
||||
* @param basicUsername The basic username.
|
||||
* @param basicPassword The basic password.
|
||||
*
|
||||
* @return The server builder.
|
||||
*/
|
||||
fun configureSecurity(
|
||||
basicUsername: String,
|
||||
basicPassword: String,
|
||||
) = apply {
|
||||
securityConfiguration = Server.SecurityConfiguration(
|
||||
username = basicUsername,
|
||||
password = basicPassword,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the dependencies.
|
||||
*
|
||||
* @param block The block to configure the dependencies.
|
||||
*
|
||||
* @return The server builder.
|
||||
*/
|
||||
fun configureDependencies(block: DependenciesConfigurationBuilder.() -> Unit) = apply {
|
||||
dependenciesConfiguration = DependenciesConfigurationBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the serializers.
|
||||
*
|
||||
* @param block The block to configure the serializers.
|
||||
*
|
||||
* @return The server builder.
|
||||
*/
|
||||
fun configureSerializers(block: SerializersConfigurationBuilder.() -> Unit) = apply {
|
||||
serializersConfiguration = SerializersConfigurationBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
class DependenciesConfigurationBuilder internal constructor() {
|
||||
private lateinit var storageRepository: StorageRepository
|
||||
private lateinit var patchSetRepository: PatchSetRepository
|
||||
private lateinit var appRepository: AppRepository
|
||||
private lateinit var installerRepository: InstallerRepository
|
||||
|
||||
fun configureStorageRepository(storageRepository: StorageRepository) = apply {
|
||||
this.storageRepository = storageRepository
|
||||
}
|
||||
|
||||
fun configurePatchSetRepository(patchSetRepository: PatchSetRepository) = apply {
|
||||
this.patchSetRepository = patchSetRepository
|
||||
}
|
||||
|
||||
fun configureAppRepository(appRepository: AppRepository) = apply {
|
||||
this.appRepository = appRepository
|
||||
}
|
||||
|
||||
fun configureInstallerRepository(installerRepository: InstallerRepository) = apply {
|
||||
this.installerRepository = installerRepository
|
||||
}
|
||||
|
||||
fun build() = Server.DependenciesConfiguration(
|
||||
storageRepository,
|
||||
patchSetRepository,
|
||||
appRepository,
|
||||
installerRepository,
|
||||
)
|
||||
}
|
||||
|
||||
class SerializersConfigurationBuilder internal constructor() {
|
||||
private lateinit var patchOptionValueTypes: Map<String, KType>
|
||||
|
||||
fun configurePatchOptionSerializers(vararg pairs: Pair<String, KType>) {
|
||||
this.patchOptionValueTypes = mapOf(*pairs)
|
||||
}
|
||||
|
||||
fun build() = Server.SerializersConfiguration(patchOptionValueTypes)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the server.
|
||||
*
|
||||
* @return The server.
|
||||
*/
|
||||
internal fun build() = Server(
|
||||
host,
|
||||
port,
|
||||
engineFactory,
|
||||
securityConfiguration,
|
||||
dependenciesConfiguration,
|
||||
serializersConfiguration,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a server.
|
||||
*
|
||||
* @param host The host.
|
||||
* @param port The port.
|
||||
* @param engineFactory The engine factory.
|
||||
* @param block The block to build the server.
|
||||
*
|
||||
* @return The server.
|
||||
*/
|
||||
fun server(
|
||||
host: String = "localhost",
|
||||
port: Int = 8080,
|
||||
engineFactory: ApplicationEngineFactory<*, *> = Netty,
|
||||
block: ServerBuilder.() -> Unit = {},
|
||||
) = ServerBuilder(host, port, engineFactory).apply(block).build()
|
||||
|
||||
fun main() {
|
||||
server {
|
||||
configureSecurity("username", "password")
|
||||
|
||||
val storageRepository = object : StorageRepository(
|
||||
temporaryFilesPath = File("temp"),
|
||||
keystoreFilePath = File("keystore.jks"),
|
||||
) {
|
||||
override fun readPatchBundles() = setOf(
|
||||
PatchBundle(
|
||||
"ReVanced Patches",
|
||||
File("D:\\ReVanced\\revanced-patches\\build\\libs\\revanced-patches-4.7.0-dev.2.jar"),
|
||||
),
|
||||
)
|
||||
|
||||
override fun writePatchBundles(patchBundles: Set<PatchBundle>) {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun newPatchBundle(patchBundleName: String, withIntegrations: Boolean): PatchBundle {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
val patchSetRepository = object : PatchSetRepository(storageRepository) {
|
||||
override fun readPatchSet(patchBundles: Set<PatchBundle>): PatchSet {
|
||||
return PatchBundleLoader.Jar(*patchBundles.map { it.patchBundleFile }.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
val appRepository = object : AppRepository() {
|
||||
override fun readInstalledApps() = emptySet<App>()
|
||||
}
|
||||
|
||||
val installerRepository = object : InstallerRepository() {
|
||||
override val installer = AdbInstaller("127.0.0.1:58526")
|
||||
}
|
||||
|
||||
configureDependencies {
|
||||
configureStorageRepository(storageRepository)
|
||||
configurePatchSetRepository(patchSetRepository)
|
||||
configureAppRepository(appRepository)
|
||||
configureInstallerRepository(installerRepository)
|
||||
}
|
||||
|
||||
configureSerializers {
|
||||
configurePatchOptionSerializers(
|
||||
"LocalDateTime" to typeOf<PatchOption<LocalDateTime>>(),
|
||||
)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package app.revanced.library.networking.configuration
|
||||
|
||||
import app.revanced.library.networking.Server
|
||||
import app.revanced.library.networking.services.HttpClientService
|
||||
import app.revanced.library.networking.services.PatchBundleService
|
||||
import app.revanced.library.networking.services.PatcherService
|
||||
import io.ktor.server.application.*
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
import org.koin.ktor.plugin.Koin
|
||||
|
||||
/**
|
||||
* Configure the dependencies for the application.
|
||||
*
|
||||
* @param dependenciesConfiguration The dependencies configuration.
|
||||
*/
|
||||
internal fun Application.configureDependencies(
|
||||
dependenciesConfiguration: Server.DependenciesConfiguration,
|
||||
) {
|
||||
val globalModule = module {
|
||||
single { dependenciesConfiguration.storageRepository }
|
||||
single { dependenciesConfiguration.patchSetRepository }
|
||||
single { dependenciesConfiguration.appRepository }
|
||||
single { dependenciesConfiguration.installerRepository }
|
||||
}
|
||||
|
||||
val patchBundleModule = module {
|
||||
single { HttpClientService() }
|
||||
singleOf(::PatchBundleService)
|
||||
}
|
||||
|
||||
val patcherModule = module {
|
||||
singleOf(::PatcherService)
|
||||
}
|
||||
|
||||
install(Koin) {
|
||||
modules(
|
||||
globalModule,
|
||||
patchBundleModule,
|
||||
patcherModule,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package app.revanced.library.networking.configuration
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.content.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.cachingheaders.*
|
||||
import io.ktor.server.plugins.conditionalheaders.*
|
||||
import io.ktor.server.plugins.cors.routing.*
|
||||
import io.ktor.server.websocket.*
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
/**
|
||||
* Configures HTTP for the application.
|
||||
*
|
||||
* @param allowedHost The allowed host for the application.
|
||||
*/
|
||||
internal fun Application.configureHTTP(
|
||||
allowedHost: String,
|
||||
) {
|
||||
install(ConditionalHeaders)
|
||||
install(CORS) {
|
||||
allowMethod(HttpMethod.Options)
|
||||
allowMethod(HttpMethod.Put)
|
||||
allowMethod(HttpMethod.Delete)
|
||||
allowMethod(HttpMethod.Patch)
|
||||
allowHeader(HttpHeaders.Authorization)
|
||||
allowHost(allowedHost)
|
||||
}
|
||||
install(WebSockets)
|
||||
install(CachingHeaders) {
|
||||
options { _, _ -> CachingOptions(CacheControl.MaxAge(maxAgeSeconds = 5.minutes.inWholeSeconds.toInt())) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package app.revanced.library.networking.configuration
|
||||
|
||||
import app.revanced.library.networking.Server
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
|
||||
/**
|
||||
* Configures the security for the application.
|
||||
*
|
||||
* @param securityConfiguration The security configuration.
|
||||
*/
|
||||
internal fun Application.configureSecurity(
|
||||
securityConfiguration: Server.SecurityConfiguration,
|
||||
) {
|
||||
install(Authentication) {
|
||||
basic {
|
||||
validate { credentials ->
|
||||
if (credentials.name == securityConfiguration.username &&
|
||||
credentials.password == securityConfiguration.password
|
||||
) {
|
||||
UserIdPrincipal(credentials.name)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package app.revanced.library.networking.configuration
|
||||
|
||||
import app.revanced.library.networking.Server
|
||||
import app.revanced.library.networking.models.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.plugins.contentnegotiation.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.SetSerializer
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.encoding.decodeStructure
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.SerializersModuleBuilder
|
||||
import kotlinx.serialization.modules.contextual
|
||||
import kotlinx.serialization.serializer
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* Configures the serialization for the application.
|
||||
*
|
||||
* @param serializersConfiguration The serializers configuration.
|
||||
*/
|
||||
fun Application.configureSerialization(serializersConfiguration: Server.SerializersConfiguration) {
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
serializersModule = SerializersModule {
|
||||
configurePatchOptionSerializers(serializersConfiguration.patchOptionValueTypes)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the patch option serializers.
|
||||
*
|
||||
* @param patchOptionValueTypes A map of [PatchOption.valueType] to [KType] to add serializers for patch options
|
||||
* additional to the default ones.
|
||||
*/
|
||||
private fun SerializersModuleBuilder.configurePatchOptionSerializers(patchOptionValueTypes: Map<String, KType>) {
|
||||
val knownPatchOptionValueTypes = mapOf(
|
||||
"String" to typeOf<Patch.PatchOption<String>>(),
|
||||
"Int" to typeOf<Patch.PatchOption<Int>>(),
|
||||
"Boolean" to typeOf<Patch.PatchOption<Boolean>>(),
|
||||
"Long" to typeOf<Patch.PatchOption<Long>>(),
|
||||
"Float" to typeOf<Patch.PatchOption<Float>>(),
|
||||
"StringArray" to typeOf<Patch.PatchOption<Array<String>>>(),
|
||||
"IntArray" to typeOf<Patch.PatchOption<IntArray>>(),
|
||||
"BooleanArray" to typeOf<Patch.PatchOption<BooleanArray>>(),
|
||||
"LongArray" to typeOf<Patch.PatchOption<LongArray>>(),
|
||||
"FloatArray" to typeOf<Patch.PatchOption<FloatArray>>(),
|
||||
) + patchOptionValueTypes
|
||||
|
||||
/**
|
||||
* Gets the [KType] for a patch option value type.
|
||||
*
|
||||
* @param valueType The value type of the patch option.
|
||||
*
|
||||
* @return The [KType] for the patch option value type.
|
||||
*/
|
||||
fun patchOptionTypeOf(valueType: String) = knownPatchOptionValueTypes[valueType]
|
||||
?: error("Unknown patch option value type: $valueType")
|
||||
|
||||
/**
|
||||
* Serializer for [Patch.PatchOption].
|
||||
* Uses the [Patch.PatchOption.valueType] to determine the serializer for the generic type.
|
||||
*/
|
||||
val patchOptionSerializer = object : KSerializer<Patch.PatchOption<*>> {
|
||||
override val descriptor = serializer(typeOf<Patch.PatchOption<Serializable>>()).descriptor
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Patch.PatchOption<*>) = serializer(
|
||||
patchOptionTypeOf(value.valueType),
|
||||
).serialize(encoder, value)
|
||||
|
||||
override fun deserialize(decoder: Decoder) = serializer(
|
||||
patchOptionTypeOf(
|
||||
decoder.decodeStructure(descriptor) {
|
||||
decodeStringElement(descriptor, descriptor.getElementIndex("valueType"))
|
||||
},
|
||||
),
|
||||
).deserialize(decoder) as Patch.PatchOption<*>
|
||||
}
|
||||
|
||||
contextual(patchOptionSerializer)
|
||||
contextual(SetSerializer(patchOptionSerializer))
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package app.revanced.library.networking.configuration.repository
|
||||
|
||||
import app.revanced.library.networking.models.App
|
||||
|
||||
/**
|
||||
* A repository for apps and installers.
|
||||
*/
|
||||
abstract class AppRepository {
|
||||
/**
|
||||
* The set of [App] installed.
|
||||
*/
|
||||
internal lateinit var installedApps: Set<App>
|
||||
private set
|
||||
|
||||
init {
|
||||
readAndSetInstalledApps()
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a set of [App] from a storage.
|
||||
*
|
||||
* @return The set of [App] read.
|
||||
*/
|
||||
internal abstract fun readInstalledApps(): Set<App>
|
||||
|
||||
/**
|
||||
* Read a set of [App] using [readInstalledApps] and set [installedApps] to it.
|
||||
*/
|
||||
internal fun readAndSetInstalledApps() {
|
||||
this.installedApps = readInstalledApps()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.library.networking.configuration.repository
|
||||
|
||||
import app.revanced.library.installation.installer.Installer
|
||||
import app.revanced.library.installation.installer.MountInstaller
|
||||
import app.revanced.library.networking.models.App
|
||||
|
||||
abstract class InstallerRepository {
|
||||
/**
|
||||
* The installer to use for installing and uninstalling [App]s.
|
||||
*/
|
||||
internal abstract val installer: Installer<*, *>
|
||||
|
||||
/**
|
||||
* The root installer to use for mounting and unmounting [App]s.
|
||||
*/
|
||||
internal open val mountInstaller: MountInstaller? = null
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package app.revanced.library.networking.configuration.repository
|
||||
|
||||
import app.revanced.library.networking.models.PatchBundle
|
||||
import app.revanced.patcher.PatchBundleLoader
|
||||
import app.revanced.patcher.PatchSet
|
||||
import app.revanced.patcher.patch.Patch
|
||||
|
||||
/**
|
||||
* A repository for patches from a set of [PatchBundle]s.
|
||||
*
|
||||
* @param storageRepository The [StorageRepository] to read the [PatchBundle]s from.
|
||||
*/
|
||||
abstract class PatchSetRepository(
|
||||
private val storageRepository: StorageRepository,
|
||||
) {
|
||||
/**
|
||||
* The set of [Patch]es loaded from [StorageRepository.patchBundles].
|
||||
*/
|
||||
internal lateinit var patchSet: PatchSet
|
||||
private set
|
||||
|
||||
init {
|
||||
readAndSetPatchSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a [PatchSet] from a set of [patchBundles] using a [PatchBundleLoader].
|
||||
*
|
||||
* @param patchBundles The set of [PatchBundle]s to read the [PatchSet] from.
|
||||
*/
|
||||
internal abstract fun readPatchSet(patchBundles: Set<PatchBundle>): PatchSet
|
||||
|
||||
/**
|
||||
* Read a [PatchSet] from patch bundles from [storageRepository] using [readPatchSet] and set [patchSet] to it.
|
||||
*/
|
||||
internal fun readAndSetPatchSet() {
|
||||
this.patchSet = readPatchSet(storageRepository.patchBundles.values.toSet())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package app.revanced.library.networking.configuration.repository
|
||||
|
||||
import app.revanced.library.networking.models.PatchBundle
|
||||
import app.revanced.patcher.Patcher
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* A repository for storage.
|
||||
*
|
||||
* @param temporaryFilesPath The path to the temporary files for [Patcher].
|
||||
* @param outputFilePath The path to the output file to save patched APKs to.
|
||||
* @param keystoreFilePath The path to the keystore file to sign patched APKs with.
|
||||
* @param aaptBinaryPath The path to the aapt binary to use by [Patcher].
|
||||
*/
|
||||
abstract class StorageRepository(
|
||||
val temporaryFilesPath: File,
|
||||
val outputFilePath: File = File(temporaryFilesPath, "output.apk"),
|
||||
val keystoreFilePath: File,
|
||||
val aaptBinaryPath: File? = null,
|
||||
) {
|
||||
/**
|
||||
* The stored [PatchBundle]s mapped by their name.
|
||||
*/
|
||||
internal lateinit var patchBundles: MutableMap<String, PatchBundle>
|
||||
private set
|
||||
|
||||
/**
|
||||
* The path to save the patched, but unsigned APK to.
|
||||
*/
|
||||
internal val unsignedApkFilePath = File(temporaryFilesPath, "unsigned.apk")
|
||||
|
||||
init {
|
||||
readAndSetPatchBundles()
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a set of [patchBundles] from a storage.
|
||||
*
|
||||
* @return The set of [PatchBundle] read.
|
||||
*/
|
||||
internal abstract fun readPatchBundles(): Set<PatchBundle>
|
||||
|
||||
/**
|
||||
* Write a set of [patchBundles] to a storage.
|
||||
*
|
||||
* @param patchBundles The set of patch bundles to write.
|
||||
*/
|
||||
internal abstract fun writePatchBundles(patchBundles: Set<PatchBundle>)
|
||||
|
||||
/**
|
||||
* Create a new [PatchBundle] in a storage to write to.
|
||||
*
|
||||
* @param patchBundleName The name of the patch bundle.
|
||||
* @param withIntegrations Whether the patch bundle also has integrations.
|
||||
*
|
||||
* @return The new [PatchBundle] created.
|
||||
*/
|
||||
internal abstract fun newPatchBundle(patchBundleName: String, withIntegrations: Boolean): PatchBundle
|
||||
|
||||
/**
|
||||
* Read the set of [patchBundles] stored and set it to [patchBundles].
|
||||
*/
|
||||
internal fun readAndSetPatchBundles() {
|
||||
patchBundles = readPatchBundles().associateBy { it.name }.toMutableMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a [patchBundle] to the map of the stored [patchBundles] and write the set to a storage using [writePatchBundles].
|
||||
*
|
||||
* @param patchBundle The patch bundle to add.
|
||||
*/
|
||||
internal fun addPersistentlyPatchBundle(patchBundle: PatchBundle) {
|
||||
patchBundles[patchBundle.name] = patchBundle
|
||||
writePatchBundles(patchBundles.values.toSet())
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a path bundle from the map of [patchBundles] stored and write the set to a storage using [writePatchBundles].
|
||||
*
|
||||
* @param patchBundleName The name of the patch bundle to remove.
|
||||
*/
|
||||
internal fun removePersistentlyPatchBundle(patchBundleName: String) {
|
||||
patchBundles.remove(patchBundleName)
|
||||
writePatchBundles(patchBundles.values.toSet())
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the temporary files.
|
||||
*/
|
||||
internal fun deleteTemporaryFiles() {
|
||||
temporaryFilesPath.deleteRecursively()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.revanced.library.networking.configuration.routing
|
||||
|
||||
import app.revanced.library.networking.configuration.routing.routes.configurePatchBundlesRoute
|
||||
import app.revanced.library.networking.configuration.routing.routes.configurePatcherRoute
|
||||
import app.revanced.library.networking.configuration.routing.routes.configurePingRoute
|
||||
import app.revanced.library.networking.configuration.routing.routes.configureRootRoute
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.auth.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
/**
|
||||
* Configures the routing for the application.
|
||||
*/
|
||||
internal fun Application.configureRouting() {
|
||||
routing {
|
||||
authenticate {
|
||||
configureRootRoute()
|
||||
configurePingRoute()
|
||||
configurePatchBundlesRoute()
|
||||
configurePatcherRoute()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package app.revanced.library.networking.configuration.routing.routes
|
||||
|
||||
import app.revanced.library.networking.parameters
|
||||
import app.revanced.library.networking.services.PatchBundleService
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.util.*
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
/**
|
||||
* Route to handle all patch bundle related requests such as creating, reading, updating and deleting patch bundles.
|
||||
*/
|
||||
internal fun Route.configurePatchBundlesRoute() {
|
||||
val patchBundleService = get<PatchBundleService>()
|
||||
|
||||
route("/patch-bundles") {
|
||||
get {
|
||||
call.respond(patchBundleService.patchBundleNames)
|
||||
}
|
||||
|
||||
post("/add") {
|
||||
val patchBundleName: String by parameters
|
||||
val patchBundleFilePath = parameters["patchBundleFilePath"]
|
||||
|
||||
if (patchBundleFilePath != null) {
|
||||
val patchBundleIntegrationsFilePath = parameters["patchBundleIntegrationsFilePath"]
|
||||
|
||||
patchBundleService.addPersistentlyLocalPatchBundle(
|
||||
patchBundleName,
|
||||
patchBundleFilePath,
|
||||
patchBundleIntegrationsFilePath,
|
||||
)
|
||||
} else {
|
||||
val patchBundleDownloadLink: String by parameters
|
||||
val patchBundleIntegrationsDownloadLink = parameters["patchBundleIntegrationsDownloadLink"]
|
||||
|
||||
patchBundleService.addPersistentlyDownloadPatchBundle(
|
||||
patchBundleName,
|
||||
patchBundleDownloadLink,
|
||||
patchBundleIntegrationsDownloadLink,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
post("/remove") {
|
||||
val patchBundleName: String by parameters
|
||||
|
||||
patchBundleService.removePersistentlyPatchBundle(patchBundleName)
|
||||
}
|
||||
|
||||
post("/refresh") {
|
||||
patchBundleService.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package app.revanced.library.networking.configuration.routing.routes
|
||||
|
||||
import app.revanced.library.networking.configuration.repository.InstallerRepository
|
||||
import app.revanced.library.networking.models.Patch
|
||||
import app.revanced.library.networking.parameters
|
||||
import app.revanced.library.networking.services.PatcherService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.util.*
|
||||
import org.koin.ktor.ext.get
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Route to the patcher to handles all patcher related requests such as patching, signing and installing patched apps.
|
||||
*/
|
||||
internal fun Route.configurePatcherRoute() {
|
||||
route("/patcher") {
|
||||
configureAppsRoute()
|
||||
configurePatchesRoute()
|
||||
configurePatchOptionsRoute()
|
||||
configurePatchRoute()
|
||||
configureSignRoute()
|
||||
configureInstallationRoute()
|
||||
configureCleanRoute()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to list all patchable apps that can be patched.
|
||||
*/
|
||||
private fun Route.configureAppsRoute() {
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
get("/apps") {
|
||||
val universal = parameters.contains("universal")
|
||||
|
||||
call.respond(patcherService.getInstalledApps(universal))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to get all patches for a specific app and version.
|
||||
*/
|
||||
private fun Route.configurePatchesRoute() {
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
get("/patches") {
|
||||
val app = parameters["app"]
|
||||
val version = parameters["version"]
|
||||
val universal = "universal" in parameters
|
||||
|
||||
call.respond(patcherService.getPatches(app, version, universal))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to get and set patch options.
|
||||
*/
|
||||
private fun Route.configurePatchOptionsRoute() {
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
route("/options") {
|
||||
get {
|
||||
val app: String by parameters
|
||||
val patch: String by parameters
|
||||
|
||||
call.respond(patcherService.getPatchOptions(patchName = patch, app))
|
||||
}
|
||||
|
||||
post {
|
||||
// Abuse serialization capabilities of Patch.PatchOption
|
||||
// because Patch.KeyValuePatchOption isn't serializable.
|
||||
// ONLY the Patch.PatchOption.key and Patch.PatchOption.value properties are used here.
|
||||
val patchOptions: Set<Patch.PatchOption<*>> by call.receive()
|
||||
val patch: String by parameters
|
||||
val app: String by parameters
|
||||
|
||||
patcherService.setPatchOptions(
|
||||
// Use Patch.PatchOption.default for Patch.KeyValuePatchOption.value.
|
||||
patchOptions = patchOptions.map { Patch.KeyValuePatchOption(it) }.toSet(),
|
||||
patchName = patch,
|
||||
app,
|
||||
)
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
|
||||
delete {
|
||||
val patch: String by parameters
|
||||
val app: String by parameters
|
||||
|
||||
patcherService.resetPatchOptions(patchName = patch, app)
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to patch an app with a set of patches.
|
||||
*/
|
||||
private fun Route.configurePatchRoute() {
|
||||
val installerRepository = get<InstallerRepository>()
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
post("/patch") {
|
||||
val patchNames = parameters.getAll("patch")?.toSet() ?: emptySet()
|
||||
val multithreading = "multithreading" in parameters
|
||||
|
||||
// TODO: The path to the APK must be local to the server, otherwise it will not work.
|
||||
val apkPath = parameters["app"]?.let {
|
||||
installerRepository.installer.getInstallation(it)?.apkFilePath
|
||||
} ?: parameters["apkPath"]
|
||||
val apkFile = File(apkPath ?: return@post call.respond(HttpStatusCode.BadRequest))
|
||||
|
||||
patcherService.patch(patchNames, multithreading, apkFile)
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to sign the patched APK.
|
||||
*/
|
||||
private fun Route.configureSignRoute() {
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
post("/sign") {
|
||||
val signer: String by parameters
|
||||
val keyStorePassword = parameters["keyStorePassword"]
|
||||
val keyStoreEntryAlias: String by parameters
|
||||
val keyStoreEntryPassword: String by parameters
|
||||
|
||||
patcherService.sign(signer, keyStorePassword, keyStoreEntryAlias, keyStoreEntryPassword)
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to install or uninstall a patched APK.
|
||||
*/
|
||||
private fun Route.configureInstallationRoute() {
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
post("/install") {
|
||||
val mount = parameters["mount"]
|
||||
|
||||
patcherService.install(mount)
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
|
||||
post("/uninstall") {
|
||||
val packageName: String by parameters
|
||||
val unmount = "unmount" in parameters
|
||||
|
||||
patcherService.uninstall(packageName, unmount)
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to delete temporary files produced by the patcher.
|
||||
*/
|
||||
private fun Route.configureCleanRoute() {
|
||||
val patcherService = get<PatcherService>()
|
||||
|
||||
post("/clean") {
|
||||
patcherService.deleteTemporaryFiles()
|
||||
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.library.networking.configuration.routing.routes
|
||||
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
/**
|
||||
* Route to check if the server is up.
|
||||
*/
|
||||
internal fun Route.configurePingRoute() {
|
||||
head("/ping") {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package app.revanced.library.networking.configuration.routing.routes
|
||||
|
||||
import app.revanced.library.logging.Logger
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import io.ktor.server.websocket.*
|
||||
import io.ktor.websocket.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.*
|
||||
|
||||
internal fun Route.configureRootRoute() {
|
||||
route("/") {
|
||||
configureAboutRoute()
|
||||
configureLoggingRoute()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route to get information about the server.
|
||||
*/
|
||||
private fun Route.configureAboutRoute() {
|
||||
val name = this::class.java.getResourceAsStream(
|
||||
"/app/revanced/library/networking/version.properties",
|
||||
)?.use { stream ->
|
||||
Properties().apply {
|
||||
load(stream)
|
||||
}.let {
|
||||
"ReVanced Networking Library v${it.getProperty("version")}"
|
||||
}
|
||||
} ?: "ReVanced Networking Library"
|
||||
|
||||
handle {
|
||||
call.respondText(name)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix clients disconnecting from the server.
|
||||
/**
|
||||
* Route to get logs from the server.
|
||||
*/
|
||||
private fun Route.configureLoggingRoute() {
|
||||
val sessions = Collections.synchronizedSet<DefaultWebSocketSession?>(LinkedHashSet())
|
||||
|
||||
Logger.addHandler({ log: String, level: java.util.logging.Level, loggerName: String? ->
|
||||
runBlocking {
|
||||
sessions.forEach {
|
||||
try {
|
||||
it.send("[$loggerName] $level: $log")
|
||||
} catch (e: Exception) {
|
||||
sessions -= it
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {}, {})
|
||||
|
||||
webSocket("/logs") {
|
||||
sessions += this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package app.revanced.library.networking.models
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import java.io.File
|
||||
|
||||
private typealias PackageName = String
|
||||
private typealias PackageVersion = String
|
||||
private typealias PackageVersions = Set<PackageVersion>
|
||||
private typealias CompatiblePackages = Map<PackageName, PackageVersions?>
|
||||
|
||||
@Serializable
|
||||
open class App(
|
||||
internal val name: String,
|
||||
internal val version: String,
|
||||
internal val packageName: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
class Patch internal constructor(
|
||||
internal val name: String,
|
||||
internal val description: String?,
|
||||
internal val use: Boolean,
|
||||
internal val compatiblePackages: CompatiblePackages?,
|
||||
) {
|
||||
@Serializable
|
||||
class PatchOption<T> internal constructor(
|
||||
internal val key: String,
|
||||
internal val default: T?,
|
||||
internal val values: Map<String, T?>?,
|
||||
internal val title: String?,
|
||||
internal val description: String?,
|
||||
internal val required: Boolean,
|
||||
internal val valueType: String,
|
||||
)
|
||||
|
||||
class KeyValuePatchOption<T>(
|
||||
val key: String,
|
||||
val value: T?,
|
||||
val valueType: String,
|
||||
) {
|
||||
// Abuse serialization capabilities of Patch.PatchOption which is used in request bodies.
|
||||
// Use Patch.PatchOption.default as Patch.KeyValuePatchOption.value.
|
||||
internal constructor(patchOption: PatchOption<T>) : this(
|
||||
patchOption.key,
|
||||
patchOption.default,
|
||||
patchOption.valueType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class PatchBundle internal constructor(
|
||||
val name: String,
|
||||
val patchBundleFile: File,
|
||||
val patchBundleIntegrationsFile: File? = null,
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
package app.revanced.library.networking.services
|
||||
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.engine.cio.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.utils.io.*
|
||||
import io.ktor.utils.io.jvm.javaio.*
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Service for HTTP client.
|
||||
*/
|
||||
internal class HttpClientService {
|
||||
private val client by lazy { HttpClient(CIO) }
|
||||
|
||||
/**
|
||||
* Download a file from a URL to a file.
|
||||
*
|
||||
* @param file The file to download to.
|
||||
* @param url The URL to download from.
|
||||
*/
|
||||
internal suspend fun downloadToFile(file: File, url: String) {
|
||||
client.get(url).body<ByteReadChannel>().copyTo(file.outputStream())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package app.revanced.library.networking.services
|
||||
|
||||
import app.revanced.library.networking.configuration.repository.PatchSetRepository
|
||||
import app.revanced.library.networking.configuration.repository.StorageRepository
|
||||
import app.revanced.library.networking.models.PatchBundle
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Service for patch bundles.
|
||||
*
|
||||
* @property storageRepository The storage repository to get storage paths from.
|
||||
* @property patchSetRepository The patch set repository to get patches from.
|
||||
* @property httpClientService The HTTP client service to download patch bundles with.
|
||||
*/
|
||||
internal class PatchBundleService(
|
||||
private val storageRepository: StorageRepository,
|
||||
private val patchSetRepository: PatchSetRepository,
|
||||
private val httpClientService: HttpClientService,
|
||||
) {
|
||||
/**
|
||||
* Get the names of the patch bundles stored.
|
||||
*
|
||||
* @return The set of patch bundle names.
|
||||
*/
|
||||
internal val patchBundleNames: Set<String>
|
||||
get() = storageRepository.patchBundles.keys.toSet()
|
||||
|
||||
/**
|
||||
* Add a local patch bundle to storage persistently.
|
||||
*
|
||||
* @param patchBundleName The name of the patch bundle.
|
||||
* @param patchBundleFilePath The path to the patch bundle file.
|
||||
* @param patchBundleIntegrationsFilePath The path to the patch bundle integrations file.
|
||||
*/
|
||||
internal fun addPersistentlyLocalPatchBundle(
|
||||
patchBundleName: String,
|
||||
patchBundleFilePath: String,
|
||||
patchBundleIntegrationsFilePath: String?,
|
||||
) = storageRepository.addPersistentlyPatchBundle(
|
||||
PatchBundle(
|
||||
name = patchBundleName,
|
||||
patchBundleFile = File(patchBundleFilePath),
|
||||
patchBundleIntegrationsFile = patchBundleIntegrationsFilePath?.let { File(it) },
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Add a patch bundle that needs to be downloaded to storage persistently.
|
||||
*
|
||||
* @param patchBundleName The name of the patch bundle.
|
||||
* @param patchBundleDownloadLink The download link to the patch bundle.
|
||||
* @param patchBundleIntegrationsDownloadLink The download link to the patch bundle integrations.
|
||||
*/
|
||||
internal suspend fun addPersistentlyDownloadPatchBundle(
|
||||
patchBundleName: String,
|
||||
patchBundleDownloadLink: String,
|
||||
patchBundleIntegrationsDownloadLink: String?,
|
||||
) {
|
||||
val withIntegrations = patchBundleIntegrationsDownloadLink != null
|
||||
|
||||
storageRepository.newPatchBundle(patchBundleName, withIntegrations).apply {
|
||||
httpClientService.downloadToFile(patchBundleFile, patchBundleDownloadLink)
|
||||
|
||||
if (withIntegrations) {
|
||||
httpClientService.downloadToFile(patchBundleIntegrationsFile!!, patchBundleIntegrationsDownloadLink!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a patch bundle from storage persistently.
|
||||
*
|
||||
* @param name The name of the patch bundle to remove.
|
||||
*/
|
||||
internal fun removePersistentlyPatchBundle(name: String) =
|
||||
storageRepository.removePersistentlyPatchBundle(name)
|
||||
|
||||
/**
|
||||
* Reload the patch bundles from storage and read the patch set from them.
|
||||
*/
|
||||
internal fun refresh() {
|
||||
storageRepository.readAndSetPatchBundles()
|
||||
patchSetRepository.readAndSetPatchSet()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package app.revanced.library.networking.services
|
||||
|
||||
import app.revanced.library.ApkUtils
|
||||
import app.revanced.library.ApkUtils.applyTo
|
||||
import app.revanced.library.installation.installer.Installer
|
||||
import app.revanced.library.networking.configuration.repository.AppRepository
|
||||
import app.revanced.library.networking.configuration.repository.InstallerRepository
|
||||
import app.revanced.library.networking.configuration.repository.PatchSetRepository
|
||||
import app.revanced.library.networking.configuration.repository.StorageRepository
|
||||
import app.revanced.library.networking.models.App
|
||||
import app.revanced.library.networking.models.Patch
|
||||
import app.revanced.patcher.Patcher
|
||||
import app.revanced.patcher.PatcherConfig
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.util.logging.Logger
|
||||
|
||||
/**
|
||||
* Service for patching and installing apps.
|
||||
*
|
||||
* @property storageRepository The storage repository to get storage paths from.
|
||||
* @property patchSetRepository The patch set repository to get patches from.
|
||||
* @property appRepository The app repository to get installed apps from.
|
||||
* @property installerRepository The installer repository to install apps with.
|
||||
*/
|
||||
internal class PatcherService(
|
||||
private val storageRepository: StorageRepository,
|
||||
private val patchSetRepository: PatchSetRepository,
|
||||
private val appRepository: AppRepository,
|
||||
private val installerRepository: InstallerRepository,
|
||||
) {
|
||||
private val logger = Logger.getLogger(PatcherService::class.simpleName)
|
||||
|
||||
/**
|
||||
* Get installed apps.
|
||||
*
|
||||
* @param universal Whether to show apps that only have universal patches.
|
||||
*
|
||||
* @return The installed apps.
|
||||
*/
|
||||
internal fun getInstalledApps(universal: Boolean = true): Set<App> {
|
||||
// TODO: Show apps, that only have universal patches, only if universal is true.
|
||||
return appRepository.installedApps
|
||||
}
|
||||
|
||||
/**
|
||||
* Get patches.
|
||||
*
|
||||
* @param app The app to get patches for.
|
||||
* @param version The version of the app to get patches for.
|
||||
* @param universal Whether to show patches that are compatible with all apps.
|
||||
*
|
||||
* @return The patches.
|
||||
*/
|
||||
internal fun getPatches(
|
||||
app: String? = null,
|
||||
version: String? = null,
|
||||
universal: Boolean = true,
|
||||
) = if (app != null) {
|
||||
patchSetRepository.patchSet.filter { patch ->
|
||||
patch.compatiblePackages?.any { pkg ->
|
||||
pkg.name == app && (version == null || pkg.versions?.contains(version) ?: false)
|
||||
} ?: universal
|
||||
}
|
||||
} else {
|
||||
patchSetRepository.patchSet.filter { patch ->
|
||||
patch.compatiblePackages != null || universal
|
||||
}
|
||||
}.map { patch ->
|
||||
Patch(
|
||||
patch.name!!,
|
||||
patch.description,
|
||||
patch.use,
|
||||
patch.compatiblePackages?.associate { pkg -> pkg.name to pkg.versions },
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
/**
|
||||
* Patch an app.
|
||||
* Due to the likely-hood, that patches for the same app have the same name, duplicates are unhandled.
|
||||
*
|
||||
* @param patchNames The names of the patches to apply.
|
||||
* @param multithreading Whether to use multi-threading for dex file writing.
|
||||
* @param apkFile The APK file to patch.
|
||||
*/
|
||||
internal suspend fun patch(
|
||||
patchNames: Set<String>,
|
||||
multithreading: Boolean = false,
|
||||
apkFile: File,
|
||||
) = Patcher(
|
||||
PatcherConfig(
|
||||
apkFile = apkFile,
|
||||
temporaryFilesPath = storageRepository.temporaryFilesPath,
|
||||
aaptBinaryPath = storageRepository.aaptBinaryPath?.absolutePath,
|
||||
frameworkFileDirectory = storageRepository.temporaryFilesPath.absolutePath,
|
||||
multithreadingDexFileWriter = multithreading,
|
||||
),
|
||||
).use { patcher ->
|
||||
val packageName = patcher.context.packageMetadata.packageName
|
||||
|
||||
patcher.apply {
|
||||
acceptPatches(
|
||||
patchSetRepository.patchSet.filter { patch ->
|
||||
patch.name in patchNames && patch.compatiblePackages?.any { it.name == packageName } ?: true
|
||||
}.toSet(),
|
||||
)
|
||||
|
||||
// TODO: Only accept integrations from patch bundles that contain selected patches.
|
||||
acceptIntegrations(
|
||||
storageRepository.patchBundles.values.mapNotNull {
|
||||
it.patchBundleIntegrationsFile
|
||||
}.toSet(),
|
||||
)
|
||||
}
|
||||
|
||||
patcher.apply(false).collect { patchResult ->
|
||||
patchResult.exception?.let {
|
||||
StringWriter().use { writer ->
|
||||
it.printStackTrace(PrintWriter(writer))
|
||||
logger.severe("${patchResult.patch.name} failed:\n$writer")
|
||||
}
|
||||
} ?: logger.info("${patchResult.patch.name} succeeded")
|
||||
}
|
||||
|
||||
patcher.get()
|
||||
}.let { patcherResult ->
|
||||
apkFile.copyTo(storageRepository.unsignedApkFilePath, overwrite = true).apply {
|
||||
patcherResult.applyTo(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign an APK.
|
||||
*
|
||||
* @param signer The signer to use.
|
||||
* @param keyStorePassword The password of the keystore.
|
||||
* @param keyStoreEntryAlias The alias of the keystore entry.
|
||||
* @param keyStoreEntryPassword The password of the keystore entry.
|
||||
*/
|
||||
internal fun sign(
|
||||
signer: String,
|
||||
keyStorePassword: String?,
|
||||
keyStoreEntryAlias: String,
|
||||
keyStoreEntryPassword: String,
|
||||
) = ApkUtils.signApk(
|
||||
storageRepository.unsignedApkFilePath,
|
||||
storageRepository.outputFilePath,
|
||||
signer,
|
||||
ApkUtils.KeyStoreDetails(
|
||||
storageRepository.keystoreFilePath,
|
||||
keyStorePassword,
|
||||
keyStoreEntryAlias,
|
||||
keyStoreEntryPassword,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Install an APK.
|
||||
*
|
||||
* @param mount The package name to mount the APK to.
|
||||
*/
|
||||
internal suspend fun install(mount: String?) {
|
||||
if (mount != null) {
|
||||
if (installerRepository.mountInstaller == null) {
|
||||
throw IllegalArgumentException("Mount installer not available")
|
||||
}
|
||||
|
||||
installerRepository.mountInstaller!! to Installer.Apk(
|
||||
storageRepository.unsignedApkFilePath,
|
||||
packageName = mount,
|
||||
)
|
||||
} else {
|
||||
installerRepository.installer to Installer.Apk(storageRepository.outputFilePath)
|
||||
}.let { (installer, apk) ->
|
||||
installer.install(apk)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall an APK.
|
||||
*
|
||||
* @param packageName The package name of the APK to uninstall.
|
||||
* @param unmount Whether to uninstall a mounted APK.
|
||||
*/
|
||||
internal suspend fun uninstall(packageName: String, unmount: Boolean) = if (unmount) {
|
||||
installerRepository.mountInstaller!!
|
||||
} else {
|
||||
installerRepository.installer
|
||||
}.uninstall(packageName)
|
||||
|
||||
/**
|
||||
* Get patch options from [PatchSetRepository.patchSet].
|
||||
* The [app] parameter is necessary in case there are patches with the same name.
|
||||
* Due to the likely-hood, that patches for the same app have the same name, duplicates are unhandled.
|
||||
*
|
||||
* @param patchName The name of the patch to get options for.
|
||||
* @param app The app to get options for.
|
||||
*
|
||||
* @return The patch options for the patch.
|
||||
*/
|
||||
internal fun getPatchOptions(patchName: String, app: String) = patchSetRepository.patchSet.single { patch ->
|
||||
patch.name == patchName && patch.compatiblePackages?.any { it.name == app } ?: true
|
||||
}.options.map { (key, option) ->
|
||||
Patch.PatchOption(
|
||||
key,
|
||||
option.default,
|
||||
option.values,
|
||||
option.title,
|
||||
option.description,
|
||||
option.required,
|
||||
option.valueType,
|
||||
)
|
||||
}.toSet()
|
||||
|
||||
/**
|
||||
* Set patch options.
|
||||
* The [app] parameter is necessary in case there are patches with the same name.
|
||||
* Due to the likely-hood, that patches for the same app have the same name, duplicates are unhandled.
|
||||
*
|
||||
* @param patchOptions The options to set.
|
||||
* @param patchName The name of the patch to set options for.
|
||||
* @param app The app to set options for.
|
||||
*/
|
||||
internal fun setPatchOptions(
|
||||
patchOptions: Set<Patch.KeyValuePatchOption<*>>,
|
||||
patchName: String,
|
||||
app: String,
|
||||
) = patchSetRepository.patchSet.single { patch ->
|
||||
patch.name == patchName && patch.compatiblePackages?.any { it.name == app } ?: true
|
||||
}.options.let { options ->
|
||||
patchOptions.forEach { option ->
|
||||
options[option.key] = option.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset patch options and persist them to the storage.
|
||||
*
|
||||
* @param patchName The name of the patch to reset options for.
|
||||
* @param app The app to reset options for.
|
||||
*/
|
||||
internal fun resetPatchOptions(patchName: String, app: String) {
|
||||
patchSetRepository.patchSet.single { patch ->
|
||||
patch.name == patchName && patch.compatiblePackages?.any { it.name == app } ?: true
|
||||
}.options.forEach { (_, option) -> option.reset() }
|
||||
}
|
||||
|
||||
internal fun deleteTemporaryFiles() = storageRepository.deleteTemporaryFiles()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
version=${projectVersion}
|
||||
@@ -249,7 +249,7 @@ public final class app/revanced/library/installation/installer/AdbInstallerResul
|
||||
public static final field INSTANCE Lapp/revanced/library/installation/installer/AdbInstallerResult$Success;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/AdbRootInstaller : app/revanced/library/installation/installer/RootInstaller {
|
||||
public final class app/revanced/library/installation/installer/AdbMountInstaller : app/revanced/library/installation/installer/MountInstaller {
|
||||
public fun <init> ()V
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
@@ -297,18 +297,18 @@ public final class app/revanced/library/installation/installer/LocalInstallerSer
|
||||
public fun onStartCommand (Landroid/content/Intent;II)I
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/LocalRootInstaller : app/revanced/library/installation/installer/RootInstaller, java/io/Closeable {
|
||||
public final class app/revanced/library/installation/installer/LocalMountInstaller : app/revanced/library/installation/installer/MountInstaller, java/io/Closeable {
|
||||
public fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun close ()V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/RootInstallation : app/revanced/library/installation/installer/Installation {
|
||||
public final class app/revanced/library/installation/installer/MountInstallation : app/revanced/library/installation/installer/Installation {
|
||||
public final fun getInstalledApkFilePath ()Ljava/lang/String;
|
||||
public final fun getMounted ()Z
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/installation/installer/RootInstaller : app/revanced/library/installation/installer/Installer {
|
||||
public abstract class app/revanced/library/installation/installer/MountInstaller : app/revanced/library/installation/installer/Installer {
|
||||
public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
protected final fun getShellCommandRunner ()Lapp/revanced/library/installation/command/ShellCommandRunner;
|
||||
public fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
@@ -318,12 +318,12 @@ public abstract class app/revanced/library/installation/installer/RootInstaller
|
||||
protected final fun write (Ljava/lang/String;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/RootInstallerResult : java/lang/Enum {
|
||||
public static final field FAILURE Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public static final field SUCCESS Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public final class app/revanced/library/installation/installer/MountInstallerResult : java/lang/Enum {
|
||||
public static final field FAILURE Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
public static final field SUCCESS Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
public static fun getEntries ()Lkotlin/enums/EnumEntries;
|
||||
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public static fun values ()[Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
public static fun values ()[Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/logging/Logger {
|
||||
@@ -225,7 +225,7 @@ public final class app/revanced/library/installation/installer/AdbInstallerResul
|
||||
public static final field INSTANCE Lapp/revanced/library/installation/installer/AdbInstallerResult$Success;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/AdbRootInstaller : app/revanced/library/installation/installer/RootInstaller {
|
||||
public final class app/revanced/library/installation/installer/AdbMountInstaller : app/revanced/library/installation/installer/MountInstaller {
|
||||
public fun <init> ()V
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
@@ -249,12 +249,12 @@ public final class app/revanced/library/installation/installer/Installer$Apk {
|
||||
public final fun getPackageName ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/RootInstallation : app/revanced/library/installation/installer/Installation {
|
||||
public final class app/revanced/library/installation/installer/MountInstallation : app/revanced/library/installation/installer/Installation {
|
||||
public final fun getInstalledApkFilePath ()Ljava/lang/String;
|
||||
public final fun getMounted ()Z
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/installation/installer/RootInstaller : app/revanced/library/installation/installer/Installer {
|
||||
public abstract class app/revanced/library/installation/installer/MountInstaller : app/revanced/library/installation/installer/Installer {
|
||||
public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
protected final fun getShellCommandRunner ()Lapp/revanced/library/installation/command/ShellCommandRunner;
|
||||
public fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
@@ -264,12 +264,12 @@ public abstract class app/revanced/library/installation/installer/RootInstaller
|
||||
protected final fun write (Ljava/lang/String;Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/RootInstallerResult : java/lang/Enum {
|
||||
public static final field FAILURE Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public static final field SUCCESS Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public final class app/revanced/library/installation/installer/MountInstallerResult : java/lang/Enum {
|
||||
public static final field FAILURE Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
public static final field SUCCESS Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
public static fun getEntries ()Lkotlin/enums/EnumEntries;
|
||||
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public static fun values ()[Lapp/revanced/library/installation/installer/RootInstallerResult;
|
||||
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
public static fun values ()[Lapp/revanced/library/installation/installer/MountInstallerResult;
|
||||
}
|
||||
|
||||
public final class app/revanced/library/logging/Logger {
|
||||
123
library/build.gradle.kts
Normal file
123
library/build.gradle.kts
Normal file
@@ -0,0 +1,123 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
`maven-publish`
|
||||
signing
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidTarget {
|
||||
compilations.all {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
}
|
||||
|
||||
publishLibraryVariants("release")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidMain.dependencies {
|
||||
implementation(libs.libsu.nio)
|
||||
implementation(libs.libsu.service)
|
||||
implementation(libs.core.ktx)
|
||||
}
|
||||
|
||||
commonMain.dependencies {
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.jadb) // Fork with Shell v2 support.
|
||||
implementation(libs.bcpkix.jdk15on)
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
implementation(libs.apkzlib)
|
||||
implementation(libs.apksig)
|
||||
implementation(libs.guava)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.kotlin.test.junit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.library"
|
||||
compileSdk = 34
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-library")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
create<MavenPublication>("revanced-library-publication") {
|
||||
version = project.version.toString()
|
||||
|
||||
pom {
|
||||
name = "ReVanced Library"
|
||||
description = "Library containing common utilities for ReVanced"
|
||||
url = "https://revanced.app"
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = "GNU General Public License v3.0"
|
||||
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = "ReVanced"
|
||||
name = "ReVanced"
|
||||
email = "contact@revanced.app"
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
connection = "scm:git:git://github.com/revanced/revanced-library.git"
|
||||
developerConnection = "scm:git:git@github.com:revanced/revanced-library.git"
|
||||
url = "https://github.com/revanced/revanced-library"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications["revanced-library-publication"])
|
||||
}
|
||||
@@ -3,15 +3,15 @@ package app.revanced.library.installation.installer
|
||||
import android.content.Context
|
||||
import app.revanced.library.installation.command.LocalShellCommandRunner
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
|
||||
import app.revanced.library.installation.installer.MountInstaller.NoRootPermissionException
|
||||
import com.topjohnwu.superuser.ipc.RootService
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* [LocalRootInstaller] for installing and uninstalling [Apk] files locally with using root permissions by mounting.
|
||||
* [LocalMountInstaller] for installing and uninstalling [Apk] files locally with using root permissions by mounting.
|
||||
*
|
||||
* @param context The [Context] to use for binding to the [RootService].
|
||||
* @param onReady A callback to be invoked when [LocalRootInstaller] is ready to be used.
|
||||
* @param onReady A callback to be invoked when [LocalMountInstaller] is ready to be used.
|
||||
*
|
||||
* @throws NoRootPermissionException If the device does not have root permission.
|
||||
*
|
||||
@@ -19,13 +19,13 @@ import java.io.Closeable
|
||||
* @see LocalShellCommandRunner
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class LocalRootInstaller(
|
||||
class LocalMountInstaller(
|
||||
context: Context,
|
||||
onReady: LocalRootInstaller.() -> Unit = {},
|
||||
) : RootInstaller(
|
||||
onReady: LocalMountInstaller.() -> Unit = {},
|
||||
) : MountInstaller(
|
||||
{ installer ->
|
||||
LocalShellCommandRunner(context) {
|
||||
(installer as LocalRootInstaller).onReady()
|
||||
(installer as LocalMountInstaller).onReady()
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -4,7 +4,7 @@ package app.revanced.library.adb
|
||||
|
||||
import app.revanced.library.adb.AdbManager.Apk
|
||||
import app.revanced.library.installation.installer.AdbInstaller
|
||||
import app.revanced.library.installation.installer.AdbRootInstaller
|
||||
import app.revanced.library.installation.installer.AdbMountInstaller
|
||||
import app.revanced.library.installation.installer.Constants.PLACEHOLDER
|
||||
import app.revanced.library.installation.installer.Installer
|
||||
import app.revanced.library.run
|
||||
@@ -68,18 +68,18 @@ sealed class AdbManager private constructor(
|
||||
*
|
||||
* @param deviceSerial The device serial. If null, the first connected device will be used.
|
||||
*/
|
||||
@Deprecated("Use AdbRootInstaller instead.", ReplaceWith("AdbRootInstaller(deviceSerial)"))
|
||||
@Deprecated("Use AdbMountInstaller instead.", ReplaceWith("AdbMountInstaller(deviceSerial)"))
|
||||
class RootAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) {
|
||||
override val installer = AdbRootInstaller(deviceSerial)
|
||||
override val installer = AdbMountInstaller(deviceSerial)
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Use AdbRootInstaller.install instead.")
|
||||
@Deprecated("Use AdbMountInstaller.install instead.")
|
||||
override fun install(apk: Apk) = suspend {
|
||||
installer.install(Installer.Apk(apk.file, apk.packageName))
|
||||
}
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Use AdbRootInstaller.uninstall instead.")
|
||||
@Deprecated("Use AdbMountInstaller.uninstall instead.")
|
||||
override fun uninstall(packageName: String) = suspend {
|
||||
installer.uninstall(packageName)
|
||||
}
|
||||
@@ -134,11 +134,11 @@ sealed class AdbManager private constructor(
|
||||
} ?: "No ADB device found",
|
||||
)
|
||||
|
||||
@Deprecated("Use RootInstaller.FailedToFindInstalledPackageException instead.")
|
||||
@Deprecated("Use MountInstaller.FailedToFindInstalledPackageException instead.")
|
||||
class FailedToFindInstalledPackageException internal constructor(packageName: String) :
|
||||
Exception("Failed to find installed package \"$packageName\" because no activity was found")
|
||||
|
||||
@Deprecated("Use RootInstaller.PackageNameRequiredException instead.")
|
||||
@Deprecated("Use MountInstaller.PackageNameRequiredException instead.")
|
||||
class PackageNameRequiredException internal constructor() :
|
||||
Exception("Package name is required")
|
||||
}
|
||||
@@ -2,21 +2,21 @@ package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.command.AdbShellCommandRunner
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
|
||||
import app.revanced.library.installation.installer.MountInstaller.NoRootPermissionException
|
||||
|
||||
/**
|
||||
* [AdbRootInstaller] for installing and uninstalling [Apk] files with using ADB root permissions by mounting.
|
||||
* [AdbMountInstaller] for installing and uninstalling [Apk] files with using ADB root permissions by mounting.
|
||||
*
|
||||
* @param deviceSerial The device serial. If null, the first connected device will be used.
|
||||
*
|
||||
* @throws NoRootPermissionException If the device does not have root permission.
|
||||
*
|
||||
* @see RootInstaller
|
||||
* @see MountInstaller
|
||||
* @see AdbShellCommandRunner
|
||||
*/
|
||||
class AdbRootInstaller(
|
||||
class AdbMountInstaller(
|
||||
deviceSerial: String? = null,
|
||||
) : RootInstaller({ AdbShellCommandRunner(deviceSerial) }) {
|
||||
) : MountInstaller({ AdbShellCommandRunner(deviceSerial) }) {
|
||||
init {
|
||||
logger.fine("Connected to $deviceSerial")
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
/**
|
||||
* [RootInstallation] of the apk file that is mounted to [installedApkFilePath] with root permissions.
|
||||
* [MountInstallation] of the apk file that is mounted to [installedApkFilePath] with root permissions.
|
||||
*
|
||||
* @param installedApkFilePath The installed apk file path or null if the apk is not installed.
|
||||
* @param apkFilePath The mounting apk file path.
|
||||
* @param mounted Whether the apk is mounted to [installedApkFilePath].
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class RootInstallation internal constructor(
|
||||
class MountInstallation internal constructor(
|
||||
val installedApkFilePath: String?,
|
||||
apkFilePath: String,
|
||||
val mounted: Boolean,
|
||||
@@ -17,20 +17,20 @@ import app.revanced.library.installation.installer.Constants.TMP_FILE_PATH
|
||||
import app.revanced.library.installation.installer.Constants.UMOUNT
|
||||
import app.revanced.library.installation.installer.Constants.invoke
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
|
||||
import app.revanced.library.installation.installer.MountInstaller.NoRootPermissionException
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* [RootInstaller] for installing and uninstalling [Apk] files using root permissions by mounting.
|
||||
* [MountInstaller] for installing and uninstalling [Apk] files using root permissions by mounting.
|
||||
*
|
||||
* @param shellCommandRunnerSupplier A supplier for the [ShellCommandRunner] to use.
|
||||
*
|
||||
* @throws NoRootPermissionException If the device does not have root permission.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
abstract class RootInstaller internal constructor(
|
||||
shellCommandRunnerSupplier: (RootInstaller) -> ShellCommandRunner,
|
||||
) : Installer<RootInstallerResult, RootInstallation>() {
|
||||
abstract class MountInstaller internal constructor(
|
||||
shellCommandRunnerSupplier: (MountInstaller) -> ShellCommandRunner,
|
||||
) : Installer<MountInstallerResult, MountInstallation>() {
|
||||
|
||||
/**
|
||||
* The command runner used to run commands on the device.
|
||||
@@ -49,7 +49,7 @@ abstract class RootInstaller internal constructor(
|
||||
*
|
||||
* @throws PackageNameRequiredException If the [Apk] does not have a package name.
|
||||
*/
|
||||
override suspend fun install(apk: Apk): RootInstallerResult {
|
||||
override suspend fun install(apk: Apk): MountInstallerResult {
|
||||
logger.info("Installing ${apk.packageName} by mounting")
|
||||
|
||||
val packageName = apk.packageName?.also { it.assertInstalled() } ?: throw PackageNameRequiredException()
|
||||
@@ -67,10 +67,10 @@ abstract class RootInstaller internal constructor(
|
||||
|
||||
DELETE(TMP_FILE_PATH)()
|
||||
|
||||
return RootInstallerResult.SUCCESS
|
||||
return MountInstallerResult.SUCCESS
|
||||
}
|
||||
|
||||
override suspend fun uninstall(packageName: String): RootInstallerResult {
|
||||
override suspend fun uninstall(packageName: String): MountInstallerResult {
|
||||
logger.info("Uninstalling $packageName by unmounting")
|
||||
|
||||
UMOUNT(packageName)()
|
||||
@@ -81,16 +81,16 @@ abstract class RootInstaller internal constructor(
|
||||
|
||||
KILL(packageName)()
|
||||
|
||||
return RootInstallerResult.SUCCESS
|
||||
return MountInstallerResult.SUCCESS
|
||||
}
|
||||
|
||||
override suspend fun getInstallation(packageName: String): RootInstallation? {
|
||||
override suspend fun getInstallation(packageName: String): MountInstallation? {
|
||||
val patchedApkPath = MOUNTED_APK_PATH(packageName)
|
||||
|
||||
val patchedApkExists = EXISTS(patchedApkPath)().exitCode == 0
|
||||
if (patchedApkExists) return null
|
||||
|
||||
return RootInstallation(
|
||||
return MountInstallation(
|
||||
INSTALLED_APK_PATH(packageName)().output.ifEmpty { null },
|
||||
patchedApkPath,
|
||||
MOUNT_GREP(patchedApkPath)().exitCode == 0,
|
||||
@@ -3,11 +3,11 @@ package app.revanced.library.installation.installer
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
|
||||
/**
|
||||
* The result of installing or uninstalling an [Apk] with root permissions using [RootInstaller].
|
||||
* The result of installing or uninstalling an [Apk] with root permissions using [MountInstaller].
|
||||
*
|
||||
* @see RootInstaller
|
||||
* @see MountInstaller
|
||||
*/
|
||||
enum class RootInstallerResult {
|
||||
enum class MountInstallerResult {
|
||||
/**
|
||||
* The result of installing an [Apk] successfully.
|
||||
*/
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: Figure out why this causes problems.
|
||||
rootProject.name = "revanced-library"
|
||||
|
||||
buildCache {
|
||||
local {
|
||||
isEnabled = "CI" !in System.getenv()
|
||||
@@ -9,7 +6,10 @@ buildCache {
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
include(":library", ":library-networking")
|
||||
|
||||
Reference in New Issue
Block a user