Compare commits

..

36 Commits

Author SHA1 Message Date
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
semantic-release-bot
92ff93d6e6 chore(release): 3.0.0 [skip ci]
# [3.0.0](https://github.com/ReVanced/revanced-library/compare/v2.3.0...v3.0.0) (2024-08-06)

### Bug Fixes

* Make functions internal which are supposed to be internal ([893d22d](893d22d793))

### Build System

* Refactor to DSL to bump ReVanced Patcher ([7f5d6da](7f5d6dad7b))

### Features

* Add local Android installer ([#25](https://github.com/ReVanced/revanced-library/issues/25)) ([43d655a](43d655aea5))
* Remove deprecated functions ([b9bf3bc](b9bf3bc882))

### BREAKING CHANGES

* Some functions have been removed.
* Some functions are not available anymore.
* The signature of some functions has changed.
2024-08-06 22:53:49 +00:00
oSumAtrIX
d56126aa58 chore: Merge branch dev to main (#60) 2024-08-07 00:51:37 +02:00
semantic-release-bot
079776f241 chore(release): 3.0.0-dev.1 [skip ci]
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.4.0-dev.1...v3.0.0-dev.1) (2024-08-06)

### Bug Fixes

* Make functions internal which are supposed to be internal ([893d22d](893d22d793))

### Build System

* Refactor to DSL to bump ReVanced Patcher ([7f5d6da](7f5d6dad7b))

### Features

* Remove deprecated functions ([b9bf3bc](b9bf3bc882))

### BREAKING CHANGES

* Some functions have been removed.
* Some functions are not available anymore.
* The signature of some functions has changed.
2024-08-06 22:48:55 +00:00
oSumAtrIX
7a554a85a8 build: Fix duplicate publication
KMP already creates one.
2024-08-07 00:46:26 +02:00
oSumAtrIX
b9bf3bc882 feat: Remove deprecated functions
BREAKING CHANGE: Some functions have been removed.
2024-08-06 17:55:35 +02:00
oSumAtrIX
27b3359d66 refactor: Move functions to top level 2024-08-06 17:55:35 +02:00
oSumAtrIX
893d22d793 fix: Make functions internal which are supposed to be internal
BREAKING CHANGE: Some functions are not available anymore.
2024-08-06 17:55:35 +02:00
oSumAtrIX
7f5d6dad7b build: Refactor to DSL to bump ReVanced Patcher
BREAKING CHANGE:  The signature of some functions has changed.
2024-08-06 17:55:35 +02:00
oSumAtrIX
8aca650ebc ci: Correct usage of repository variable 2024-07-13 00:45:18 +02:00
oSumAtrIX
db59d2cd0b docs: Improve issue templates 2024-05-26 00:43:37 +02:00
78 changed files with 2959 additions and 4420 deletions

View File

@@ -70,7 +70,8 @@ body:
Before creating a new bug report, please keep the following in mind: Before creating a new bug report, please keep the following in mind:
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-library/labels/Bug%20report). - **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-library/issues?q=label%3A%22Bug+report%22).
- **Review the contribution guidelines**: Make sure your bug request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-library/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app). - **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea - type: textarea
attributes: attributes:
@@ -100,7 +101,7 @@ body:
label: Acknowledgements label: Acknowledgements
description: Your bug report will be closed if you don't follow the checklist below. description: Your bug report will be closed if you don't follow the checklist below.
options: options:
- label: This issue is not a duplicate of an existing bug report. - label: I have checked all open and closed bug reports and this is not a duplicate.
required: true required: true
- label: I have chosen an appropriate title. - label: I have chosen an appropriate title.
required: true required: true

View File

