Compare commits

...

31 Commits

Author SHA1 Message Date
oSumAtrIX
00487cf27c update to newest patcher v22 2026-01-28 16:27:40 +01:00
oSumAtrIX
b02c81de07 deprecation 2026-01-28 15:43:52 +01:00
oSumAtrIX
873a20ed53 update to patcher v22 for dev 2026-01-28 15:34:26 +01:00
oSumAtrIX
fbd64ed8ce feat: Add SLSA attestation and PGP signature verification 2026-01-28 15:34:08 +01:00
oSumAtrIX
1dc19c0b4b build: Update Gradle 2026-01-28 15:34:08 +01:00
Pun Butrach
0673307204 docs: Use American spelling (#96) 2025-12-14 16:40:34 +01:00
semantic-release-bot
5df2bb81bf chore: Release v3.2.0-dev.1 [skip ci]
# [3.2.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.1.1-dev.1...v3.2.0-dev.1) (2025-05-27)

### Features

* Request the update ownership enforcement ([#71](https://github.com/ReVanced/revanced-library/issues/71)) ([be0f6bf](be0f6bf247))
2025-05-27 13:37:00 +00:00
Brosssh
be0f6bf247 feat: Request the update ownership enforcement (#71)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-05-27 15:31:09 +02:00
semantic-release-bot
9d060c188f chore: Release v3.1.1-dev.1 [skip ci]
## [3.1.1-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.1.0...v3.1.1-dev.1) (2025-05-02)

### Bug Fixes

* Interpret package name as a string instead of Regex when using grep  ([#68](https://github.com/ReVanced/revanced-library/issues/68)) ([254f36d](254f36d03c))
2025-05-02 13:07:51 +00:00
laur89
254f36d03c fix: Interpret package name as a string instead of Regex when using grep (#68) 2025-05-02 15:04:34 +02:00
semantic-release-bot
4065c87d5f chore: Release v3.1.0 [skip ci]
# [3.1.0](https://github.com/ReVanced/revanced-library/compare/v3.0.2...v3.1.0) (2024-11-27)

### Bug Fixes

* Detect if app is installed by fixing inversion ([649f06b](649f06b19d))

### Features

* Warn when option could not be set because the option does not exist ([7ec6504](7ec6504619))
2024-11-27 21:47:55 +00:00
oSumAtrIX
be8d7bf643 chore: Merge branch dev to main (#65) 2024-11-27 22:45:14 +01:00
semantic-release-bot
2328902b6b chore: Release v3.1.0-dev.1 [skip ci]
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.3-dev.1...v3.1.0-dev.1) (2024-11-25)

### Features

* Warn when option could not be set because the option does not exist ([7ec6504](7ec6504619))
2024-11-25 21:30:09 +00:00
oSumAtrIX
7ec6504619 feat: Warn when option could not be set because the option does not exist 2024-11-25 22:23:31 +01:00
semantic-release-bot
e7a98b5795 chore: Release v3.0.3-dev.1 [skip ci]
## [3.0.3-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.2...v3.0.3-dev.1) (2024-11-11)

### Bug Fixes

* Detect if app is installed by fixing inversion ([649f06b](649f06b19d))
2024-11-11 22:32:39 +00:00
oSumAtrIX
649f06b19d fix: Detect if app is installed by fixing inversion 2024-11-11 23:29:43 +01:00
semantic-release-bot
cace51700a chore: Release v3.0.2 [skip ci]
## [3.0.2](https://github.com/ReVanced/revanced-library/compare/v3.0.1...v3.0.2) (2024-11-05)
2024-11-05 18:44:21 +00:00
oSumAtrIX
91cefc8598 chore: Merge branch dev to main (#63) 2024-11-05 19:41:48 +01:00
semantic-release-bot
735c1e39cd chore: Release v3.0.2-dev.1 [skip ci]
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.1...v3.0.2-dev.1) (2024-11-05)
2024-11-05 18:39:39 +00:00
oSumAtrIX
84cc315541 build(Needs bump): Bump dependencies 2024-11-05 19:37:09 +01:00
semantic-release-bot
4fe9304570 chore: Release v3.0.1 [skip ci]
## [3.0.1](https://github.com/ReVanced/revanced-library/compare/v3.0.0...v3.0.1) (2024-10-13)

### Bug Fixes

* Serialize compatible packages as a map instead of a set of pairs. ([737e272](737e272481))
2024-10-13 01:55:25 +00:00
oSumAtrIX
8bb41be8fc chore: Merge branch dev to main (#62) 2024-10-13 03:52:36 +02:00
semantic-release-bot
4b8ac026c3 chore: Release v3.0.1-dev.3 [skip ci]
## [3.0.1-dev.3](https://github.com/ReVanced/revanced-library/compare/v3.0.1-dev.2...v3.0.1-dev.3) (2024-10-06)
2024-10-06 01:27:57 +00:00
oSumAtrIX
557b6035f8 build(Needs bump): Bump dependencies 2024-10-06 03:25:03 +02:00
oSumAtrIX
bfc5394b4e refactor: Indent code 2024-10-01 17:30:14 +02:00
semantic-release-bot
5b1cf1f190 chore: Release v3.0.1-dev.2 [skip ci]
## [3.0.1-dev.2](https://github.com/ReVanced/revanced-library/compare/v3.0.1-dev.1...v3.0.1-dev.2) (2024-10-01)
2024-10-01 15:09:29 +00:00
oSumAtrIX
dd5c37ddec ci: Use permissions and regular GitHub token instead of PAT 2024-10-01 17:07:05 +02:00
oSumAtrIX
9adccc04dd build(Needs bump): Update dependencies 2024-09-30 23:21:45 +02:00
oSumAtrIX
ed94d29461 ci: Adjust release commit message 2024-09-30 22:34:24 +02:00
semantic-release-bot
efc72cdc55 chore(release): 3.0.1-dev.1 [skip ci]
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.0...v3.0.1-dev.1) (2024-08-16)

### Bug Fixes

* Serialize compatible packages as a map instead of a set of pairs. ([737e272](737e272481))
2024-08-16 22:39:26 +00:00
oSumAtrIX
737e272481 fix: Serialize compatible packages as a map instead of a set of pairs. 2024-08-17 00:36:43 +02:00
27 changed files with 2249 additions and 1241 deletions

View File

@@ -10,6 +10,9 @@ on:
jobs: jobs:
release: release:
name: Release name: Release
permissions:
contents: write
packages: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@@ -49,5 +52,5 @@ jobs:
- name: Release - name: Release
env: env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm exec semantic-release run: npm exec semantic-release

View File

@@ -23,7 +23,8 @@
"assets": [ "assets": [
"CHANGELOG.md", "CHANGELOG.md",
"gradle.properties" "gradle.properties"
] ],
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
} }
], ],
[ [

View File

@@ -1,3 +1,65 @@
# [3.2.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.1.1-dev.1...v3.2.0-dev.1) (2025-05-27)
### Features
* Request the update ownership enforcement ([#71](https://github.com/ReVanced/revanced-library/issues/71)) ([be0f6bf](https://github.com/ReVanced/revanced-library/commit/be0f6bf247461d16fbf649a9f2dc6facbb5b0c93))
## [3.1.1-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.1.0...v3.1.1-dev.1) (2025-05-02)
### Bug Fixes
* Interpret package name as a string instead of Regex when using grep ([#68](https://github.com/ReVanced/revanced-library/issues/68)) ([254f36d](https://github.com/ReVanced/revanced-library/commit/254f36d03cc8fd3e2508a5e8f69bb5c8e1eb9775))
# [3.1.0](https://github.com/ReVanced/revanced-library/compare/v3.0.2...v3.1.0) (2024-11-27)
### Bug Fixes
* Detect if app is installed by fixing inversion ([649f06b](https://github.com/ReVanced/revanced-library/commit/649f06b19dd4d2a3f3216a0b3ea947b9fe0d475f))
### Features
* Warn when option could not be set because the option does not exist ([7ec6504](https://github.com/ReVanced/revanced-library/commit/7ec650461935faf2a8fbb667db3cf137157b70b5))
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.3-dev.1...v3.1.0-dev.1) (2024-11-25)
### Features
* Warn when option could not be set because the option does not exist ([7ec6504](https://github.com/ReVanced/revanced-library/commit/7ec650461935faf2a8fbb667db3cf137157b70b5))
## [3.0.3-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.2...v3.0.3-dev.1) (2024-11-11)
### Bug Fixes
* Detect if app is installed by fixing inversion ([649f06b](https://github.com/ReVanced/revanced-library/commit/649f06b19dd4d2a3f3216a0b3ea947b9fe0d475f))
## [3.0.2](https://github.com/ReVanced/revanced-library/compare/v3.0.1...v3.0.2) (2024-11-05)
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.1...v3.0.2-dev.1) (2024-11-05)
## [3.0.1](https://github.com/ReVanced/revanced-library/compare/v3.0.0...v3.0.1) (2024-10-13)
### Bug Fixes
* Serialize compatible packages as a map instead of a set of pairs. ([737e272](https://github.com/ReVanced/revanced-library/commit/737e272481fe3b0b4c89233d139b5e657a0c1de4))
## [3.0.1-dev.3](https://github.com/ReVanced/revanced-library/compare/v3.0.1-dev.2...v3.0.1-dev.3) (2024-10-06)
## [3.0.1-dev.2](https://github.com/ReVanced/revanced-library/compare/v3.0.1-dev.1...v3.0.1-dev.2) (2024-10-01)
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-library/compare/v3.0.0...v3.0.1-dev.1) (2024-08-16)
### Bug Fixes
* Serialize compatible packages as a map instead of a set of pairs. ([737e272](https://github.com/ReVanced/revanced-library/commit/737e272481fe3b0b4c89233d139b5e657a0c1de4))
# [3.0.0](https://github.com/ReVanced/revanced-library/compare/v2.3.0...v3.0.0) (2024-08-06) # [3.0.0](https://github.com/ReVanced/revanced-library/compare/v2.3.0...v3.0.0) (2024-08-06)

View File

@@ -109,9 +109,9 @@ You can find the contribution guidelines [here](CONTRIBUTING.md).
To build ReVanced Library, To build ReVanced Library,
you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation). you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
## 📜 Licence ## 📜 License
ReVanced Library is licensed under the GPLv3 license. Please see the [licence file](LICENSE) for more information. ReVanced Library is licensed under the GPLv3 license. Please see the [license file](LICENSE) for more information.
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Library as long as you track changes/dates in source files. [tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Library as long as you track changes/dates in source files.
Any modifications to ReVanced Library must also be made available under the GPL, Any modifications to ReVanced Library must also be made available under the GPL,
along with build & install instructions. along with build & install instructions.

View File

@@ -26,7 +26,7 @@ public final class app/revanced/library/ApkSigner$Signer {
public final class app/revanced/library/ApkUtils { public final class app/revanced/library/ApkUtils {
public static final field INSTANCE Lapp/revanced/library/ApkUtils; public static final field INSTANCE Lapp/revanced/library/ApkUtils;
public final fun applyTo (Lapp/revanced/patcher/PatcherResult;Ljava/io/File;)V public final fun applyTo (Lapp/revanced/patcher/PatchesResult;Ljava/io/File;)V
public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V
} }
@@ -47,6 +47,18 @@ public final class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetail
public final fun getValidUntil ()Ljava/util/Date; public final fun getValidUntil ()Ljava/util/Date;
} }
public final class app/revanced/library/CryptographyKt {
public static final fun getPublicKey (Lorg/bouncycastle/openpgp/PGPPublicKeyRing;)Lorg/bouncycastle/openpgp/PGPPublicKey;
public static final fun getPublicKeyRing (Lorg/bouncycastle/openpgp/PGPPublicKeyRingCollection;J)Lorg/bouncycastle/openpgp/PGPPublicKeyRing;
public static final fun getPublicKeyRingCollection (Ljava/io/InputStream;)Lorg/bouncycastle/openpgp/PGPPublicKeyRingCollection;
public static final fun getSignature (Ljava/io/InputStream;)Lorg/bouncycastle/openpgp/PGPSignature;
public static final fun matchGitHub (Ldev/sigstore/fulcio/client/ImmutableFulcioCertificateMatcher$Builder;Ljava/lang/String;)Ldev/sigstore/fulcio/client/ImmutableFulcioCertificateMatcher$Builder;
public static final fun verificationOptions (Lkotlin/jvm/functions/Function1;)Ldev/sigstore/VerificationOptions;
public static final fun verifySLSA ([BLjava/io/InputStream;Ldev/sigstore/VerificationOptions;)Z
public static final fun verifySLSA ([BLjava/io/InputStream;Lkotlin/jvm/functions/Function1;)Z
public static final fun verifySignature ([BLorg/bouncycastle/openpgp/PGPSignature;Lorg/bouncycastle/openpgp/PGPPublicKey;)Z
}
public final class app/revanced/library/OptionsKt { public final class app/revanced/library/OptionsKt {
public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V
} }
@@ -97,7 +109,7 @@ public abstract interface class app/revanced/library/installation/command/RunRes
public abstract fun getError ()Ljava/lang/String; public abstract fun getError ()Ljava/lang/String;
public abstract fun getExitCode ()I public abstract fun getExitCode ()I
public abstract fun getOutput ()Ljava/lang/String; public abstract fun getOutput ()Ljava/lang/String;
public abstract fun waitFor ()V public fun waitFor ()V
} }
public final class app/revanced/library/installation/command/RunResult$DefaultImpls { public final class app/revanced/library/installation/command/RunResult$DefaultImpls {

View File

@@ -26,7 +26,7 @@ public final class app/revanced/library/ApkSigner$Signer {
public final class app/revanced/library/ApkUtils { public final class app/revanced/library/ApkUtils {
public static final field INSTANCE Lapp/revanced/library/ApkUtils; public static final field INSTANCE Lapp/revanced/library/ApkUtils;
public final fun applyTo (Lapp/revanced/patcher/PatcherResult;Ljava/io/File;)V public final fun applyTo (Lapp/revanced/patcher/PatchesResult;Ljava/io/File;)V
public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V
} }
@@ -47,6 +47,18 @@ public final class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetail
public final fun getValidUntil ()Ljava/util/Date; public final fun getValidUntil ()Ljava/util/Date;
} }
public final class app/revanced/library/CryptographyKt {
public static final fun getPublicKey (Lorg/bouncycastle/openpgp/PGPPublicKeyRing;)Lorg/bouncycastle/openpgp/PGPPublicKey;
public static final fun getPublicKeyRing (Lorg/bouncycastle/openpgp/PGPPublicKeyRingCollection;J)Lorg/bouncycastle/openpgp/PGPPublicKeyRing;
public static final fun getPublicKeyRingCollection (Ljava/io/InputStream;)Lorg/bouncycastle/openpgp/PGPPublicKeyRingCollection;
public static final fun getSignature (Ljava/io/InputStream;)Lorg/bouncycastle/openpgp/PGPSignature;
public static final fun matchGitHub (Ldev/sigstore/fulcio/client/ImmutableFulcioCertificateMatcher$Builder;Ljava/lang/String;)Ldev/sigstore/fulcio/client/ImmutableFulcioCertificateMatcher$Builder;
public static final fun verificationOptions (Lkotlin/jvm/functions/Function1;)Ldev/sigstore/VerificationOptions;
public static final fun verifySLSA ([BLjava/io/InputStream;Ldev/sigstore/VerificationOptions;)Z
public static final fun verifySLSA ([BLjava/io/InputStream;Lkotlin/jvm/functions/Function1;)Z
public static final fun verifySignature ([BLorg/bouncycastle/openpgp/PGPSignature;Lorg/bouncycastle/openpgp/PGPPublicKey;)Z
}
public final class app/revanced/library/OptionsKt { public final class app/revanced/library/OptionsKt {
public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V
} }
@@ -73,7 +85,7 @@ public abstract interface class app/revanced/library/installation/command/RunRes
public abstract fun getError ()Ljava/lang/String; public abstract fun getError ()Ljava/lang/String;
public abstract fun getExitCode ()I public abstract fun getExitCode ()I
public abstract fun getOutput ()Ljava/lang/String; public abstract fun getOutput ()Ljava/lang/String;
public abstract fun waitFor ()V public fun waitFor ()V
} }
public final class app/revanced/library/installation/command/RunResult$DefaultImpls { public final class app/revanced/library/installation/command/RunResult$DefaultImpls {

View File

@@ -54,7 +54,9 @@ kotlin {
commonMain.dependencies { commonMain.dependencies {
implementation(libs.apksig) implementation(libs.apksig)
implementation(libs.apkzlib) implementation(libs.apkzlib)
implementation(libs.bcpkix.jdk15on) implementation(libs.bouncycastle.bcpkix)
implementation(libs.bouncycastle.pgp)
implementation(libs.sigstore.java)
implementation(libs.guava) implementation(libs.guava)
implementation(libs.jadb) implementation(libs.jadb)
implementation(libs.kotlin.reflect) implementation(libs.kotlin.reflect)
@@ -67,11 +69,18 @@ kotlin {
implementation(libs.revanced.patcher) implementation(libs.revanced.patcher)
} }
} }
compilerOptions {
freeCompilerArgs.addAll(
"-Xexplicit-backing-fields",
"-Xcontext-parameters",
)
}
} }
android { android {
namespace = "app.revanced.library" namespace = "app.revanced.library"
compileSdk = 34 compileSdk = 36
defaultConfig { defaultConfig {
minSdk = 26 minSdk = 26
} }

View File

@@ -1,4 +1,4 @@
version = 3.0.0 version = 3.2.0-dev.2-local
#Gradle #Gradle
org.gradle.jvmargs = -Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options="-Xmx2048M" org.gradle.jvmargs = -Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options="-Xmx2048M"
org.gradle.caching = true org.gradle.caching = true

View File

@@ -1,20 +1,20 @@
[versions] [versions]
android = "8.5.1" android = "8.12.3"
bcpkix-jdk15on = "1.70" binary-compatibility-validator = "0.18.1"
binary-compatibility-validator = "0.15.1" core-ktx = "1.17.0"
core-ktx = "1.13.1" guava = "33.5.0-jre"
guava = "33.0.0-jre" jadb = "1.2.1.1"
jadb = "1.2.1" kotlin = "2.3.0"
kotlin = "2.0.0" kotlinx-coroutines = "1.10.2"
kotlinx-coroutines = "1.8.1" kotlinx-serialization = "1.9.0"
kotlinx-serialization = "1.7.1"
libsu = "5.2.2" libsu = "5.2.2"
revanced-patcher = "20.0.0" revanced-patcher = "22.0.0-local"
bouncy-castle = "1.82"
sigstore = "2.0.0"
[libraries] [libraries]
apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "android" } apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "android" }
apksig = { module = "com.android.tools.build:apksig", version.ref = "android" } apksig = { module = "com.android.tools.build:apksig", version.ref = "android" }
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
guava = { module = "com.google.guava:guava", version.ref = "guava" } guava = { module = "com.google.guava:guava", version.ref = "guava" }
jadb = { module = "app.revanced:jadb", version.ref = "jadb" } # Fork with Shell v2 support. jadb = { module = "app.revanced:jadb", version.ref = "jadb" } # Fork with Shell v2 support.
@@ -25,7 +25,10 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa
libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" } libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" }
libsu-nio = { module = "com.github.topjohnwu.libsu:nio", 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" } libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" }
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } revanced-patcher = { module = "app.revanced:patcher", version.ref = "revanced-patcher" }
bouncycastle-bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bouncy-castle" }
bouncycastle-pgp = { module = "org.bouncycastle:bcpg-jdk18on", version.ref = "bouncy-castle" }
sigstore-java = { module = "dev.sigstore:sigstore-java", version.ref = "sigstore" }
[plugins] [plugins]
android-library = { id = "com.android.library", version.ref = "android" } android-library = { id = "com.android.library", version.ref = "android" }

Binary file not shown.

View File

@@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

12
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -173,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -206,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.

3
gradlew.bat vendored
View File

@@ -70,11 +70,10 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

3002
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
"@saithodev/semantic-release-backmerge": "^4.0.1", "@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.1", "gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^23.0.2" "semantic-release": "^24.1.2"
} }
} }

View File

@@ -8,6 +8,7 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import app.revanced.library.installation.installer.Installer.Apk import app.revanced.library.installation.installer.Installer.Apk
import java.io.Closeable import java.io.Closeable
@@ -78,17 +79,20 @@ class LocalInstaller(
override suspend fun getInstallation(packageName: String) = try { override suspend fun getInstallation(packageName: String) = try {
val packageInfo = context.packageManager.getPackageInfo(packageName, 0) val packageInfo = context.packageManager.getPackageInfo(packageName, 0)
Installation(packageInfo.applicationInfo.sourceDir) Installation(packageInfo.applicationInfo!!.sourceDir)
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
null null
} }
override fun close() = context.unregisterReceiver(broadcastReceiver) override fun close() = context.unregisterReceiver(broadcastReceiver)
@SuppressLint("MissingPermission")
companion object { companion object {
private val sessionParams = PackageInstaller.SessionParams( private val sessionParams = PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL, PackageInstaller.SessionParams.MODE_FULL_INSTALL,
).apply { ).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setRequestUpdateOwnership(true)
setInstallReason(PackageManager.INSTALL_REASON_USER) setInstallReason(PackageManager.INSTALL_REASON_USER)
} }

View File

@@ -1,6 +1,7 @@
package app.revanced.library package app.revanced.library
import com.android.apksig.ApkSigner.SignerConfig import com.android.apksig.ApkSigner.SignerConfig
import com.android.apksig.KeyConfig
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.X509v3CertificateBuilder
@@ -155,8 +156,7 @@ object ApkSigner {
// Read the private key and certificate from the keystore. // Read the private key and certificate from the keystore.
val privateKey = val privateKey = try {
try {
keyStore.getKey(keyStoreEntryAlias, keyStoreEntryPassword.toCharArray()) as PrivateKey keyStore.getKey(keyStoreEntryAlias, keyStoreEntryPassword.toCharArray()) as PrivateKey
} catch (exception: UnrecoverableKeyException) { } catch (exception: UnrecoverableKeyException) {
throw IllegalArgumentException("Invalid password for keystore entry $keyStoreEntryAlias") throw IllegalArgumentException("Invalid password for keystore entry $keyStoreEntryAlias")
@@ -186,7 +186,7 @@ object ApkSigner {
listOf( listOf(
SignerConfig.Builder( SignerConfig.Builder(
signer, signer,
privateKeyCertificatePair.privateKey, KeyConfig.Jca(privateKeyCertificatePair.privateKey),
listOf(privateKeyCertificatePair.certificate), listOf(privateKeyCertificatePair.certificate),
).build(), ).build(),
), ),

View File

@@ -1,7 +1,7 @@
package app.revanced.library package app.revanced.library
import app.revanced.library.ApkSigner.newPrivateKeyCertificatePair import app.revanced.library.ApkSigner.newPrivateKeyCertificatePair
import app.revanced.patcher.PatcherResult import app.revanced.patcher.PatchesResult
import com.android.tools.build.apkzlib.zip.AlignmentRules import com.android.tools.build.apkzlib.zip.AlignmentRules
import com.android.tools.build.apkzlib.zip.StoredEntry import com.android.tools.build.apkzlib.zip.StoredEntry
import com.android.tools.build.apkzlib.zip.ZFile import com.android.tools.build.apkzlib.zip.ZFile
@@ -38,7 +38,7 @@ object ApkUtils {
) )
/** /**
* Applies the [PatcherResult] to the given [apkFile]. * Applies the [PatchesResult] to the given [apkFile].
* *
* The order of operation is as follows: * The order of operation is as follows:
* 1. Write patched dex files. * 1. Write patched dex files.
@@ -50,7 +50,7 @@ object ApkUtils {
* *
* @param apkFile The file to apply the patched files to. * @param apkFile The file to apply the patched files to.
*/ */
fun PatcherResult.applyTo(apkFile: File) { fun PatchesResult.applyTo(apkFile: File) {
ZFile.openReadWrite(apkFile, zFileOptions).use { targetApkZFile -> ZFile.openReadWrite(apkFile, zFileOptions).use { targetApkZFile ->
dexFiles.forEach { dexFile -> dexFiles.forEach { dexFile ->
targetApkZFile.add(dexFile.name, dexFile.stream) targetApkZFile.add(dexFile.name, dexFile.stream)
@@ -82,7 +82,7 @@ object ApkUtils {
// Delete resources that were staged for deletion. // Delete resources that were staged for deletion.
if (resources.deleteResources.isNotEmpty()) { if (resources.deleteResources.isNotEmpty()) {
targetApkZFile.entries().filter { entry -> targetApkZFile.entries().filter { entry ->
resources.deleteResources.any { shouldDelete -> shouldDelete(entry.centralDirectoryHeader.name) } entry.centralDirectoryHeader.name in resources.deleteResources
}.forEach(StoredEntry::delete) }.forEach(StoredEntry::delete)
} }
} }

View File

@@ -0,0 +1,163 @@
@file:Suppress("unused")
package app.revanced.library
import dev.sigstore.KeylessVerifier
import dev.sigstore.VerificationOptions
import dev.sigstore.VerificationOptions.CertificateMatcher
import dev.sigstore.bundle.Bundle
import dev.sigstore.dsse.InTotoPayload
import dev.sigstore.fulcio.client.ImmutableFulcioCertificateMatcher.Builder
import dev.sigstore.strings.StringMatcher
import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider
import java.io.InputStream
// region PGP signature verification.
private val verifierBuilderProvider = BcPGPContentVerifierBuilderProvider()
private val fingerprintCalculator = BcKeyFingerprintCalculator()
/**
* Verifies the PGP signature of the provided bytes using the provided signature and public key.
*
* @param bytes The bytes to verify.
* @param signature The PGP signature.
* @param publicKey The PGP public key.
* @return True if the signature is valid, false otherwise.
*/
fun verifySignature(
bytes: ByteArray, signature: PGPSignature, publicKey: PGPPublicKey
) = signature.apply {
init(verifierBuilderProvider, publicKey)
update(bytes)
}.verify()
/**
* Gets the PGP signature from the provided signature input stream.
*
* @param signatureInputStream The input stream of the PGP signature.
* @return The PGP signature.
* @throws IllegalArgumentException if the signature format is invalid.
*/
fun getSignature(
signatureInputStream: InputStream
) = when (val pgpObject = PGPObjectFactory(
PGPUtil.getDecoderStream(signatureInputStream), fingerprintCalculator
).nextObject()) {
is PGPSignatureList -> pgpObject
is PGPCompressedData -> {
val compressedDataFactory = PGPObjectFactory(
pgpObject.dataStream, fingerprintCalculator
)
compressedDataFactory.nextObject() as PGPSignatureList
}
else -> throw IllegalArgumentException("Invalid PGP signature format.")
}.first()
/**
* Gets the PGP public key ring collection from the provided public key ring input stream.
*
* @param publicKeyRingInputStream The input stream of the public key ring.
* @return The PGP public key ring collection.
*/
fun getPublicKeyRingCollection(
publicKeyRingInputStream: InputStream
) = PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(publicKeyRingInputStream), fingerprintCalculator)
/**
* Gets the PGP public key ring with the specified key ID from the provided public key ring collection.
*
* @param publicKeyRingCollection The PGP public key ring collection.
* @param keyId The key ID of the public key ring to retrieve.
* @return The PGP public key ring with the specified key ID.
* @throws IllegalArgumentException if the public key ring with the specified key ID is not found.
*/
fun getPublicKeyRing(
publicKeyRingCollection: PGPPublicKeyRingCollection, keyId: Long
) = publicKeyRingCollection.getPublicKeyRing(keyId)
?: throw IllegalArgumentException("Can't find public key ring with ID $keyId.")
/**
* Gets the PGP public key from the provided public key ring.
*/
fun getPublicKey(publicKeyRing: PGPPublicKeyRing): PGPPublicKey = publicKeyRing.publicKey
// endregion
// region SLSA attestation verification.
private val keylessVerifier: KeylessVerifier = KeylessVerifier.builder().sigstorePublicDefaults().build()
private const val RUNNER_ENVIRONMENT_OID = "1.3.6.1.4.1.57264.1.11"
private const val PROVENANCE_PREDICATE_TYPE = "https://slsa.dev/provenance/v1"
/**
* Verifies the SLSA attestation of the provided digest using the provided attestation input stream and matcher.
*
* @param digest The digest to verify.
* @param attestationInputStream The input stream of the attestation.
* @param matcher The matcher to add to the verification options.
* @return True if the verification is successful, false otherwise.
*/
fun verifySLSA(
digest: ByteArray,
attestationInputStream: InputStream,
matcher: Builder.() -> Builder,
) = verifySLSA(digest, attestationInputStream, verificationOptions(matcher))
/**
* Verifies the SLSA attestation of the provided digest using the provided attestation input stream
* and verification options.
*
* @param digest The digest to verify.
* @param attestationInputStream The input stream of the attestation.
* @param verificationOptions The verification options to use.
* @return True if the verification is successful, false otherwise.
*/
fun verifySLSA(
digest: ByteArray,
attestationInputStream: InputStream,
verificationOptions: VerificationOptions,
) = runCatching {
val bundle = Bundle.from(attestationInputStream.reader())
val predicateType = InTotoPayload.from(bundle.dsseEnvelope.get()).predicateType
require(predicateType == PROVENANCE_PREDICATE_TYPE)
keylessVerifier.verify(digest, bundle, verificationOptions)
}.isSuccess
/**
* Creates verification options with the provided matcher.
*
* @param matcher The matcher to add to the verification options.
* @return The created verification options.
*/
fun verificationOptions(
matcher: Builder.() -> Builder
): VerificationOptions = VerificationOptions.builder().addCertificateMatchers(
CertificateMatcher.fulcio().matcher().build()
).build()
/**
* Adds GitHub-specific matching to the builder for the specified repository.
*
* @param repository The GitHub repository in the format "owner/repo".
* @return The updated builder with GitHub-specific matching.
*/
fun Builder.matchGitHub(
repository: String
): Builder = issuer(
StringMatcher.string("https://token.actions.githubusercontent.com")
).subjectAlternativeName(
StringMatcher.regex("(?i)^https://github.com/$repository")
).putOidDerAsn1Strings(RUNNER_ENVIRONMENT_OID, StringMatcher.string("github-hosted"))
// endregion

View File

@@ -18,12 +18,17 @@ private val logger = Logger.getLogger("Options")
* *
* @param options The options to set. The key is the patch name and the value is a map of option keys to option values. * @param options The options to set. The key is the patch name and the value is a map of option keys to option values.
*/ */
fun Set<Patch<*>>.setOptions(options: PatchesOptions) = filter { it.name != null }.forEach { patch -> fun Set<Patch>.setOptions(options: PatchesOptions) = filter { it.name != null }.forEach { patch ->
val patchOptions = options[patch.name] ?: return@forEach options[patch.name]?.forEach setOption@{ (optionKey, optionValue) ->
if (optionKey !in patch.options) {
return@setOption logger.warning(
"Could not set option for the \"${patch.name}\" patch because " +
"option with key \"${optionKey}\" does not exist",
)
}
patch.options.forEach option@{ option ->
try { try {
patch.options[option.key] = patchOptions[option.key] ?: return@option patch.options[optionKey] = optionValue
} catch (e: OptionException) { } catch (e: OptionException) {
logger.warning("Could not set option value for the \"${patch.name}\" patch: ${e.message}") logger.warning("Could not set option value for the \"${patch.name}\" patch: ${e.message}")
} }

View File

@@ -17,7 +17,7 @@ typealias PackageNameMap = Map<PackageName, VersionMap>
* @param countUnusedPatches Whether to count patches that are not used. * @param countUnusedPatches Whether to count patches that are not used.
* @return A map of package names to a map of versions to their count. * @return A map of package names to a map of versions to their count.
*/ */
fun Set<Patch<*>>.mostCommonCompatibleVersions( fun Set<Patch>.mostCommonCompatibleVersions(
packageNames: Set<String>? = null, packageNames: Set<String>? = null,
countUnusedPatches: Boolean = false, countUnusedPatches: Boolean = false,
): PackageNameMap = buildMap { ): PackageNameMap = buildMap {

View File

@@ -1,30 +1,35 @@
package app.revanced.library package app.revanced.library
import app.revanced.patcher.patch.* import app.revanced.patcher.patch.Option
import kotlinx.serialization.* import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.VersionName
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.* import kotlinx.serialization.builtins.*
import kotlinx.serialization.descriptors.buildClassSerialDescriptor import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.encodeStructure import kotlinx.serialization.encoding.encodeStructure
import kotlinx.serialization.json.* import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.serializer
import java.io.OutputStream import java.io.OutputStream
private class PatchSerializer : KSerializer<Patch<*>> { private class PatchSerializer : KSerializer<Patch> {
override val descriptor = buildClassSerialDescriptor("Patch") { override val descriptor = buildClassSerialDescriptor("Patch") {
element<String?>("name") element<String?>("name")
element<String?>("description") element<String?>("description")
element<Boolean>("use") element<Boolean>("use")
element<List<String>>("dependencies") element<List<String>>("dependencies")
element<Set<Package>?>("compatiblePackages") element<Map<PackageName, Set<VersionName>?>?>("compatiblePackages")
element("options", OptionSerializer.descriptor) element("options", OptionSerializer.descriptor)
} }
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported") override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
override fun serialize(encoder: Encoder, value: Patch<*>) { override fun serialize(encoder: Encoder, value: Patch) {
encoder.encodeStructure(descriptor) { encoder.encodeStructure(descriptor) {
encodeNullableSerializableElement( encodeNullableSerializableElement(
descriptor, descriptor,
@@ -52,8 +57,8 @@ private class PatchSerializer : KSerializer<Patch<*>> {
encodeNullableSerializableElement( encodeNullableSerializableElement(
descriptor, descriptor,
4, 4,
SetSerializer(PairSerializer(String.serializer(), SetSerializer(String.serializer()).nullable)), MapSerializer(String.serializer(), SetSerializer(String.serializer()).nullable),
value.compatiblePackages, value.compatiblePackages?.associate { (packageName, versions) -> packageName to versions },
) )
encodeSerializableElement( encodeSerializableElement(
descriptor, descriptor,
@@ -82,13 +87,12 @@ private class PatchSerializer : KSerializer<Patch<*>> {
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
override fun serialize(encoder: Encoder, value: Option<*>) { override fun serialize(encoder: Encoder, value: Option<*>) {
encoder.encodeStructure(descriptor) { encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.key) encodeStringElement(descriptor, 0, value.name)
encodeNullableSerializableElement(descriptor, 1, String.serializer(), value.title) encodeNullableSerializableElement(descriptor, 1, String.serializer(), value.description)
encodeNullableSerializableElement(descriptor, 2, String.serializer(), value.description) encodeBooleanElement(descriptor, 2, value.required)
encodeBooleanElement(descriptor, 3, value.required) encodeSerializableElement(descriptor, 3, String.serializer(), value.type.toString())
encodeSerializableElement(descriptor, 4, String.serializer(), value.type.toString()) encodeNullableSerializableElement(descriptor, 4, serializer(value.type), value.default)
encodeNullableSerializableElement(descriptor, 5, serializer(value.type), value.default) encodeNullableSerializableElement(descriptor, 5, MapSerializer(String.serializer(), serializer(value.type)), value.values)
encodeNullableSerializableElement(descriptor, 6, MapSerializer(String.serializer(), serializer(value.type)), value.values)
} }
} }
} }
@@ -104,7 +108,7 @@ private val patchSerializer by lazy { Json }
* @param prettyPrint Whether to pretty print the JSON. * @param prettyPrint Whether to pretty print the JSON.
*/ */
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
fun Set<Patch<*>>.serializeTo( fun Set<Patch>.serializeTo(
outputStream: OutputStream, outputStream: OutputStream,
prettyPrint: Boolean = true, prettyPrint: Boolean = true,
) = if (prettyPrint) { ) = if (prettyPrint) {

View File

@@ -1,11 +1,13 @@
package app.revanced.library.installation.installer package app.revanced.library.installation.installer
import app.revanced.library.installation.command.AdbShellCommandRunner import app.revanced.library.installation.command.AdbShellCommandRunner
import app.revanced.library.installation.command.ShellCommandRunner
import app.revanced.library.installation.installer.Constants.GET_SDK_VERSION
import app.revanced.library.installation.installer.Constants.INSTALLED_APK_PATH import app.revanced.library.installation.installer.Constants.INSTALLED_APK_PATH
import app.revanced.library.installation.installer.Installer.Apk
import se.vidstige.jadb.JadbException import se.vidstige.jadb.JadbException
import se.vidstige.jadb.managers.Package import se.vidstige.jadb.managers.Package
import se.vidstige.jadb.managers.PackageManager import se.vidstige.jadb.managers.PackageManager
import se.vidstige.jadb.managers.PackageManager.UPDATE_OWNERSHIP
/** /**
* [AdbInstaller] for installing and uninstalling [Apk] files using ADB. * [AdbInstaller] for installing and uninstalling [Apk] files using ADB.
@@ -17,18 +19,23 @@ import se.vidstige.jadb.managers.PackageManager
class AdbInstaller( class AdbInstaller(
deviceSerial: String? = null, deviceSerial: String? = null,
) : Installer<AdbInstallerResult, Installation>() { ) : Installer<AdbInstallerResult, Installation>() {
private val device = getDevice(deviceSerial, logger) private val shellCommandRunner: ShellCommandRunner
private val adbShellCommandRunner = AdbShellCommandRunner(device) private val packageManager: PackageManager
private val packageManager = PackageManager(device)
init { init {
val device = getDevice(deviceSerial, logger)
shellCommandRunner = AdbShellCommandRunner(device)
packageManager = PackageManager(device)
logger.fine("Connected to $deviceSerial") logger.fine("Connected to $deviceSerial")
} }
override suspend fun install(apk: Apk): AdbInstallerResult { override suspend fun install(apk: Apk): AdbInstallerResult {
logger.info("Installing ${apk.file.name}") return runPackageManager {
val sdkVersion = shellCommandRunner(GET_SDK_VERSION).output.toInt()
return runPackageManager { install(apk.file) } if (sdkVersion < 34) install(apk.file)
else installWithOptions(apk.file, listOf(UPDATE_OWNERSHIP))
}
} }
override suspend fun uninstall(packageName: String): AdbInstallerResult { override suspend fun uninstall(packageName: String): AdbInstallerResult {
@@ -39,7 +46,7 @@ class AdbInstaller(
override suspend fun getInstallation(packageName: String): Installation? = packageManager.packages.find { override suspend fun getInstallation(packageName: String): Installation? = packageManager.packages.find {
it.toString() == packageName it.toString() == packageName
}?.let { Installation(adbShellCommandRunner(INSTALLED_APK_PATH).output) } }?.let { Installation(shellCommandRunner(INSTALLED_APK_PATH).output) }
private fun runPackageManager(block: PackageManager.() -> Unit) = try { private fun runPackageManager(block: PackageManager.() -> Unit) = try {
packageManager.run(block) packageManager.run(block)

View File

@@ -4,26 +4,28 @@ package app.revanced.library.installation.installer
internal object Constants { internal object Constants {
const val PLACEHOLDER = "PLACEHOLDER" const val PLACEHOLDER = "PLACEHOLDER"
const val SELINUX_CONTEXT = "u:object_r:apk_data_file:s0"
const val TMP_FILE_PATH = "/data/local/tmp/revanced.tmp" const val TMP_FILE_PATH = "/data/local/tmp/revanced.tmp"
const val MOUNT_PATH = "/data/adb/revanced/" const val MOUNT_PATH = "/data/adb/revanced/"
const val MOUNTED_APK_PATH = "$MOUNT_PATH$PLACEHOLDER.apk" const val MOUNTED_APK_PATH = "$MOUNT_PATH$PLACEHOLDER.apk"
const val MOUNT_SCRIPT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh" const val MOUNT_SCRIPT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
const val EXISTS = "[[ -f $PLACEHOLDER ]] || exit 1" const val EXISTS = "[[ -f $PLACEHOLDER ]] || exit 1"
const val MOUNT_GREP = "grep $PLACEHOLDER /proc/mounts" const val MOUNT_GREP = "grep -F $PLACEHOLDER /proc/mounts"
const val DELETE = "rm -rf $PLACEHOLDER" const val DELETE = "rm -rf $PLACEHOLDER"
const val CREATE_DIR = "mkdir -p" const val CREATE_DIR = "mkdir -p"
const val RESTART = "am start -S $PLACEHOLDER" const val RESTART = "am start -S $PLACEHOLDER"
const val KILL = "am force-stop $PLACEHOLDER" const val KILL = "am force-stop $PLACEHOLDER"
const val INSTALLED_APK_PATH = "pm path $PLACEHOLDER" const val INSTALLED_APK_PATH = "pm path $PLACEHOLDER"
const val CREATE_INSTALLATION_PATH = "$CREATE_DIR $MOUNT_PATH" const val CREATE_INSTALLATION_PATH = "$CREATE_DIR $MOUNT_PATH"
const val GET_SDK_VERSION = "getprop ro.build.version.sdk"
const val MOUNT_APK = const val MOUNT_APK =
"base_path=\"$MOUNTED_APK_PATH\" && " + "base_path=\"$MOUNTED_APK_PATH\" && " +
"mv $TMP_FILE_PATH \$base_path && " + "mv $TMP_FILE_PATH \$base_path && " +
"chmod 644 \$base_path && " + "chmod 644 \$base_path && " +
"chown system:system \$base_path && " + "chown system:system \$base_path && " +
"chcon u:object_r:apk_data_file:s0 \$base_path" "chcon $SELINUX_CONTEXT \$base_path"
const val UMOUNT = const val UMOUNT =
"grep $PLACEHOLDER /proc/mounts | " + "grep $PLACEHOLDER /proc/mounts | " +
@@ -52,7 +54,7 @@ internal object Constants {
base_path="$MOUNTED_APK_PATH" base_path="$MOUNTED_APK_PATH"
chcon u:object_r:apk_data_file:s0 ${'$'}base_path chcon $SELINUX_CONTEXT ${'$'}base_path
# Use Magisk mirror, if possible. # Use Magisk mirror, if possible.
if command -v magisk &> /dev/null; then if command -v magisk &> /dev/null; then

View File

@@ -122,13 +122,12 @@ abstract class RootInstaller internal constructor(
* @throws FailedToFindInstalledPackageException If the package is not installed. * @throws FailedToFindInstalledPackageException If the package is not installed.
*/ */
private fun String.assertInstalled() { private fun String.assertInstalled() {
if (INSTALLED_APK_PATH(this)().output.isNotEmpty()) { if (INSTALLED_APK_PATH(this)().output.isEmpty()) {
throw FailedToFindInstalledPackageException(this) throw FailedToFindInstalledPackageException(this)
} }
} }
internal class FailedToFindInstalledPackageException internal constructor(packageName: String) : internal class FailedToFindInstalledPackageException internal constructor(packageName: String) : Exception("Failed to find installed package \"$packageName\" because no activity was found")
Exception("Failed to find installed package \"$packageName\" because no activity was found")
internal class PackageNameRequiredException internal constructor() : Exception("Package name is required") internal class PackageNameRequiredException internal constructor() : Exception("Package name is required")
internal class NoRootPermissionException internal constructor() : Exception("No root permission") internal class NoRootPermissionException internal constructor() : Exception("No root permission")

View File

@@ -50,8 +50,7 @@ internal class MostCommonCompatibleVersionsTest {
@Test @Test
fun `common versions correctly ordered for each package`() { fun `common versions correctly ordered for each package`() {
fun assertEqualsExpected(compatiblePackageNames: Set<String>?) = fun assertEqualsExpected(compatiblePackageNames: Set<String>?) = assertEqualsVersions(
assertEqualsVersions(
expected = expected =
mapOf( mapOf(
"some.package" to linkedMapOf("a" to 3, "b" to 2, "c" to 1), "some.package" to linkedMapOf("a" to 3, "b" to 2, "c" to 1),
@@ -112,7 +111,7 @@ internal class MostCommonCompatibleVersionsTest {
@Test @Test
fun `return null because no patches were supplied`() { fun `return null because no patches were supplied`() {
assertEqualsVersion(null, emptySet<BytecodePatch>(), "some.package") assertEqualsVersion(null, emptySet(), "some.package")
} }
@Test @Test
@@ -135,7 +134,7 @@ internal class MostCommonCompatibleVersionsTest {
private fun assertEqualsVersions( private fun assertEqualsVersions(
expected: PackageNameMap, expected: PackageNameMap,
patches: Set<Patch<*>>, patches: Set<Patch>,
compatiblePackageNames: Set<String>?, compatiblePackageNames: Set<String>?,
countUnusedPatches: Boolean = false, countUnusedPatches: Boolean = false,
) = assertEquals( ) = assertEquals(
@@ -145,7 +144,7 @@ internal class MostCommonCompatibleVersionsTest {
private fun assertEqualsVersion( private fun assertEqualsVersion(
expected: String?, expected: String?,
patches: Set<Patch<*>>, patches: Set<Patch>,
compatiblePackageName: String, compatiblePackageName: String,
) { ) {
assertEquals( assertEquals(

View File

@@ -1,6 +1,9 @@
package app.revanced.library package app.revanced.library
import app.revanced.patcher.patch.* import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.floatsOption
import app.revanced.patcher.patch.stringOption
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import kotlin.test.Test import kotlin.test.Test
@@ -33,7 +36,7 @@ class SerializationTest {
assert(deserializedPatch["name"]!!.jsonPrimitive.content == "Test patch") assert(deserializedPatch["name"]!!.jsonPrimitive.content == "Test patch")
assert(deserializedPatch["compatiblePackages"]!!.jsonArray.size == 2) { assert(deserializedPatch["compatiblePackages"]!!.jsonObject.size == 2) {
"The patch should be compatible with two packages." "The patch should be compatible with two packages."
} }