@@ -70,8 +70,8 @@ body:
Before creating a new feature request, please keep the following in mind: Before creating a new feature request, please keep the following in mind:
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-library/labels/Feature%20request). - **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-library/issues?q=label%3A%22Feature+request%22).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-library/blob/main/CONTRIBUTING.md). - **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-library/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app). - **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea - type: textarea
attributes: attributes:
@@ -98,7 +98,7 @@ body:
label: Acknowledgements label: Acknowledgements
description: Your feature request will be closed if you don't follow the checklist below. description: Your feature request will be closed if you don't follow the checklist below.
options: options:
- label: This issue is not a duplicate of an existing feature request. - label: I have checked all open and closed feature requests and this is not a duplicate.
required: true required: true
- label: I have chosen an appropriate title. - label: I have chosen an appropriate title.
required: true required: true

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
@@ -45,9 +48,9 @@ jobs:
with: with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }} passphrase: ${{ secrets.GPG_PASSPHRASE }}
fingerprint: ${{ env.GPG_FINGERPRINT }} fingerprint: ${{ vars.GPG_FINGERPRINT }}
- 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,114 @@
# [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)
### Bug Fixes
* Make functions internal which are supposed to be internal ([893d22d](https://github.com/ReVanced/revanced-library/commit/893d22d7938fa1c7544795635ed2ffacdd0cbf0d))
### Build System
* Refactor to DSL to bump ReVanced Patcher ([7f5d6da](https://github.com/ReVanced/revanced-library/commit/7f5d6dad7ba73e2ee53010241ba3204d04860a22))
### Features
* Add local Android installer ([#25](https://github.com/ReVanced/revanced-library/issues/25)) ([43d655a](https://github.com/ReVanced/revanced-library/commit/43d655aea5d86288ae9916630e0f30de219d5cfb))
* Remove deprecated functions ([b9bf3bc](https://github.com/ReVanced/revanced-library/commit/b9bf3bc88284c0381c7370c3606b662da2ef380d))
### BREAKING CHANGES
* Some functions have been removed.
* Some functions are not available anymore.
* The signature of some functions has changed.
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.4.0-dev.1...v3.0.0-dev.1) (2024-08-06)
### Bug Fixes
* Make functions internal which are supposed to be internal ([893d22d](https://github.com/ReVanced/revanced-library/commit/893d22d7938fa1c7544795635ed2ffacdd0cbf0d))
### Build System
* Refactor to DSL to bump ReVanced Patcher ([7f5d6da](https://github.com/ReVanced/revanced-library/commit/7f5d6dad7ba73e2ee53010241ba3204d04860a22))
### Features
* Remove deprecated functions ([b9bf3bc](https://github.com/ReVanced/revanced-library/commit/b9bf3bc88284c0381c7370c3606b662da2ef380d))
### BREAKING CHANGES
* Some functions have been removed.
* Some functions are not available anymore.
* The signature of some functions has changed.
# [2.4.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.3.0...v2.4.0-dev.1) (2024-04-07) # [2.4.0-dev.1](https://github.com/ReVanced/revanced-library/compare/v2.3.0...v2.4.0-dev.1) (2024-04-07)

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

@@ -1,13 +1,8 @@
public final class app/revanced/library/ApkSigner { public final class app/revanced/library/ApkSigner {
public static final field INSTANCE Lapp/revanced/library/ApkSigner; public static final field INSTANCE Lapp/revanced/library/ApkSigner;
public final fun newApkSigner (Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newApkSigner (Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer; public final fun newApkSigner (Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newApkSigner (Ljava/lang/String;Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newApkSigner (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newKeyStore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/Set;)V
public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore; public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore;
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair; public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore; public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
public final fun readPrivateKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair; public final fun readPrivateKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
} }
@@ -26,19 +21,12 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair {
} }
public final class app/revanced/library/ApkSigner$Signer { public final class app/revanced/library/ApkSigner$Signer {
public final fun signApk (Lcom/android/tools/build/apkzlib/zip/ZFile;)V
public final fun signApk (Ljava/io/File;)V
public final fun signApk (Ljava/io/File;Ljava/io/File;)V public final fun signApk (Ljava/io/File;Ljava/io/File;)V
} }
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/PatcherResult;Ljava/io/File;)V
public final fun newPrivateKeyCertificatePair (Lapp/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readPrivateKeyCertificatePairFromKeyStore (Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun sign (Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V
public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V
public final fun sign (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)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
} }
@@ -59,80 +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/ApkUtils$SigningOptions { public final class app/revanced/library/OptionsKt {
public fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getKeyStore ()Ljava/io/File;
public final fun getKeyStorePassword ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public final fun getSigner ()Ljava/lang/String;
} }
public final class app/revanced/library/Options { public final class app/revanced/library/PatchKt {
public static final field INSTANCE Lapp/revanced/library/Options; public static final fun mostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
public final fun deserialize (Ljava/lang/String;)[Lapp/revanced/library/Options$Patch; public static synthetic fun mostCommonCompatibleVersions$default (Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
public final fun serialize (Ljava/util/Set;Z)Ljava/lang/String;
public static synthetic fun serialize$default (Lapp/revanced/library/Options;Ljava/util/Set;ZILjava/lang/Object;)Ljava/lang/String;
public final fun setOptions (Ljava/util/Set;Ljava/io/File;)V
public final fun setOptions (Ljava/util/Set;Ljava/lang/String;)V
} }
public final class app/revanced/library/Options$Patch { public final class app/revanced/library/SerializationKt {
public final fun getOptions ()Ljava/util/List; public static final fun serializeTo (Ljava/util/Set;Ljava/io/OutputStream;Z)V
public final fun getPatchName ()Ljava/lang/String; public static synthetic fun serializeTo$default (Ljava/util/Set;Ljava/io/OutputStream;ZILjava/lang/Object;)V
}
public final class app/revanced/library/Options$Patch$Option {
public final fun getKey ()Ljava/lang/String;
public final fun getValue ()Ljava/lang/Object;
}
public final class app/revanced/library/PatchUtils {
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
}
public final class app/revanced/library/PatchUtils$Json {
public static final field INSTANCE Lapp/revanced/library/PatchUtils$Json;
public final fun deserialize (Ljava/io/InputStream;Ljava/lang/Class;)Ljava/util/Set;
public final fun serialize (Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;)V
public static synthetic fun serialize$default (Lapp/revanced/library/PatchUtils$Json;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;ILjava/lang/Object;)V
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch : app/revanced/library/PatchUtils$Json$JsonPatch {
public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$Companion;
public final fun getCompatiblePackages ()Ljava/util/Set;
public final fun getDependencies ()Ljava/util/Set;
public final fun getDescription ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getOptions ()Ljava/util/Map;
public final fun getRequiresIntegrations ()Z
public final fun getUse ()Z
public final fun setRequiresIntegrations (Z)V
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$Companion {
public final fun fromPatch (Lapp/revanced/patcher/patch/Patch;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch;
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption {
public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion;
public final fun getDefault ()Ljava/lang/Object;
public final fun getDescription ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getRequired ()Z
public final fun getTitle ()Ljava/lang/String;
public final fun getValueType ()Ljava/lang/String;
public final fun getValues ()Ljava/util/Map;
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion {
public final fun fromPatchOption (Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption;
}
public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch {
} }
public final class app/revanced/library/Utils { public final class app/revanced/library/Utils {
@@ -140,52 +66,6 @@ public final class app/revanced/library/Utils {
public final fun isAndroidEnvironment ()Z public final fun isAndroidEnvironment ()Z
} }
public abstract class app/revanced/library/adb/AdbManager {
public static final field Companion Lapp/revanced/library/adb/AdbManager$Companion;
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
protected abstract fun getInstaller ()Lapp/revanced/library/installation/installer/Installer;
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1;
public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/library/adb/AdbManager$Apk {
public fun <init> (Ljava/io/File;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getFile ()Ljava/io/File;
public final fun getPackageName ()Ljava/lang/String;
}
public final class app/revanced/library/adb/AdbManager$Companion {
public final fun getAdbManager (Ljava/lang/String;Z)Lapp/revanced/library/adb/AdbManager;
public static synthetic fun getAdbManager$default (Lapp/revanced/library/adb/AdbManager$Companion;Ljava/lang/String;ZILjava/lang/Object;)Lapp/revanced/library/adb/AdbManager;
}
public final class app/revanced/library/adb/AdbManager$DeviceNotFoundException : java/lang/Exception {
public fun <init> ()V
}
public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception {
}
public final class app/revanced/library/adb/AdbManager$PackageNameRequiredException : java/lang/Exception {
}
public final class app/revanced/library/adb/AdbManager$RootAdbManager : app/revanced/library/adb/AdbManager {
public static final field Utils Lapp/revanced/library/adb/AdbManager$RootAdbManager$Utils;
public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer;
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1;
public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/library/adb/AdbManager$RootAdbManager$Utils {
}
public final class app/revanced/library/adb/AdbManager$UserAdbManager : app/revanced/library/adb/AdbManager {
public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer;
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1;
public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner { public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner {
} }
@@ -249,12 +129,16 @@ public final class app/revanced/library/installation/installer/AdbInstallerResul
public static final field INSTANCE Lapp/revanced/library/installation/installer/AdbInstallerResult$Success; public static final field INSTANCE Lapp/revanced/library/installation/installer/AdbInstallerResult$Success;
} }
public final class app/revanced/library/installation/installer/AdbMountInstaller : app/revanced/library/installation/installer/MountInstaller { public final class app/revanced/library/installation/installer/AdbRootInstaller : app/revanced/library/installation/installer/RootInstaller {
public fun <init> ()V public fun <init> ()V
public fun <init> (Ljava/lang/String;)V public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
} }
public final class app/revanced/library/installation/installer/DeviceNotFoundException : java/lang/Exception {
public fun <init> ()V
}
public class app/revanced/library/installation/installer/Installation { public class app/revanced/library/installation/installer/Installation {
public final fun getApkFilePath ()Ljava/lang/String; public final fun getApkFilePath ()Ljava/lang/String;
} }
@@ -297,18 +181,18 @@ public final class app/revanced/library/installation/installer/LocalInstallerSer
public fun onStartCommand (Landroid/content/Intent;II)I public fun onStartCommand (Landroid/content/Intent;II)I
} }
public final class app/revanced/library/installation/installer/LocalMountInstaller : app/revanced/library/installation/installer/MountInstaller, java/io/Closeable { public final class app/revanced/library/installation/installer/LocalRootInstaller : app/revanced/library/installation/installer/RootInstaller, java/io/Closeable {
public fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V 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 synthetic fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun close ()V public fun close ()V
} }
public final class app/revanced/library/installation/installer/MountInstallation : app/revanced/library/installation/installer/Installation { public final class app/revanced/library/installation/installer/RootInstallation : app/revanced/library/installation/installer/Installation {
public final fun getInstalledApkFilePath ()Ljava/lang/String; public final fun getInstalledApkFilePath ()Ljava/lang/String;
public final fun getMounted ()Z public final fun getMounted ()Z
} }
public abstract class app/revanced/library/installation/installer/MountInstaller : app/revanced/library/installation/installer/Installer { public abstract class app/revanced/library/installation/installer/RootInstaller : app/revanced/library/installation/installer/Installer {
public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected final fun getShellCommandRunner ()Lapp/revanced/library/installation/command/ShellCommandRunner; 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; public fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -318,12 +202,12 @@ public abstract class app/revanced/library/installation/installer/MountInstaller
protected final fun write (Ljava/lang/String;Ljava/lang/String;)V protected final fun write (Ljava/lang/String;Ljava/lang/String;)V
} }
public final class app/revanced/library/installation/installer/MountInstallerResult : java/lang/Enum { public final class app/revanced/library/installation/installer/RootInstallerResult : java/lang/Enum {
public static final field FAILURE Lapp/revanced/library/installation/installer/MountInstallerResult; public static final field FAILURE Lapp/revanced/library/installation/installer/RootInstallerResult;
public static final field SUCCESS Lapp/revanced/library/installation/installer/MountInstallerResult; public static final field SUCCESS Lapp/revanced/library/installation/installer/RootInstallerResult;
public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/library/installation/installer/MountInstallerResult; public static fun valueOf (Ljava/lang/String;)Lapp/revanced/library/installation/installer/RootInstallerResult;
public static fun values ()[Lapp/revanced/library/installation/installer/MountInstallerResult; public static fun values ()[Lapp/revanced/library/installation/installer/RootInstallerResult;
} }
public final class app/revanced/library/logging/Logger { public final class app/revanced/library/logging/Logger {

View File

@@ -0,0 +1,167 @@
public final class app/revanced/library/ApkSigner {
public static final field INSTANCE Lapp/revanced/library/ApkSigner;
public final fun newApkSigner (Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore;
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
public final fun readPrivateKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
}
public final class app/revanced/library/ApkSigner$KeyStoreEntry {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public final fun getPrivateKeyCertificatePair ()Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
}
public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair {
public fun <init> (Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V
public final fun getCertificate ()Ljava/security/cert/X509Certificate;
public final fun getPrivateKey ()Ljava/security/PrivateKey;
}
public final class app/revanced/library/ApkSigner$Signer {
public final fun signApk (Ljava/io/File;Ljava/io/File;)V
}
public final class app/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 signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V
}
public final class app/revanced/library/ApkUtils$KeyStoreDetails {
public fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getKeyStore ()Ljava/io/File;
public final fun getKeyStorePassword ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
}
public final class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/util/Date;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Date;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCommonName ()Ljava/lang/String;
public final fun getValidUntil ()Ljava/util/Date;
}
public final class app/revanced/library/OptionsKt {
public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V
}
public final class app/revanced/library/PatchKt {
public static final fun mostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
public static synthetic fun mostCommonCompatibleVersions$default (Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
}
public final class app/revanced/library/SerializationKt {
public static final fun serializeTo (Ljava/util/Set;Ljava/io/OutputStream;Z)V
public static synthetic fun serializeTo$default (Ljava/util/Set;Ljava/io/OutputStream;ZILjava/lang/Object;)V
}
public final class app/revanced/library/Utils {
public static final field INSTANCE Lapp/revanced/library/Utils;
public final fun isAndroidEnvironment ()Z
}
public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner {
}
public abstract interface class app/revanced/library/installation/command/RunResult {
public abstract fun getError ()Ljava/lang/String;
public abstract fun getExitCode ()I
public abstract fun getOutput ()Ljava/lang/String;
public abstract fun waitFor ()V
}
public final class app/revanced/library/installation/command/RunResult$DefaultImpls {
public static fun waitFor (Lapp/revanced/library/installation/command/RunResult;)V
}
public abstract class app/revanced/library/installation/command/ShellCommandRunner {
protected final fun getLogger ()Ljava/util/logging/Logger;
protected abstract fun runCommand (Ljava/lang/String;)Lapp/revanced/library/installation/command/RunResult;
}
public final class app/revanced/library/installation/installer/AdbInstaller : app/revanced/library/installation/installer/Installer {
public fun <init> ()V
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class app/revanced/library/installation/installer/AdbInstallerResult {
}
public final class app/revanced/library/installation/installer/AdbInstallerResult$Failure : app/revanced/library/installation/installer/AdbInstallerResult {
public final fun getException ()Ljava/lang/Exception;
}
public final class app/revanced/library/installation/installer/AdbInstallerResult$Success : app/revanced/library/installation/installer/AdbInstallerResult {
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 fun <init> ()V
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
}
public final class app/revanced/library/installation/installer/DeviceNotFoundException : java/lang/Exception {
public fun <init> ()V
}
public class app/revanced/library/installation/installer/Installation {
public final fun getApkFilePath ()Ljava/lang/String;
}
public abstract class app/revanced/library/installation/installer/Installer {
public abstract fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected final fun getLogger ()Ljava/util/logging/Logger;
public abstract fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class app/revanced/library/installation/installer/Installer$Apk {
public fun <init> (Ljava/io/File;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getFile ()Ljava/io/File;
public final fun getPackageName ()Ljava/lang/String;
}
public final class app/revanced/library/installation/installer/RootInstallation : 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 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;
protected final fun invoke (Ljava/lang/String;)Lapp/revanced/library/installation/command/RunResult;
protected final fun move (Ljava/io/File;Ljava/lang/String;)V
public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
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 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 final class app/revanced/library/logging/Logger {
public static final field INSTANCE Lapp/revanced/library/logging/Logger;
public final fun addHandler (Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V
public final fun removeAllHandlers ()V
public final fun setDefault ()V
public final fun setFormat (Ljava/lang/String;)V
public static synthetic fun setFormat$default (Lapp/revanced/library/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V
}

View File

@@ -1,30 +1,141 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.binary.compatibility.validator)
alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.binary.compatibility.validator) apply false `maven-publish`
alias(libs.plugins.ktor) apply false signing
} }
group = "app.revanced" group = "app.revanced"
subprojects { // Because access to the project is necessary to authenticate with GitHub,
// Because access to the project is necessary to authenticate with GitHub, // the following block must be placed in the root build.gradle.kts file
// the following block must be placed in the root build.gradle.kts file // instead of the settings.gradle.kts file inside the dependencyResolutionManagement block.
// 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 {
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}
androidTarget {
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
publishLibraryVariants("release")
}
sourceSets {
androidMain.dependencies {
implementation(libs.core.ktx)
implementation(libs.libsu.nio)
implementation(libs.libsu.service)
}
commonMain.dependencies {
implementation(libs.apksig)
implementation(libs.apkzlib)
implementation(libs.bcpkix.jdk18on)
implementation(libs.guava)
implementation(libs.jadb)
implementation(libs.kotlin.reflect)
implementation(libs.kotlinx.serialization.json)
implementation(libs.revanced.patcher)
}
commonTest.dependencies {
implementation(libs.kotlin.test.junit)
implementation(libs.revanced.patcher)
}
}
}
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 { repositories {
mavenCentral()
mavenLocal()
google()
maven { maven {
// A repository must be specified for some reason. "registry" is a dummy. name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/revanced/registry") url = uri("https://maven.pkg.github.com/revanced/revanced-library")
credentials { credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR") username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN") password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
} }
} }
maven { url = uri("https://jitpack.io") } }
// KMP plugin creates a publication already, so just configure the POM.
publications.all {
if (this !is MavenPublication) return@all
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)
}

View File

@@ -1,4 +1,4 @@
version = 2.4.0-dev.1 version = 3.2.0-dev.1
#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,54 +1,35 @@
[versions] [versions]
jackson-module-kotlin = "2.15.0" android = "8.5.2"
jadb = "1.2.1" bcpkix-jdk18on = "1.77"
kotlin = "1.9.22" binary-compatibility-validator = "0.15.1"
ktor-client = "2.3.10" core-ktx = "1.15.0"
ktor-server-test-host = "2.3.9" guava = "33.2.1-jre"
revanced-patcher = "19.3.1" jadb = "1.2.1.1"
binary-compatibility-validator = "0.14.0" kotlin = "2.0.20"
android = "8.3.2" kotlinx-coroutines = "1.8.1"
bcpkix-jdk15on = "1.70" kotlinx-serialization = "1.7.1"
guava = "33.0.0-jre"
libsu = "5.2.2" libsu = "5.2.2"
core-ktx = "1.12.0" revanced-patcher = "21.0.0"
ktor = "2.3.9"
koin = "3.5.3"
logback = "1.4.14"
[libraries] [libraries]
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" }
jadb = { module = "app.revanced:jadb", version.ref = "jadb" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
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" } bcpkix-jdk18on = { module = "org.bouncycastle:bcpkix-jdk18on", version.ref = "bcpkix-jdk18on" }
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.
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
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" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
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] [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" } android-library = { id = "com.android.library", version.ref = "android" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

Binary file not shown.

View File

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

297
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 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.
@@ -15,69 +15,104 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" # This is normally unused
APP_BASE_NAME=`basename "$0"` # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$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
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
MAX_FD="$MAX_FD_LIMIT" # shellcheck disable=SC2039,SC3045
fi MAX_FD=$( ulimit -H -n ) ||
ulimit -n $MAX_FD warn "Could not query maximum file descriptor limit"
if [ $? -ne 0 ] ; then esac
warn "Could not set maximum file descriptor limit: $MAX_FD" case $MAX_FD in #(
fi '' | soft) :;; #(
else *)
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
fi # shellcheck disable=SC2039,SC3045
fi ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# 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
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

37
gradlew.bat vendored
View File

@@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -75,13 +78,15 @@ set CLASSPATH=%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
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View File

@@ -1,143 +0,0 @@
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;
}

View File

@@ -1,97 +0,0 @@
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"])
}

View File

@@ -1,6 +0,0 @@
package app.revanced.library.networking
import io.ktor.server.application.*
import io.ktor.util.pipeline.*
internal val PipelineContext<*, ApplicationCall>.parameters get() = call.parameters

View File

@@ -1,281 +0,0 @@
@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()
}

View File

@@ -1,43 +0,0 @@
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,
)
}
}

View File

@@ -1,33 +0,0 @@
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())) }
}
}

View File

@@ -1,28 +0,0 @@
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
}
}
}
}
}

View File

@@ -1,92 +0,0 @@
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))
}

View File

@@ -1,32 +0,0 @@
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()
}
}

View File

@@ -1,17 +0,0 @@
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
}

View File

@@ -1,41 +0,0 @@
@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())
}
}

View File

@@ -1,93 +0,0 @@
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()
}
}

View File

@@ -1,23 +0,0 @@
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()
}
}
}

View File

@@ -1,57 +0,0 @@
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()
}
}
}

View File

@@ -1,179 +0,0 @@
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)
}
}

View File

@@ -1,15 +0,0 @@
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)
}
}

View File

@@ -1,60 +0,0 @@
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
}
}

View File

@@ -1,57 +0,0 @@
@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,
)

View File

@@ -1,26 +0,0 @@
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())
}
}

View File

@@ -1,85 +0,0 @@
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()
}
}

View File

@@ -1,250 +0,0 @@
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()
}

View File

@@ -1,283 +0,0 @@
public final class app/revanced/library/ApkSigner {
public static final field INSTANCE Lapp/revanced/library/ApkSigner;
public final fun newApkSigner (Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newApkSigner (Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newApkSigner (Ljava/lang/String;Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newApkSigner (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$Signer;
public final fun newKeyStore (Ljava/io/OutputStream;Ljava/lang/String;Ljava/util/Set;)V
public final fun newKeyStore (Ljava/util/Set;)Ljava/security/KeyStore;
public final fun newPrivateKeyCertificatePair (Ljava/lang/String;Ljava/util/Date;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readKeyStore (Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/KeyStore;
public final fun readPrivateKeyCertificatePair (Ljava/security/KeyStore;Ljava/lang/String;Ljava/lang/String;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
}
public final class app/revanced/library/ApkSigner$KeyStoreEntry {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public final fun getPrivateKeyCertificatePair ()Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
}
public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair {
public fun <init> (Ljava/security/PrivateKey;Ljava/security/cert/X509Certificate;)V
public final fun getCertificate ()Ljava/security/cert/X509Certificate;
public final fun getPrivateKey ()Ljava/security/PrivateKey;
}
public final class app/revanced/library/ApkSigner$Signer {
public final fun signApk (Lcom/android/tools/build/apkzlib/zip/ZFile;)V
public final fun signApk (Ljava/io/File;)V
public final fun signApk (Ljava/io/File;Ljava/io/File;)V
}
public final class app/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 newPrivateKeyCertificatePair (Lapp/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun readPrivateKeyCertificatePairFromKeyStore (Lapp/revanced/library/ApkUtils$KeyStoreDetails;)Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;
public final fun sign (Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V
public final fun sign (Ljava/io/File;Ljava/io/File;Lapp/revanced/library/ApkUtils$SigningOptions;)V
public final fun sign (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkSigner$PrivateKeyCertificatePair;)V
public final fun signApk (Ljava/io/File;Ljava/io/File;Ljava/lang/String;Lapp/revanced/library/ApkUtils$KeyStoreDetails;)V
}
public final class app/revanced/library/ApkUtils$KeyStoreDetails {
public fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getKeyStore ()Ljava/io/File;
public final fun getKeyStorePassword ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
}
public final class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetails {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/util/Date;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Date;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCommonName ()Ljava/lang/String;
public final fun getValidUntil ()Ljava/util/Date;
}
public final class app/revanced/library/ApkUtils$SigningOptions {
public fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAlias ()Ljava/lang/String;
public final fun getKeyStore ()Ljava/io/File;
public final fun getKeyStorePassword ()Ljava/lang/String;
public final fun getPassword ()Ljava/lang/String;
public final fun getSigner ()Ljava/lang/String;
}
public final class app/revanced/library/Options {
public static final field INSTANCE Lapp/revanced/library/Options;
public final fun deserialize (Ljava/lang/String;)[Lapp/revanced/library/Options$Patch;
public final fun serialize (Ljava/util/Set;Z)Ljava/lang/String;
public static synthetic fun serialize$default (Lapp/revanced/library/Options;Ljava/util/Set;ZILjava/lang/Object;)Ljava/lang/String;
public final fun setOptions (Ljava/util/Set;Ljava/io/File;)V
public final fun setOptions (Ljava/util/Set;Ljava/lang/String;)V
}
public final class app/revanced/library/Options$Patch {
public final fun getOptions ()Ljava/util/List;
public final fun getPatchName ()Ljava/lang/String;
}
public final class app/revanced/library/Options$Patch$Option {
public final fun getKey ()Ljava/lang/String;
public final fun getValue ()Ljava/lang/Object;
}
public final class app/revanced/library/PatchUtils {
public static final field INSTANCE Lapp/revanced/library/PatchUtils;
public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map;
public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map;
}
public final class app/revanced/library/PatchUtils$Json {
public static final field INSTANCE Lapp/revanced/library/PatchUtils$Json;
public final fun deserialize (Ljava/io/InputStream;Ljava/lang/Class;)Ljava/util/Set;
public final fun serialize (Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;)V
public static synthetic fun serialize$default (Lapp/revanced/library/PatchUtils$Json;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;ILjava/lang/Object;)V
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch : app/revanced/library/PatchUtils$Json$JsonPatch {
public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$Companion;
public final fun getCompatiblePackages ()Ljava/util/Set;
public final fun getDependencies ()Ljava/util/Set;
public final fun getDescription ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getOptions ()Ljava/util/Map;
public final fun getRequiresIntegrations ()Z
public final fun getUse ()Z
public final fun setRequiresIntegrations (Z)V
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$Companion {
public final fun fromPatch (Lapp/revanced/patcher/patch/Patch;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch;
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption {
public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion;
public final fun getDefault ()Ljava/lang/Object;
public final fun getDescription ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getRequired ()Z
public final fun getTitle ()Ljava/lang/String;
public final fun getValueType ()Ljava/lang/String;
public final fun getValues ()Ljava/util/Map;
}
public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion {
public final fun fromPatchOption (Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption;
}
public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch {
}
public final class app/revanced/library/Utils {
public static final field INSTANCE Lapp/revanced/library/Utils;
public final fun isAndroidEnvironment ()Z
}
public abstract class app/revanced/library/adb/AdbManager {
public static final field Companion Lapp/revanced/library/adb/AdbManager$Companion;
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
protected abstract fun getInstaller ()Lapp/revanced/library/installation/installer/Installer;
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1;
public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/library/adb/AdbManager$Apk {
public fun <init> (Ljava/io/File;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getFile ()Ljava/io/File;
public final fun getPackageName ()Ljava/lang/String;
}
public final class app/revanced/library/adb/AdbManager$Companion {
public final fun getAdbManager (Ljava/lang/String;Z)Lapp/revanced/library/adb/AdbManager;
public static synthetic fun getAdbManager$default (Lapp/revanced/library/adb/AdbManager$Companion;Ljava/lang/String;ZILjava/lang/Object;)Lapp/revanced/library/adb/AdbManager;
}
public final class app/revanced/library/adb/AdbManager$DeviceNotFoundException : java/lang/Exception {
public fun <init> ()V
}
public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception {
}
public final class app/revanced/library/adb/AdbManager$PackageNameRequiredException : java/lang/Exception {
}
public final class app/revanced/library/adb/AdbManager$RootAdbManager : app/revanced/library/adb/AdbManager {
public static final field Utils Lapp/revanced/library/adb/AdbManager$RootAdbManager$Utils;
public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer;
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1;
public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/library/adb/AdbManager$RootAdbManager$Utils {
}
public final class app/revanced/library/adb/AdbManager$UserAdbManager : app/revanced/library/adb/AdbManager {
public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer;
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1;
public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1;
}
public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner {
}
public abstract interface class app/revanced/library/installation/command/RunResult {
public abstract fun getError ()Ljava/lang/String;
public abstract fun getExitCode ()I
public abstract fun getOutput ()Ljava/lang/String;
public abstract fun waitFor ()V
}
public final class app/revanced/library/installation/command/RunResult$DefaultImpls {
public static fun waitFor (Lapp/revanced/library/installation/command/RunResult;)V
}
public abstract class app/revanced/library/installation/command/ShellCommandRunner {
protected final fun getLogger ()Ljava/util/logging/Logger;
protected abstract fun runCommand (Ljava/lang/String;)Lapp/revanced/library/installation/command/RunResult;
}
public final class app/revanced/library/installation/installer/AdbInstaller : app/revanced/library/installation/installer/Installer {
public fun <init> ()V
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public abstract interface class app/revanced/library/installation/installer/AdbInstallerResult {
}
public final class app/revanced/library/installation/installer/AdbInstallerResult$Failure : app/revanced/library/installation/installer/AdbInstallerResult {
public final fun getException ()Ljava/lang/Exception;
}
public final class app/revanced/library/installation/installer/AdbInstallerResult$Success : app/revanced/library/installation/installer/AdbInstallerResult {
public static final field INSTANCE Lapp/revanced/library/installation/installer/AdbInstallerResult$Success;
}
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
}
public class app/revanced/library/installation/installer/Installation {
public final fun getApkFilePath ()Ljava/lang/String;
}
public abstract class app/revanced/library/installation/installer/Installer {
public abstract fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected final fun getLogger ()Ljava/util/logging/Logger;
public abstract fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}
public final class app/revanced/library/installation/installer/Installer$Apk {
public fun <init> (Ljava/io/File;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/io/File;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getFile ()Ljava/io/File;
public final fun getPackageName ()Ljava/lang/String;
}
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/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;
protected final fun invoke (Ljava/lang/String;)Lapp/revanced/library/installation/command/RunResult;
protected final fun move (Ljava/io/File;Ljava/lang/String;)V
public fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
protected final fun write (Ljava/lang/String;Ljava/lang/String;)V
}
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/MountInstallerResult;
public static fun values ()[Lapp/revanced/library/installation/installer/MountInstallerResult;
}
public final class app/revanced/library/logging/Logger {
public static final field INSTANCE Lapp/revanced/library/logging/Logger;
public final fun addHandler (Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V
public final fun removeAllHandlers ()V
public final fun setDefault ()V
public final fun setFormat (Ljava/lang/String;)V
public static synthetic fun setFormat$default (Lapp/revanced/library/logging/Logger;Ljava/lang/String;ILjava/lang/Object;)V
}

View File

@@ -1,123 +0,0 @@
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"])
}

View File

@@ -1,43 +0,0 @@
@file:Suppress("DeprecatedCallableAddReplaceWith")
package app.revanced.library
import app.revanced.library.installation.command.AdbShellCommandRunner
import se.vidstige.jadb.JadbDevice
import se.vidstige.jadb.ShellProcessBuilder
import java.io.File
@Deprecated("Do not use this anymore. Instead use AdbCommandRunner.")
internal fun JadbDevice.buildCommand(
command: String,
su: Boolean = true,
): ShellProcessBuilder {
if (su) return shellProcessBuilder("su -c \'$command\'")
val args = command.split(" ") as ArrayList<String>
val cmd = args.removeFirst()
return shellProcessBuilder(cmd, *args.toTypedArray())
}
@Suppress("DEPRECATION")
@Deprecated("Use AdbShellCommandRunner instead.")
internal fun JadbDevice.run(
command: String,
su: Boolean = true,
) = buildCommand(command, su).start()
@Deprecated("Use AdbShellCommandRunner instead.")
internal fun JadbDevice.hasSu() = AdbShellCommandRunner(this).hasRootPermission()
@Deprecated("Use AdbShellCommandRunner instead.")
internal fun JadbDevice.push(
file: File,
targetFilePath: String,
) = AdbShellCommandRunner(this).move(file, targetFilePath)
@Deprecated("Use AdbShellCommandRunner instead.")
internal fun JadbDevice.createFile(
targetFile: String,
content: String,
) = AdbShellCommandRunner(this).write(content.byteInputStream(), targetFile)

View File

@@ -1,120 +0,0 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package app.revanced.library
import app.revanced.library.Options.Patch.Option
import app.revanced.patcher.PatchSet
import app.revanced.patcher.patch.options.PatchOptionException
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import java.io.File
import java.util.logging.Logger
@Suppress("unused")
object Options {
private val logger = Logger.getLogger(Options::class.java.name)
private val mapper = jacksonObjectMapper()
/**
* Serializes the options for a set of patches.
*
* @param patches The set of patches to serialize.
* @param prettyPrint Whether to pretty print the JSON.
* @return The JSON string containing the options.
*/
fun serialize(
patches: PatchSet,
prettyPrint: Boolean = false,
): String =
patches
.filter { it.options.any() }
.map { patch ->
Patch(
patch.name!!,
patch.options.values.map { option ->
val optionValue =
try {
option.value
} catch (e: PatchOptionException) {
logger.warning("Using default option value for the ${patch.name} patch: ${e.message}")
option.default
}
Option(option.key, optionValue)
},
)
}
// See https://github.com/revanced/revanced-patches/pull/2434/commits/60e550550b7641705e81aa72acfc4faaebb225e7.
.distinctBy { it.patchName }
.let {
if (prettyPrint) {
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(it)
} else {
mapper.writeValueAsString(it)
}
}
/**
* Deserializes the options to a set of patches.
*
* @param json The JSON string containing the options.
* @return A set of [Patch]s.
* @see Patch
*/
fun deserialize(json: String): Array<Patch> = mapper.readValue(json, Array<Patch>::class.java)
/**
* Sets the options for a set of patches.
*
* @param json The JSON string containing the options.
*/
fun PatchSet.setOptions(json: String) {
filter { it.options.any() }.let { patches ->
if (patches.isEmpty()) return
val jsonPatches =
deserialize(json).associate {
it.patchName to it.options.associate { option -> option.key to option.value }
}
patches.forEach { patch ->
jsonPatches[patch.name]?.let { jsonPatchOptions ->
jsonPatchOptions.forEach { (option, value) ->
try {
patch.options[option] = value
} catch (e: PatchOptionException) {
logger.warning("Could not set option value for the ${patch.name} patch: ${e.message}")
}
}
}
}
}
}
/**
* Sets the options for a set of patches.
*
* @param file The file containing the JSON string containing the options.
* @see setOptions
*/
fun PatchSet.setOptions(file: File) = setOptions(file.readText())
/**
* Data class for a patch and its [Option]s.
*
* @property patchName The name of the patch.
* @property options The [Option]s for the patch.
*/
class Patch internal constructor(
val patchName: String,
val options: List<Option>,
) {
/**
* Data class for patch option.
*
* @property key The name of the option.
* @property value The value of the option.
*/
class Option internal constructor(val key: String, val value: Any?)
}
}

View File

@@ -1,169 +0,0 @@
package app.revanced.library
import app.revanced.patcher.PatchSet
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.options.PatchOption
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import java.io.InputStream
import java.io.OutputStream
import kotlin.reflect.jvm.jvmName
typealias PackageName = String
typealias Version = String
typealias Count = Int
typealias VersionMap = LinkedHashMap<Version, Count>
typealias PackageNameMap = Map<PackageName, VersionMap>
/**
* Utility functions for working with patches.
*/
@Suppress("MemberVisibilityCanBePrivate", "unused")
object PatchUtils {
/**
* Get the count of versions for each compatible package from a supplied set of [patches] ordered by the most common version.
*
* @param patches The set of patches to check.
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
* @param countUnusedPatches Whether to count patches that are not used.
* @return A map of package names to a map of versions to their count.
*/
fun getMostCommonCompatibleVersions(
patches: PatchSet,
packageNames: Set<String>? = null,
countUnusedPatches: Boolean = false,
): PackageNameMap =
buildMap {
fun filterWantedPackages(compatiblePackages: Iterable<Patch.CompatiblePackage>): Iterable<Patch.CompatiblePackage> {
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
return compatiblePackages.filter { it.name in wantedPackages }
}
patches
.filter { it.use || countUnusedPatches }
.flatMap { it.compatiblePackages ?: emptyList() }
.let(::filterWantedPackages)
.forEach { compatiblePackage ->
if (compatiblePackage.versions?.isEmpty() == true) {
return@forEach
}
val versionMap = getOrPut(compatiblePackage.name) { linkedMapOf() }
compatiblePackage.versions?.let { versions ->
versions.forEach { version ->
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
}
}
}
// Sort the version maps by the most common version.
forEach { (packageName, versionMap) ->
this[packageName] =
versionMap
.asIterable()
.sortedWith(compareByDescending { it.value })
.associate { it.key to it.value } as VersionMap
}
}
object Json {
private val mapper = jacksonObjectMapper()
/**
* Serializes a set of [Patch]es to a JSON string and writes it to an output stream.
*
* @param patches The set of [Patch]es to serialize.
* @param transform A function to transform the [Patch]es to [JsonPatch]es.
* @param prettyPrint Whether to pretty print the JSON.
* @param outputStream The output stream to write the JSON to.
*/
fun serialize(
patches: PatchSet,
transform: (Patch<*>) -> JsonPatch = { patch -> FullJsonPatch.fromPatch(patch) },
prettyPrint: Boolean = false,
outputStream: OutputStream,
) {
patches.map(transform).let { transformed ->
if (prettyPrint) {
mapper.writerWithDefaultPrettyPrinter().writeValue(outputStream, transformed)
} else {
mapper.writeValue(outputStream, transformed)
}
}
}
/**
* Deserializes a JSON string to a set of [FullJsonPatch]es from an input stream.
*
* @param inputStream The input stream to read the JSON from.
* @param jsonPatchElementClass The class of the [JsonPatch]es to deserialize.
* @return A set of [JsonPatch]es.
* @see FullJsonPatch
*/
fun <T : JsonPatch> deserialize(
inputStream: InputStream,
jsonPatchElementClass: Class<T>,
): Set<T> =
mapper.readValue(
inputStream,
mapper.typeFactory.constructCollectionType(Set::class.java, jsonPatchElementClass),
)
interface JsonPatch
/**
* A JSON representation of a [Patch].
* @see Patch
*/
class FullJsonPatch internal constructor(
val name: String?,
val description: String?,
val compatiblePackages: Set<Patch.CompatiblePackage>?,
val dependencies: Set<String>?,
val use: Boolean,
var requiresIntegrations: Boolean,
val options: Map<String, FullJsonPatchOption<*>>,
) : JsonPatch {
companion object {
fun fromPatch(patch: Patch<*>) =
FullJsonPatch(
patch.name,
patch.description,
patch.compatiblePackages,
buildSet { patch.dependencies?.forEach { add(it.jvmName) } },
patch.use,
patch.requiresIntegrations,
patch.options.mapValues { FullJsonPatchOption.fromPatchOption(it.value) },
)
}
/**
* A JSON representation of a [PatchOption].
* @see PatchOption
*/
class FullJsonPatchOption<T> internal constructor(
val key: String,
val default: T?,
val values: Map<String, T?>?,
val title: String?,
val description: String?,
val required: Boolean,
val valueType: String,
) {
companion object {
fun fromPatchOption(option: PatchOption<*>) =
FullJsonPatchOption(
option.key,
option.default,
option.values,
option.title,
option.description,
option.required,
option.valueType,
)
}
}
}
}
}

View File

@@ -1,144 +0,0 @@
@file:Suppress("DEPRECATION")
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.AdbMountInstaller
import app.revanced.library.installation.installer.Constants.PLACEHOLDER
import app.revanced.library.installation.installer.Installer
import app.revanced.library.run
import se.vidstige.jadb.JadbDevice
import java.io.File
/**
* [AdbManager] to install and uninstall [Apk] files.
*
* @param deviceSerial The serial of the device. If null, the first connected device will be used.
*/
@Deprecated("Use an implementation of Installer instead.")
@Suppress("unused")
sealed class AdbManager private constructor(
@Suppress("UNUSED_PARAMETER") deviceSerial: String?,
) {
protected abstract val installer: Installer<*, *>
/**
* Installs the [Apk] file.
*
* @param apk The [Apk] file.
*/
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use Installer.install instead.")
open fun install(apk: Apk) = suspend {
installer.install(Installer.Apk(apk.file, apk.packageName))
}
/**
* Uninstalls the package.
*
* @param packageName The package name.
*/
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use Installer.uninstall instead.")
open fun uninstall(packageName: String) = suspend {
installer.uninstall(packageName)
}
@Deprecated("Use Installer instead.")
companion object {
/**
* Gets an [AdbManager] for the supplied device serial.
*
* @param deviceSerial The device serial. If null, the first connected device will be used.
* @param root Whether to use root or not.
* @return The [AdbManager].
* @throws DeviceNotFoundException If the device can not be found.
*/
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("This is deprecated.")
fun getAdbManager(
deviceSerial: String? = null,
root: Boolean = false,
): AdbManager = if (root) RootAdbManager(deviceSerial) else UserAdbManager(deviceSerial)
}
/**
* Adb manager for rooted devices.
*
* @param deviceSerial The device serial. If null, the first connected device will be used.
*/
@Deprecated("Use AdbMountInstaller instead.", ReplaceWith("AdbMountInstaller(deviceSerial)"))
class RootAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) {
override val installer = AdbMountInstaller(deviceSerial)
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use AdbMountInstaller.install instead.")
override fun install(apk: Apk) = suspend {
installer.install(Installer.Apk(apk.file, apk.packageName))
}
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use AdbMountInstaller.uninstall instead.")
override fun uninstall(packageName: String) = suspend {
installer.uninstall(packageName)
}
@Deprecated("This is deprecated.")
companion object Utils {
private fun JadbDevice.run(
command: String,
with: String,
) = run(command.applyReplacement(with))
private fun String.applyReplacement(with: String) = replace(PLACEHOLDER, with)
}
}
/**
* Adb manager for non-rooted devices.
*
* @param deviceSerial The device serial. If null, the first connected device will be used.
*/
@Deprecated("Use AdbInstaller instead.")
class UserAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) {
override val installer = AdbInstaller(deviceSerial)
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use AdbInstaller.install instead.")
override fun install(apk: Apk) = suspend {
installer.install(Installer.Apk(apk.file, apk.packageName))
}
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Use AdbInstaller.uninstall instead.")
override fun uninstall(packageName: String) = suspend {
installer.uninstall(packageName)
}
}
/**
* Apk file for [AdbManager].
*
* @param file The [Apk] file.
* @param packageName The package name of the [Apk] file.
*/
@Deprecated("Use Installer.Apk instead.")
class Apk(val file: File, val packageName: String? = null)
@Deprecated("Use AdbCommandRunner.DeviceNotFoundException instead.")
class DeviceNotFoundException internal constructor(deviceSerial: String? = null) :
Exception(
deviceSerial?.let {
"The device with the ADB device serial \"$deviceSerial\" can not be found"
} ?: "No ADB device found",
)
@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 MountInstaller.PackageNameRequiredException instead.")
class PackageNameRequiredException internal constructor() :
Exception("Package name is required")
}

View File

@@ -1,40 +0,0 @@
package app.revanced.library.installation.installer
import se.vidstige.jadb.JadbConnection
import java.util.logging.Logger
/**
* Utility functions for [Installer].
*
* @see Installer
*/
internal object Utils {
/**
* Gets the device with the given serial.
*
* @param deviceSerial The device serial. If null, the first connected device will be used.
* @param logger The logger.
* @return The device.
* @throws DeviceNotFoundException If no device with the given serial is found.
*/
internal fun getDevice(
deviceSerial: String? = null,
logger: Logger,
) = with(JadbConnection().devices) {
if (isEmpty()) throw DeviceNotFoundException()
deviceSerial?.let {
firstOrNull { it.serial == deviceSerial } ?: throw DeviceNotFoundException(
deviceSerial,
)
} ?: first().also {
logger.warning("No device serial supplied. Using device with serial ${it.serial}")
}
}!!
class DeviceNotFoundException internal constructor(deviceSerial: String? = null) : Exception(
deviceSerial?.let {
"The device with the ADB device serial \"$deviceSerial\" can not be found"
} ?: "No ADB device found",
)
}

View File

@@ -1,41 +0,0 @@
package app.revanced.library
import app.revanced.library.Options.setOptions
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
import kotlin.test.Test
class PatchOptionsTest {
private var patches = setOf(PatchOptionsTestPatch)
private val serializedJson =
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":null},{\"key\":\"key2\"," +
"\"value\":true}]}]"
private val changedJson =
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2" +
"\",\"value\":false}]}]"
@Test
fun `serializes and deserializes`() {
assert(serializedJson == Options.serialize(patches))
patches.setOptions(changedJson)
assert(PatchOptionsTestPatch.option1 == "test")
assert(PatchOptionsTestPatch.option2 == false)
}
@Patch("PatchOptionsTestPatch")
object PatchOptionsTestPatch : BytecodePatch(emptySet()) {
var option1 by stringPatchOption("key1", null, null, "title1", "description1")
var option2 by booleanPatchOption("key2", true, null, "title2", "description2")
override fun execute(context: BytecodeContext) {
// Do nothing
}
}
}

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

@@ -1,3 +1,6 @@
// TODO: Figure out why this causes problems.
rootProject.name = "revanced-library"
buildCache { buildCache {
local { local {
isEnabled = "CI" !in System.getenv() isEnabled = "CI" !in System.getenv()
@@ -6,10 +9,7 @@ buildCache {
pluginManagement { pluginManagement {
repositories { repositories {
gradlePluginPortal()
mavenCentral()
google() google()
mavenCentral()
} }
} }
include(":library", ":library-networking")

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
@@ -85,10 +86,13 @@ class LocalInstaller(
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

@@ -3,15 +3,15 @@ package app.revanced.library.installation.installer
import android.content.Context import android.content.Context
import app.revanced.library.installation.command.LocalShellCommandRunner import app.revanced.library.installation.command.LocalShellCommandRunner
import app.revanced.library.installation.installer.Installer.Apk import app.revanced.library.installation.installer.Installer.Apk
import app.revanced.library.installation.installer.MountInstaller.NoRootPermissionException import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
import com.topjohnwu.superuser.ipc.RootService import com.topjohnwu.superuser.ipc.RootService
import java.io.Closeable import java.io.Closeable
/** /**
* [LocalMountInstaller] for installing and uninstalling [Apk] files locally with using root permissions by mounting. * [LocalRootInstaller] 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 context The [Context] to use for binding to the [RootService].
* @param onReady A callback to be invoked when [LocalMountInstaller] is ready to be used. * @param onReady A callback to be invoked when [LocalRootInstaller] is ready to be used.
* *
* @throws NoRootPermissionException If the device does not have root permission. * @throws NoRootPermissionException If the device does not have root permission.
* *
@@ -19,13 +19,13 @@ import java.io.Closeable
* @see LocalShellCommandRunner * @see LocalShellCommandRunner
*/ */
@Suppress("unused") @Suppress("unused")
class LocalMountInstaller( class LocalRootInstaller(
context: Context, context: Context,
onReady: LocalMountInstaller.() -> Unit = {}, onReady: LocalRootInstaller.() -> Unit = {},
) : MountInstaller( ) : RootInstaller(
{ installer -> { installer ->
LocalShellCommandRunner(context) { LocalShellCommandRunner(context) {
(installer as LocalMountInstaller).onReady() (installer as LocalRootInstaller).onReady()
} }
}, },
), ),

View File

@@ -1,9 +1,6 @@
package app.revanced.library package app.revanced.library
import com.android.apksig.ApkSigner.SignerConfig import com.android.apksig.ApkSigner.SignerConfig
import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zip.ZFile
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
@@ -13,7 +10,6 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
import java.math.BigInteger import java.math.BigInteger
import java.security.* import java.security.*
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@@ -197,105 +193,6 @@ object ApkSigner {
), ),
) )
/**
* Read a [PrivateKeyCertificatePair] from a keystore entry.
*
* @param keyStore The keystore to read the entry from.
* @param keyStoreEntryAlias The alias of the key store entry to read.
* @param keyStoreEntryPassword The password for recovering the signing key.
*
* @return The read [PrivateKeyCertificatePair].
*
* @throws IllegalArgumentException If the keystore does not contain the given alias or the password is invalid.
*/
@Deprecated("This method will be removed in the future.")
fun readKeyCertificatePair(
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = readPrivateKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword)
/**
* Create a new keystore with a new keypair and saves it to the given [keyStoreOutputStream].
*
* @param keyStoreOutputStream The stream to write the keystore to.
* @param keyStorePassword The password for the keystore.
* @param entries The entries to add to the keystore.
*/
@Deprecated("This method will be removed in the future.")
fun newKeyStore(
keyStoreOutputStream: OutputStream,
keyStorePassword: String?,
entries: Set<KeyStoreEntry>,
) = newKeyStore(entries).store(
keyStoreOutputStream,
keyStorePassword?.toCharArray(),
)
/**
* Create a new [Signer].
*
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
*
* @return The new [Signer].
*
* @see PrivateKeyCertificatePair
* @see Signer
*/
@Deprecated("This method will be removed in the future.")
fun newApkSigner(privateKeyCertificatePair: PrivateKeyCertificatePair) =
Signer(
SigningExtension(
SigningOptions.builder()
.setMinSdkVersion(21) // TODO: Extracting from the target APK would be ideal.
.setV1SigningEnabled(true)
.setV2SigningEnabled(true)
.setCertificates(privateKeyCertificatePair.certificate)
.setKey(privateKeyCertificatePair.privateKey)
.build(),
),
)
/**
* Create a new [Signer].
*
* @param signer The name of the signer.
* @param keyStore The keystore to use for signing.
* @param keyStoreEntryAlias The alias of the key store entry to use for signing.
* @param keyStoreEntryPassword The password for recovering the signing key.
*
* @return The new [Signer].
*
* @see KeyStore
* @see Signer
*/
@Deprecated("This method will be removed in the future.")
fun newApkSigner(
signer: String,
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = newApkSigner(signer, readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
/**
* Create a new [Signer].
*
* @param keyStore The keystore to use for signing.
* @param keyStoreEntryAlias The alias of the key store entry to use for signing.
* @param keyStoreEntryPassword The password for recovering the signing key.
*
* @return The new [Signer].
*
* @see KeyStore
* @see Signer
*/
@Deprecated("This method will be removed in the future.")
fun newApkSigner(
keyStore: KeyStore,
keyStoreEntryAlias: String,
keyStoreEntryPassword: String,
) = newApkSigner("ReVanced", readKeyCertificatePair(keyStore, keyStoreEntryAlias, keyStoreEntryPassword))
/** /**
* An entry in a keystore. * An entry in a keystore.
* *
@@ -322,48 +219,11 @@ object ApkSigner {
val certificate: X509Certificate, val certificate: X509Certificate,
) )
class Signer { class Signer internal constructor(private val signerBuilder: com.android.apksig.ApkSigner.Builder) {
private val signerBuilder: com.android.apksig.ApkSigner.Builder?
private val signingExtension: SigningExtension?
internal constructor(signerBuilder: com.android.apksig.ApkSigner.Builder) {
this.signerBuilder = signerBuilder
signingExtension = null
}
fun signApk(inputApkFile: File, outputApkFile: File) { fun signApk(inputApkFile: File, outputApkFile: File) {
logger.info("Signing APK") logger.info("Signing APK")
signerBuilder?.setInputApk(inputApkFile)?.setOutputApk(outputApkFile)?.build()?.sign() signerBuilder.setInputApk(inputApkFile)?.setOutputApk(outputApkFile)?.build()?.sign()
}
@Deprecated("This constructor will be removed in the future.")
internal constructor(signingExtension: SigningExtension) {
signerBuilder = null
this.signingExtension = signingExtension
}
/**
* Sign an APK file.
*
* @param apkFile The APK file to sign.
*/
@Deprecated("This method will be removed in the future.")
fun signApk(apkFile: File) = ZFile.openReadWrite(apkFile).use {
@Suppress("DEPRECATION")
signApk(it)
}
/**
* Sign an APK file.
*
* @param apkZFile The APK [ZFile] to sign.
*/
@Deprecated("This method will be removed in the future.")
fun signApk(apkZFile: ZFile) {
logger.info("Signing ${apkZFile.file.name}")
signingExtension?.register(apkZFile)
} }
} }
} }

View File

@@ -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)
} }
} }
@@ -103,8 +103,7 @@ object ApkUtils {
* *
* @return The newly created private key and certificate pair. * @return The newly created private key and certificate pair.
*/ */
@Deprecated("This method will be removed in the future.") private fun newPrivateKeyCertificatePair(
fun newPrivateKeyCertificatePair(
privateKeyCertificatePairDetails: PrivateKeyCertificatePairDetails, privateKeyCertificatePairDetails: PrivateKeyCertificatePairDetails,
keyStoreDetails: KeyStoreDetails, keyStoreDetails: KeyStoreDetails,
) = newPrivateKeyCertificatePair( ) = newPrivateKeyCertificatePair(
@@ -132,8 +131,7 @@ object ApkUtils {
* *
* @return The private key and certificate pair. * @return The private key and certificate pair.
*/ */
@Deprecated("This method will be removed in the future.") private fun readPrivateKeyCertificatePairFromKeyStore(
fun readPrivateKeyCertificatePairFromKeyStore(
keyStoreDetails: KeyStoreDetails, keyStoreDetails: KeyStoreDetails,
) = ApkSigner.readPrivateKeyCertificatePair( ) = ApkSigner.readPrivateKeyCertificatePair(
ApkSigner.readKeyStore( ApkSigner.readKeyStore(
@@ -168,91 +166,6 @@ object ApkUtils {
}, },
).signApk(inputApkFile, outputApkFile) ).signApk(inputApkFile, outputApkFile)
@Deprecated("This method will be removed in the future.")
private fun readOrNewPrivateKeyCertificatePair(
signingOptions: SigningOptions,
): ApkSigner.PrivateKeyCertificatePair {
val privateKeyCertificatePairDetails = PrivateKeyCertificatePairDetails(
signingOptions.alias,
PrivateKeyCertificatePairDetails().validUntil,
)
val keyStoreDetails = KeyStoreDetails(
signingOptions.keyStore,
signingOptions.keyStorePassword,
signingOptions.alias,
signingOptions.password,
)
return if (keyStoreDetails.keyStore.exists()) {
readPrivateKeyCertificatePairFromKeyStore(keyStoreDetails)
} else {
newPrivateKeyCertificatePair(privateKeyCertificatePairDetails, keyStoreDetails)
}
}
/**
* Signs [inputApkFile] with the given options and saves the signed apk to [outputApkFile].
*
* @param inputApkFile The apk file to sign.
* @param outputApkFile The file to save the signed apk to.
* @param signer The name of the signer.
* @param privateKeyCertificatePair The private key and certificate pair to use for signing.
*/
@Deprecated("This method will be removed in the future.")
fun sign(
inputApkFile: File,
outputApkFile: File,
signer: String,
privateKeyCertificatePair: ApkSigner.PrivateKeyCertificatePair,
) = ApkSigner.newApkSigner(
signer,
privateKeyCertificatePair,
).signApk(inputApkFile, outputApkFile)
/**
* Signs the apk file with the given options.
*
* @param signingOptions The options to use for signing.
*/
@Deprecated("This method will be removed in the future.")
fun File.sign(signingOptions: SigningOptions) = ApkSigner.newApkSigner(
signingOptions.signer,
readOrNewPrivateKeyCertificatePair(signingOptions),
).signApk(this)
/**
* Signs [inputApkFile] with the given options and saves the signed apk to [outputApkFile].
*
* @param inputApkFile The apk file to sign.
* @param outputApkFile The file to save the signed apk to.
* @param signingOptions The options to use for signing.
*/
@Deprecated("This method will be removed in the future.")
fun sign(inputApkFile: File, outputApkFile: File, signingOptions: SigningOptions) = sign(
inputApkFile,
outputApkFile,
signingOptions.signer,
readOrNewPrivateKeyCertificatePair(signingOptions),
)
/**
* Options for signing an apk.
*
* @param keyStore The keystore to use for signing.
* @param keyStorePassword The password for the keystore.
* @param alias The alias of the key store entry to use for signing.
* @param password The password for recovering the signing key.
* @param signer The name of the signer.
*/
@Deprecated("This class will be removed in the future.")
class SigningOptions(
val keyStore: File,
val keyStorePassword: String?,
val alias: String = "ReVanced Key",
val password: String = "",
val signer: String = "ReVanced",
)
/** /**
* Details for a keystore. * Details for a keystore.
* *

View File

@@ -0,0 +1,36 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package app.revanced.library
import app.revanced.patcher.patch.OptionException
import app.revanced.patcher.patch.Patch
import java.util.logging.Logger
typealias PatchName = String
typealias OptionKey = String
typealias OptionValue = Any?
typealias PatchesOptions = Map<PatchName, Map<OptionKey, OptionValue>>
private val logger = Logger.getLogger("Options")
/**
* Set the options for a set of patches that have a name.
*
* @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 ->
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",
)
}
try {
patch.options[optionKey] = optionValue
} catch (e: OptionException) {
logger.warning("Could not set option value for the \"${patch.name}\" patch: ${e.message}")
}
}
}

View File

@@ -0,0 +1,52 @@
package app.revanced.library
import app.revanced.patcher.patch.Package
import app.revanced.patcher.patch.Patch
typealias PackageName = String
typealias Version = String
typealias Count = Int
typealias VersionMap = LinkedHashMap<Version, Count>
typealias PackageNameMap = Map<PackageName, VersionMap>
/**
* Get the count of versions for each compatible package from the set of [Patch] ordered by the most common version.
*
* @param packageNames The names of the compatible packages to include. If null, all packages will be included.
* @param countUnusedPatches Whether to count patches that are not used.
* @return A map of package names to a map of versions to their count.
*/
fun Set<Patch<*>>.mostCommonCompatibleVersions(
packageNames: Set<String>? = null,
countUnusedPatches: Boolean = false,
): PackageNameMap = buildMap {
fun filterWantedPackages(compatiblePackages: List<Package>): List<Package> {
val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages
return compatiblePackages.filter { (name, _) -> name in wantedPackages }
}
this@mostCommonCompatibleVersions.filter { it.use || countUnusedPatches }
.flatMap { it.compatiblePackages ?: emptyList() }
.let(::filterWantedPackages)
.forEach { (name, versions) ->
if (versions?.isEmpty() == true) {
return@forEach
}
val versionMap = getOrPut(name) { linkedMapOf() }
versions?.forEach { version ->
versionMap[version] = versionMap.getOrDefault(version, 0) + 1
}
}
// Sort the version maps by the most common version.
forEach { (packageName, versionMap) ->
this[packageName] =
versionMap
.asIterable()
.sortedWith(compareByDescending { it.value })
.associate { it.key to it.value } as VersionMap
}
}

View File

@@ -0,0 +1,119 @@
package app.revanced.library
import app.revanced.patcher.patch.Option
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.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.encodeStructure
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.serializer
import java.io.OutputStream
private class PatchSerializer : KSerializer<Patch<*>> {
override val descriptor = buildClassSerialDescriptor("Patch") {
element<String?>("name")
element<String?>("description")
element<Boolean>("use")
element<List<String>>("dependencies")
element<Map<PackageName, Set<VersionName>?>?>("compatiblePackages")
element("options", OptionSerializer.descriptor)
}
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
@OptIn(ExperimentalSerializationApi::class)
override fun serialize(encoder: Encoder, value: Patch<*>) {
encoder.encodeStructure(descriptor) {
encodeNullableSerializableElement(
descriptor,
0,
String.serializer(),
value.name,
)
encodeNullableSerializableElement(
descriptor,
1,
String.serializer(),
value.description,
)
encodeBooleanElement(
descriptor,
2,
value.use,
)
encodeSerializableElement(
descriptor,
3,
ListSerializer(String.serializer()),
value.dependencies.map { it.name ?: it.toString() },
)
encodeNullableSerializableElement(
descriptor,
4,
MapSerializer(String.serializer(), SetSerializer(String.serializer()).nullable),
value.compatiblePackages?.associate { (packageName, versions) -> packageName to versions },
)
encodeSerializableElement(
descriptor,
5,
SetSerializer(OptionSerializer),
value.options.values.toSet(),
)
}
}
private object OptionSerializer : KSerializer<Option<*>> {
override val descriptor = buildClassSerialDescriptor("Option") {
element<String>("key")
element<String?>("title")
element<String?>("description")
element<Boolean>("required")
// Type does not matter for serialization. Using String.
element<String>("type")
element<String?>("default")
// Map value type does not matter for serialization. Using String.
element<Map<String, String?>?>("values")
}
override fun deserialize(decoder: Decoder) = throw NotImplementedError("Deserialization is unsupported")
@OptIn(ExperimentalSerializationApi::class)
override fun serialize(encoder: Encoder, value: Option<*>) {
encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.key)
encodeNullableSerializableElement(descriptor, 1, String.serializer(), value.title)
encodeNullableSerializableElement(descriptor, 2, String.serializer(), value.description)
encodeBooleanElement(descriptor, 3, value.required)
encodeSerializableElement(descriptor, 4, String.serializer(), value.type.toString())
encodeNullableSerializableElement(descriptor, 5, serializer(value.type), value.default)
encodeNullableSerializableElement(descriptor, 6, MapSerializer(String.serializer(), serializer(value.type)), value.values)
}
}
}
}
private val patchPrettySerializer by lazy { Json { prettyPrint = true } }
private val patchSerializer by lazy { Json }
/**
* Serialize this set of [Patch] to JSON and write it to the given [outputStream].
*
* @param outputStream The output stream to write the JSON to.
* @param prettyPrint Whether to pretty print the JSON.
*/
@OptIn(ExperimentalSerializationApi::class)
fun Set<Patch<*>>.serializeTo(
outputStream: OutputStream,
prettyPrint: Boolean = true,
) = if (prettyPrint) {
patchPrettySerializer
} else {
patchSerializer
}.encodeToStream(SetSerializer(PatchSerializer()), this, outputStream)

View File

@@ -1,6 +1,6 @@
package app.revanced.library.installation.command package app.revanced.library.installation.command
import app.revanced.library.installation.installer.Utils import app.revanced.library.installation.installer.getDevice
import se.vidstige.jadb.JadbDevice import se.vidstige.jadb.JadbDevice
import se.vidstige.jadb.RemoteFile import se.vidstige.jadb.RemoteFile
import java.io.File import java.io.File
@@ -29,7 +29,7 @@ class AdbShellCommandRunner : ShellCommandRunner {
* @param deviceSerial deviceSerial The device serial. If null, the first connected device will be used. * @param deviceSerial deviceSerial The device serial. If null, the first connected device will be used.
*/ */
internal constructor(deviceSerial: String?) { internal constructor(deviceSerial: String?) {
device = Utils.getDevice(deviceSerial, logger) device = getDevice(deviceSerial, logger)
} }
override fun runCommand(command: String) = device.shellProcessBuilder(command).start().let { process -> override fun runCommand(command: String) = device.shellProcessBuilder(command).start().let { process ->

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 = Utils.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

@@ -2,21 +2,21 @@ 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.installer.Installer.Apk import app.revanced.library.installation.installer.Installer.Apk
import app.revanced.library.installation.installer.MountInstaller.NoRootPermissionException import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
/** /**
* [AdbMountInstaller] for installing and uninstalling [Apk] files with using ADB root permissions by mounting. * [AdbRootInstaller] 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. * @param deviceSerial The device serial. If null, the first connected device will be used.
* *
* @throws NoRootPermissionException If the device does not have root permission. * @throws NoRootPermissionException If the device does not have root permission.
* *
* @see MountInstaller * @see RootInstaller
* @see AdbShellCommandRunner * @see AdbShellCommandRunner
*/ */
class AdbMountInstaller( class AdbRootInstaller(
deviceSerial: String? = null, deviceSerial: String? = null,
) : MountInstaller({ AdbShellCommandRunner(deviceSerial) }) { ) : RootInstaller({ AdbShellCommandRunner(deviceSerial) }) {
init { init {
logger.fine("Connected to $deviceSerial") logger.fine("Connected to $deviceSerial")
} }

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

@@ -1,14 +1,14 @@
package app.revanced.library.installation.installer package app.revanced.library.installation.installer
/** /**
* [MountInstallation] of the apk file that is mounted to [installedApkFilePath] with root permissions. * [RootInstallation] 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 installedApkFilePath The installed apk file path or null if the apk is not installed.
* @param apkFilePath The mounting apk file path. * @param apkFilePath The mounting apk file path.
* @param mounted Whether the apk is mounted to [installedApkFilePath]. * @param mounted Whether the apk is mounted to [installedApkFilePath].
*/ */
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
class MountInstallation internal constructor( class RootInstallation internal constructor(
val installedApkFilePath: String?, val installedApkFilePath: String?,
apkFilePath: String, apkFilePath: String,
val mounted: Boolean, val mounted: Boolean,

View File

@@ -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.UMOUNT
import app.revanced.library.installation.installer.Constants.invoke import app.revanced.library.installation.installer.Constants.invoke
import app.revanced.library.installation.installer.Installer.Apk import app.revanced.library.installation.installer.Installer.Apk
import app.revanced.library.installation.installer.MountInstaller.NoRootPermissionException import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
import java.io.File import java.io.File
/** /**
* [MountInstaller] for installing and uninstalling [Apk] files using root permissions by mounting. * [RootInstaller] for installing and uninstalling [Apk] files using root permissions by mounting.
* *
* @param shellCommandRunnerSupplier A supplier for the [ShellCommandRunner] to use. * @param shellCommandRunnerSupplier A supplier for the [ShellCommandRunner] to use.
* *
* @throws NoRootPermissionException If the device does not have root permission. * @throws NoRootPermissionException If the device does not have root permission.
*/ */
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
abstract class MountInstaller internal constructor( abstract class RootInstaller internal constructor(
shellCommandRunnerSupplier: (MountInstaller) -> ShellCommandRunner, shellCommandRunnerSupplier: (RootInstaller) -> ShellCommandRunner,
) : Installer<MountInstallerResult, MountInstallation>() { ) : Installer<RootInstallerResult, RootInstallation>() {
/** /**
* The command runner used to run commands on the device. * The command runner used to run commands on the device.
@@ -49,7 +49,7 @@ abstract class MountInstaller internal constructor(
* *
* @throws PackageNameRequiredException If the [Apk] does not have a package name. * @throws PackageNameRequiredException If the [Apk] does not have a package name.
*/ */
override suspend fun install(apk: Apk): MountInstallerResult { override suspend fun install(apk: Apk): RootInstallerResult {
logger.info("Installing ${apk.packageName} by mounting") logger.info("Installing ${apk.packageName} by mounting")
val packageName = apk.packageName?.also { it.assertInstalled() } ?: throw PackageNameRequiredException() val packageName = apk.packageName?.also { it.assertInstalled() } ?: throw PackageNameRequiredException()
@@ -67,10 +67,10 @@ abstract class MountInstaller internal constructor(
DELETE(TMP_FILE_PATH)() DELETE(TMP_FILE_PATH)()
return MountInstallerResult.SUCCESS return RootInstallerResult.SUCCESS
} }
override suspend fun uninstall(packageName: String): MountInstallerResult { override suspend fun uninstall(packageName: String): RootInstallerResult {
logger.info("Uninstalling $packageName by unmounting") logger.info("Uninstalling $packageName by unmounting")
UMOUNT(packageName)() UMOUNT(packageName)()
@@ -81,16 +81,16 @@ abstract class MountInstaller internal constructor(
KILL(packageName)() KILL(packageName)()
return MountInstallerResult.SUCCESS return RootInstallerResult.SUCCESS
} }
override suspend fun getInstallation(packageName: String): MountInstallation? { override suspend fun getInstallation(packageName: String): RootInstallation? {
val patchedApkPath = MOUNTED_APK_PATH(packageName) val patchedApkPath = MOUNTED_APK_PATH(packageName)
val patchedApkExists = EXISTS(patchedApkPath)().exitCode == 0 val patchedApkExists = EXISTS(patchedApkPath)().exitCode == 0
if (patchedApkExists) return null if (patchedApkExists) return null
return MountInstallation( return RootInstallation(
INSTALLED_APK_PATH(packageName)().output.ifEmpty { null }, INSTALLED_APK_PATH(packageName)().output.ifEmpty { null },
patchedApkPath, patchedApkPath,
MOUNT_GREP(patchedApkPath)().exitCode == 0, MOUNT_GREP(patchedApkPath)().exitCode == 0,
@@ -122,13 +122,12 @@ abstract class MountInstaller 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

@@ -3,11 +3,11 @@ package app.revanced.library.installation.installer
import app.revanced.library.installation.installer.Installer.Apk import app.revanced.library.installation.installer.Installer.Apk
/** /**
* The result of installing or uninstalling an [Apk] with root permissions using [MountInstaller]. * The result of installing or uninstalling an [Apk] with root permissions using [RootInstaller].
* *
* @see MountInstaller * @see RootInstaller
*/ */
enum class MountInstallerResult { enum class RootInstallerResult {
/** /**
* The result of installing an [Apk] successfully. * The result of installing an [Apk] successfully.
*/ */

View File

@@ -0,0 +1,34 @@
package app.revanced.library.installation.installer
import se.vidstige.jadb.JadbConnection
import java.util.logging.Logger
/**
* Gets the device with the given serial.
*
* @param deviceSerial The device serial. If null, the first connected device will be used.
* @param logger The logger.
* @return The device.
* @throws DeviceNotFoundException If no device with the given serial is found.
*/
internal fun getDevice(
deviceSerial: String? = null,
logger: Logger,
) = with(JadbConnection().devices) {
if (isEmpty()) throw DeviceNotFoundException()
deviceSerial?.let {
firstOrNull { it.serial == deviceSerial } ?: throw DeviceNotFoundException(
deviceSerial,
)
} ?: first().also {
logger.warning("No device serial supplied. Using device with serial ${it.serial}")
}
}!!
class DeviceNotFoundException internal constructor(deviceSerial: String? = null) :
Exception(
deviceSerial?.let {
"The device with the ADB device serial \"$deviceSerial\" can not be found"
} ?: "No ADB device found",
)

View File

@@ -1,27 +1,19 @@
package app.revanced.library package app.revanced.library
import app.revanced.patcher.PatchSet import app.revanced.patcher.patch.*
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.intArrayPatchOption
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
internal class PatchUtilsTest { internal class MostCommonCompatibleVersionsTest {
private val patches = private val patches =
arrayOf( arrayOf(
newPatch("some.package", setOf("a")) { stringPatchOption("string", "value") }, newPatch("some.package", setOf("a")) { stringOption("string", "value") },
newPatch("some.package", setOf("a", "b"), use = false), newPatch("some.package", setOf("a", "b"), use = false),
newPatch("some.package", setOf("a", "b", "c"), use = false), newPatch("some.package", setOf("a", "b", "c"), use = false),
newPatch("some.other.package", setOf("b"), use = false), newPatch("some.other.package", setOf("b"), use = false),
newPatch("some.other.package", setOf("b", "c")) { booleanPatchOption("bool", true) }, newPatch("some.other.package", setOf("b", "c")) { booleanOption("bool", true) },
newPatch("some.other.package", setOf("b", "c", "d")), newPatch("some.other.package", setOf("b", "c", "d")),
newPatch("some.other.other.package") { intArrayPatchOption("intArray", arrayOf(1, 2, 3)) }, newPatch("some.other.other.package") { intsOption("intArray", listOf(1, 2, 3)) },
newPatch("some.other.other.package", setOf("a")), newPatch("some.other.other.package", setOf("a")),
newPatch("some.other.other.package", setOf("b")), newPatch("some.other.other.package", setOf("b")),
newPatch("some.other.other.other.package", use = false), newPatch("some.other.other.other.package", use = false),
@@ -141,38 +133,24 @@ internal class PatchUtilsTest {
assertEqualsVersion(null, patches, "other.package") assertEqualsVersion(null, patches, "other.package")
} }
@Test
fun `serializes to and deserializes from JSON string correctly`() {
val out = ByteArrayOutputStream()
PatchUtils.Json.serialize(patches, outputStream = out)
val deserialized =
PatchUtils.Json.deserialize(
ByteArrayInputStream(out.toByteArray()),
PatchUtils.Json.FullJsonPatch::class.java,
)
assert(patches.size == deserialized.size)
}
private fun assertEqualsVersions( private fun assertEqualsVersions(
expected: PackageNameMap, expected: PackageNameMap,
patches: PatchSet, patches: Set<Patch<*>>,
compatiblePackageNames: Set<String>?, compatiblePackageNames: Set<String>?,
countUnusedPatches: Boolean = false, countUnusedPatches: Boolean = false,
) = assertEquals( ) = assertEquals(
expected, expected,
PatchUtils.getMostCommonCompatibleVersions(patches, compatiblePackageNames, countUnusedPatches), patches.mostCommonCompatibleVersions(compatiblePackageNames, countUnusedPatches),
) )
private fun assertEqualsVersion( private fun assertEqualsVersion(
expected: String?, expected: String?,
patches: PatchSet, patches: Set<Patch<*>>,
compatiblePackageName: String, compatiblePackageName: String,
) { ) {
assertEquals( assertEquals(
expected, expected,
PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName)) patches.mostCommonCompatibleVersions(setOf(compatiblePackageName))
.entries.firstOrNull()?.value?.keys?.firstOrNull(), .entries.firstOrNull()?.value?.keys?.firstOrNull(),
) )
} }
@@ -181,19 +159,23 @@ internal class PatchUtilsTest {
packageName: String, packageName: String,
versions: Set<String>? = null, versions: Set<String>? = null,
use: Boolean = true, use: Boolean = true,
options: Patch<*>.() -> Unit = {}, options: PatchBuilder<*>.() -> Unit = {},
) = object : BytecodePatch( ) = bytecodePatch(
name = "test", name = "test",
compatiblePackages = setOf(CompatiblePackage(packageName, versions?.toSet())),
use = use, use = use,
) { ) {
init { if (versions == null) {
options() compatibleWith(packageName)
} else {
compatibleWith(
if (versions.isEmpty()) {
packageName()
} else {
packageName(*versions.toTypedArray())
},
)
} }
override fun execute(context: BytecodeContext) {} options()
// Needed to make the patches unique.
override fun equals(other: Any?) = false
} }
} }

View File

@@ -0,0 +1,36 @@
package app.revanced.library
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.stringOption
import kotlin.test.Test
import kotlin.test.assertEquals
class OptionsTest {
@Test
fun `serializes and deserializes`() {
val options = mapOf(
"Test patch" to mapOf("key1" to "test", "key2" to false),
)
val patch = bytecodePatch("Test patch") {
stringOption("key1")
booleanOption("key2", true)
}
val duplicatePatch = bytecodePatch("Test patch") {
stringOption("key1")
}
val unnamedPatch = bytecodePatch {
booleanOption("key1")
}
setOf(patch, duplicatePatch, unnamedPatch).setOptions(options)
assert(patch.options["key1"].value == "test")
assert(patch.options["key2"].value == false)
assertEquals(patch.options["key1"].value, duplicatePatch.options["key1"].value)
assert(unnamedPatch.options["key1"].value == null)
}
}

View File

@@ -0,0 +1,58 @@
package app.revanced.library
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 java.io.ByteArrayOutputStream
import kotlin.test.Test
import kotlin.test.assertIs
class SerializationTest {
private val testPatch = bytecodePatch("Test patch") {
compatibleWith("com.example.package"("1.0.0"))
compatibleWith("com.example.package2")
dependsOn(bytecodePatch(), bytecodePatch())
stringOption("key1", null, null, "title1", "description1")
booleanOption("key2", true, null, "title2", "description2")
floatsOption("key3", listOf(1.0f), mapOf("list" to listOf(1f)), "title3", "description3")
}
private var patches = setOf(testPatch)
@Test
fun `serializes and deserializes`() {
val serializedJson = ByteArrayOutputStream().apply { patches.serializeTo(this) }.toString()
val deserializedJson = Json.parseToJsonElement(serializedJson)
// Test patch serialization.
assertIs<JsonArray>(deserializedJson)
val deserializedPatch = deserializedJson[0].jsonObject
assert(deserializedPatch["name"]!!.jsonPrimitive.content == "Test patch")
assert(deserializedPatch["compatiblePackages"]!!.jsonObject.size == 2) {
"The patch should be compatible with two packages."
}
assert(deserializedPatch["dependencies"]!!.jsonArray.size == 2) {
"Even though the dependencies are named the same, they are different objects."
}
// Test option serialization.
val options = deserializedPatch["options"]!!.jsonArray
assert(options.size == 3) { "The patch should have three options." }
assert(options[0].jsonObject["title"]!!.jsonPrimitive.content == "title1")
assert(options[0].jsonObject["default"]!!.jsonPrimitive.contentOrNull == null)
assert(options[1].jsonObject["default"]!!.jsonPrimitive.boolean)
assert(options[2].jsonObject["values"]!!.jsonObject["list"]!!.jsonArray[0].jsonPrimitive.float == 1f)
}
}