Compare commits

...

43 Commits

Author SHA1 Message Date
semantic-release-bot
dfd535c201 chore(release): 3.1.1 [skip ci]
## [3.1.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.0...v3.1.1) (2023-09-09)

### Bug Fixes

* Create options if it does not exist when updating them ([ca809f0](ca809f0948))
2023-09-09 15:26:51 +00:00
oSumAtrIX
2b6051e7f3 chore: merge branch dev to main (#259) 2023-09-09 17:25:04 +02:00
semantic-release-bot
0304988733 chore(release): 3.1.1-dev.1 [skip ci]
## [3.1.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.0...v3.1.1-dev.1) (2023-09-03)

### Bug Fixes

* Create options if it does not exist when updating them ([ca809f0](ca809f0948))
2023-09-03 20:42:58 +00:00
oSumAtrIX
ca809f0948 fix: Create options if it does not exist when updating them
Previously, the file could not be read to be updated. If the file does not exist, simply serialize the options to the file.
2023-09-03 22:41:20 +02:00
semantic-release-bot
5d50d1a622 chore(release): 3.1.0 [skip ci]
# [3.1.0](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.1.0) (2023-08-31)

### Bug Fixes

* check for package compatibility at first ([9fe5a0b](9fe5a0b6d9))
* do not filter explicitly included patches ([a3d8f00](a3d8f004ec))
* format patches input ([bbb1a63](bbb1a63abd))

### Features

* Simplify command description ([3b3f7c7](3b3f7c7a7a))
2023-08-31 20:32:08 +00:00
oSumAtrIX
83c28d9f71 chore: merge branch dev to main (#255) 2023-08-31 22:30:35 +02:00
semantic-release-bot
93cbcc28aa chore(release): 3.1.0-dev.1 [skip ci]
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.2...v3.1.0-dev.1) (2023-08-28)

### Bug Fixes

* format patches input ([bbb1a63](bbb1a63abd))

### Features

* Simplify command description ([3b3f7c7](3b3f7c7a7a))
2023-08-28 18:18:56 +00:00
oSumAtrIX
3b3f7c7a7a feat: Simplify command description 2023-08-28 20:17:12 +02:00
oSumAtrIX
bbb1a63abd fix: format patches input
Previously you could not use the original patches names because they were formatted but not the input
2023-08-28 20:16:00 +02:00
semantic-release-bot
f7fcbc55bb chore(release): 3.0.2-dev.2 [skip ci]
## [3.0.2-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.1...v3.0.2-dev.2) (2023-08-28)

### Bug Fixes

* check for package compatibility at first ([9fe5a0b](9fe5a0b6d9))
2023-08-28 16:38:55 +00:00
oSumAtrIX
9fe5a0b6d9 fix: check for package compatibility at first 2023-08-28 18:37:11 +02:00
semantic-release-bot
4f2d2568d3 chore(release): 3.0.2-dev.1 [skip ci]
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.0.2-dev.1) (2023-08-28)

### Bug Fixes

* do not filter explicitly included patches ([a3d8f00](a3d8f004ec))
2023-08-28 15:59:00 +00:00
semantic-release-bot
a3d8f004ec fix: do not filter explicitly included patches 2023-08-28 17:56:47 +02:00
semantic-release-bot
41ffc99ad0 chore(release): 3.0.1 [skip ci]
## [3.0.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1) (2023-08-28)
2023-08-28 13:23:57 +00:00
oSumAtrIX
1121376018 chore: merge branch dev to main (#254) 2023-08-28 15:21:55 +02:00
semantic-release-bot
48e1689223 chore(release): 3.0.1-dev.1 [skip ci]
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1-dev.1) (2023-08-28)
2023-08-28 13:18:00 +00:00
oSumAtrIX
7580f5c2fc build(Needs bump): bump dependencies
This fixes an issue with flagging the resource table with sparse incorrectly.
2023-08-28 15:14:53 +02:00
semantic-release-bot
dd6c1392eb chore(release): 3.0.0 [skip ci]
# [3.0.0](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v3.0.0) (2023-08-26)

### Bug Fixes

* also delete temporary files when uninstalling ([52c3be2](52c3be23f2))
* delete temporary files after root installation ([a3d8705](a3d8705e89))
* do not delete output file ([0f3e090](0f3e090418))
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](a9c2a5f096))
* filtration of patches malfunctioning ([2d5a7fd](2d5a7fdf1e))
* fix running commands not running ([2c7fcaf](2c7fcaf4ad))
* only check once for patch options ([11c3a6c](11c3a6cfd4))
* print original instead of kebab cased names ([5eaad33](5eaad33dc1))
* print stack trace when a patch failed ([924c1f8](924c1f80ec))
* specify correct class containing entry-point ([1fcc591](1fcc591222))
* use correct option name ([f8972ea](f8972eac3e))

* refactor!: restructure code ([07da528](07da528ce2))

### Features

* add install command ([0350b7f](0350b7f1a2))
* add options command ([9edbbf3](9edbbf3163))
* Check for missing integrations ([c93186f](c93186fb97))
* Improve command line argument descriptions ([f9cf7d2](f9cf7d21b7))
* properly make use of logging facade ([41898d7](41898d7547))
* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](7174364ef8))
* use better logging text ([b0e748d](b0e748daff))
* use friendly descriptions ([3dd875d](3dd875d14c))
* use separate command to list patches ([b74213f](b74213f66e))
* use separate command to patch ([32da961](32da961d57))
* use separate command to uninstall ([c0cc909](c0cc909626))
* use simpler log ([ba758f0](ba758f00f4))

### BREAKING CHANGES

* This introduces major changes to how ReVanced CLI is used from the command line.
2023-08-26 23:23:34 +00:00
oSumAtrIX
2a3dbafd17 chore: merge branch dev to main (#236) 2023-08-27 01:21:45 +02:00
semantic-release-bot
9e39a6f8e4 chore(release): 3.0.0-dev.10 [skip ci]
# [3.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.9...v3.0.0-dev.10) (2023-08-25)

### Bug Fixes

* filtration of patches malfunctioning ([2d5a7fd](2d5a7fdf1e))
2023-08-25 21:48:59 +00:00
oSumAtrIX
2d5a7fdf1e fix: filtration of patches malfunctioning
Apparently, you were not able to include patches explicitly
2023-08-25 23:47:18 +02:00
semantic-release-bot
be5d812dff chore(release): 3.0.0-dev.9 [skip ci]
# [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25)

### Features

* Check for missing integrations ([c93186f](c93186fb97))
2023-08-25 00:28:57 +00:00
oSumAtrIX
c93186fb97 feat: Check for missing integrations
Check, if the integrations file exists at first.
2023-08-25 02:26:38 +02:00
semantic-release-bot
3a198052bb chore(release): 3.0.0-dev.8 [skip ci]
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24)

### Bug Fixes

* do not delete output file ([0f3e090](0f3e090418))
2023-08-24 23:32:29 +00:00
oSumAtrIX
0f3e090418 fix: do not delete output file
This fixes the output file to be deleted when the option `--purge` was used.
2023-08-25 01:30:28 +02:00
semantic-release-bot
6aed946183 chore(release): 3.0.0-dev.7 [skip ci]
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24)

### Bug Fixes

* print stack trace when a patch failed ([924c1f8](924c1f80ec))
2023-08-24 21:47:17 +00:00
oSumAtrIX
924c1f80ec fix: print stack trace when a patch failed 2023-08-24 23:45:10 +02:00
semantic-release-bot
8dd709b6ef chore(release): 3.0.0-dev.6 [skip ci]
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24)
2023-08-24 16:11:51 +00:00
oSumAtrIX
139e7facac build(Needs bump): depend on build task when publishing
This fixes the issue that no builds are generated
2023-08-24 18:09:35 +02:00
semantic-release-bot
1d26e572f7 chore(release): 3.0.0-dev.5 [skip ci]
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24)

### Bug Fixes

* also delete temporary files when uninstalling ([52c3be2](52c3be23f2))
* delete temporary files after root installation ([a3d8705](a3d8705e89))
* fix running commands not running ([2c7fcaf](2c7fcaf4ad))
* only check once for patch options ([11c3a6c](11c3a6cfd4))

### Features

* add install command ([0350b7f](0350b7f1a2))
* use friendly descriptions ([3dd875d](3dd875d14c))
2023-08-24 15:53:09 +00:00
oSumAtrIX
2c7fcaf4ad fix: fix running commands not running 2023-08-24 17:51:31 +02:00
oSumAtrIX
52c3be23f2 fix: also delete temporary files when uninstalling 2023-08-24 16:54:08 +02:00
oSumAtrIX
3dd875d14c feat: use friendly descriptions 2023-08-24 16:53:31 +02:00
oSumAtrIX
0350b7f1a2 feat: add install command
This introduces a separate utility subcommand.
2023-08-24 16:50:10 +02:00
oSumAtrIX
a3d8705e89 fix: delete temporary files after root installation 2023-08-24 16:24:33 +02:00
oSumAtrIX
a536c9f815 refactor: use better identifiers 2023-08-24 16:24:18 +02:00
oSumAtrIX
11c3a6cfd4 fix: only check once for patch options
This prevents checking for the same patches options multiple times when it is already determined to not have any options
2023-08-24 15:52:12 +02:00
oSumAtrIX
a5851f0c1a build: clean after building 2023-08-24 15:35:27 +02:00
oSumAtrIX
963ae3a5fa docs: add missing inline docs 2023-08-24 15:35:27 +02:00
semantic-release-bot
93f338a731 chore(release): 3.0.0-dev.4 [skip ci]
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24)

### Features

* properly make use of logging facade ([41898d7](41898d7547))
2023-08-24 02:56:08 +00:00
oSumAtrIX
7dcf15e03b build: bump dependencies 2023-08-24 04:52:53 +02:00
oSumAtrIX
41898d7547 feat: properly make use of logging facade 2023-08-24 04:41:44 +02:00
oSumAtrIX
45dd15f679 build: do not use a daemon when building
This decreases build times
2023-08-23 16:43:57 +02:00
23 changed files with 508 additions and 385 deletions

View File

@@ -33,10 +33,11 @@ jobs:
build
node_modules
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
- name: Build with Gradle
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build
# Cleaning is necessary to avoid uploading two identical artifacts with different versions
run: ./gradlew clean --no-daemon
- name: Setup semantic-release
run: npm install
- name: Release

View File

@@ -1,3 +1,155 @@
## [3.1.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.0...v3.1.1) (2023-09-09)
### Bug Fixes
* Create options if it does not exist when updating them ([ca809f0](https://github.com/ReVanced/revanced-cli/commit/ca809f0948379e3a825f24d7a49aba8b6b8767d1))
## [3.1.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.1.0...v3.1.1-dev.1) (2023-09-03)
### Bug Fixes
* Create options if it does not exist when updating them ([ca809f0](https://github.com/ReVanced/revanced-cli/commit/ca809f0948379e3a825f24d7a49aba8b6b8767d1))
# [3.1.0](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.1.0) (2023-08-31)
### Bug Fixes
* check for package compatibility at first ([9fe5a0b](https://github.com/ReVanced/revanced-cli/commit/9fe5a0b6d93304f630436ed0e954723d9a27b0f6))
* do not filter explicitly included patches ([a3d8f00](https://github.com/ReVanced/revanced-cli/commit/a3d8f004ec405f696d99d96c74ca41b573ecf425))
* format patches input ([bbb1a63](https://github.com/ReVanced/revanced-cli/commit/bbb1a63abd80dcbecdcf362158c0429cf3e6318f))
### Features
* Simplify command description ([3b3f7c7](https://github.com/ReVanced/revanced-cli/commit/3b3f7c7a7a7b2795e3d1fad776f6b457f2e68c7b))
# [3.1.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.2...v3.1.0-dev.1) (2023-08-28)
### Bug Fixes
* format patches input ([bbb1a63](https://github.com/ReVanced/revanced-cli/commit/bbb1a63abd80dcbecdcf362158c0429cf3e6318f))
### Features
* Simplify command description ([3b3f7c7](https://github.com/ReVanced/revanced-cli/commit/3b3f7c7a7a7b2795e3d1fad776f6b457f2e68c7b))
## [3.0.2-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.2-dev.1...v3.0.2-dev.2) (2023-08-28)
### Bug Fixes
* check for package compatibility at first ([9fe5a0b](https://github.com/ReVanced/revanced-cli/commit/9fe5a0b6d93304f630436ed0e954723d9a27b0f6))
## [3.0.2-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.1...v3.0.2-dev.1) (2023-08-28)
### Bug Fixes
* do not filter explicitly included patches ([a3d8f00](https://github.com/ReVanced/revanced-cli/commit/a3d8f004ec405f696d99d96c74ca41b573ecf425))
## [3.0.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1) (2023-08-28)
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1-dev.1) (2023-08-28)
# [3.0.0](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v3.0.0) (2023-08-26)
### Bug Fixes
* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8))
* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633))
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3))
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69))
* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5))
* print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e))
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
* specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c))
* use correct option name ([f8972ea](https://github.com/ReVanced/revanced-cli/commit/f8972eac3e5ee0a4a186c12cbe711925656d657b))
* refactor!: restructure code ([07da528](https://github.com/ReVanced/revanced-cli/commit/07da528ce2223582f84bf64d2fec69714c647ddc))
### Features
* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
* add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6))
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
* Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83))
* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634))
* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](https://github.com/ReVanced/revanced-cli/commit/7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c))
* use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131))
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
* use separate command to list patches ([b74213f](https://github.com/ReVanced/revanced-cli/commit/b74213f66e0d04d3a0ae6197d069631388e06580))
* use separate command to patch ([32da961](https://github.com/ReVanced/revanced-cli/commit/32da961d57537e99b39fd92b625a1c73f8314bc6))
* use separate command to uninstall ([c0cc909](https://github.com/ReVanced/revanced-cli/commit/c0cc90962646cfffd5e2730ae556423271a7990b))
* use simpler log ([ba758f0](https://github.com/ReVanced/revanced-cli/commit/ba758f00f4ce18791439b7e72fe1ad2e7f11f8af))
### BREAKING CHANGES
* This introduces major changes to how ReVanced CLI is used from the command line.
# [3.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.9...v3.0.0-dev.10) (2023-08-25)
### Bug Fixes
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
# [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25)
### Features
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24)
### Bug Fixes
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24)
### Bug Fixes
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24)
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24)
### Bug Fixes
* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8))
* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633))
* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69))
* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5))
### Features
* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24)
### Features
* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634))
# [3.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-08-23)
# [3.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-08-23)

View File

@@ -52,5 +52,6 @@ tasks {
register<DefaultTask>("publish") {
group = "publish"
description = "Dummy task"
dependsOn(build)
}
}

View File

@@ -86,12 +86,26 @@ Learn how to ReVanced CLI.
> **Note**: Some patches may require integrations
such as [ReVanced Integrations](https://github.com/revanced/revanced-integrations).
Supply them with the option `-m`. If any patches accepted by ReVanced Patcher require ReVanced Integrations,
Supply them with the option `--merge`. If any patches accepted by ReVanced Patcher require ReVanced Integrations,
they will be merged into the APK file automatically.
- ### 🗑️ Uninstall a patched APK file
```bash
java -jar revanced-cli.jar uninstall \
java -jar revanced-cli.jar utility uninstall \
--package-name <package-name> \
<device-serial>
```
> **Note**: You can unmount an APK file
with the option `--unmount`.
- ### ⚙️ Manually install an APK file
```bash
java -jar revanced-cli.jar utility install \
-a input.apk \
<device-serial>
```
> **Note**: You can mount an APK file
by supplying the package name of the app to mount the supplied APK file to over the option `--mount`.

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 3.0.0-dev.3
version = 3.1.1

View File

@@ -1,6 +1,6 @@
[versions]
shadow = "8.1.1"
apksig = "8.1.0"
apksig = "8.1.1"
bcpkix-jdk15on = "1.70"
jackson-module-kotlin = "2.14.3"
jadb = "2531a28109"
@@ -8,7 +8,7 @@ kotlin-reflect = "1.9.0"
kotlin-test = "1.8.20-RC"
kotlinx-coroutines-core = "1.7.1"
picocli = "4.7.3"
revanced-patcher = "14.0.0"
revanced-patcher = "14.2.1"
[libraries]
apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" }

View File

@@ -11,43 +11,41 @@ import app.revanced.patcher.patch.PatchOption
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
import java.util.logging.Logger
@Command(name = "list-patches", description = ["List patches from supplied patch bundles"])
internal object ListPatchesCommand : Runnable {
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
@Parameters(
description = ["Paths to patch bundles"],
arity = "1..*"
description = ["Paths to patch bundles"], arity = "1..*"
)
lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Array<File>
@Option(
names = ["-d", "--with-descriptions"],
description = ["List their descriptions"],
showDefaultValue = ALWAYS
names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS
)
var withDescriptions: Boolean = true
private var withDescriptions: Boolean = true
@Option(
names = ["-p", "--with-packages"],
description = ["List the packages the patches are compatible with"],
showDefaultValue = ALWAYS
)
var withPackages: Boolean = false
private var withPackages: Boolean = false
@Option(
names = ["-v", "--with-versions"],
description = ["List the versions of the packages the patches are compatible with"],
description = ["List the versions of the apps the patches are compatible with"],
showDefaultValue = ALWAYS
)
var withVersions: Boolean = false
private var withVersions: Boolean = false
@Option(
names = ["-o", "--with-options"],
description = ["List the options of the patches"],
showDefaultValue = ALWAYS
names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS
)
var withOptions: Boolean = false
private var withOptions: Boolean = false
override fun run() {
fun Package.buildString() = buildString {
@@ -55,8 +53,7 @@ internal object ListPatchesCommand : Runnable {
appendLine("Package name: $name")
appendLine("Compatible versions:")
append(versions.joinToString("\n") { version -> version }.prependIndent("\t"))
} else
append("Package name: $name")
} else append("Package name: $name")
}
fun PatchOption<*>.buildString() = buildString {

View File

@@ -1,21 +1,48 @@
package app.revanced.cli.command
import app.revanced.cli.logging.impl.DefaultCliLogger
import app.revanced.cli.command.utility.UtilityCommand
import app.revanced.patcher.patch.PatchClass
import picocli.CommandLine
import picocli.CommandLine.Command
import picocli.CommandLine.IVersionProvider
import java.util.*
import java.util.logging.*
fun main(args: Array<String>) {
System.setProperty("java.util.logging.SimpleFormatter.format", "%4\$s: %5\$s %n")
Logger.getLogger("").apply {
handlers.forEach {
it.close()
removeHandler(it)
}
object : Handler() {
override fun publish(record: LogRecord) = formatter.format(record).toByteArray().let {
if (record.level.intValue() > Level.INFO.intValue())
System.err.write(it)
else
System.out.write(it)
}
override fun flush() {
System.out.flush()
System.err.flush()
}
override fun close() = flush()
}.also {
it.level = Level.ALL
it.formatter = SimpleFormatter()
}.let(::addHandler)
}
CommandLine(MainCommand).execute(*args)
}
internal typealias PatchList = List<PatchClass>
internal val logger = DefaultCliLogger()
object CLIVersionProvider : IVersionProvider {
private object CLIVersionProvider : IVersionProvider {
override fun getVersion(): Array<String> {
Properties().apply {
load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties"))
@@ -33,8 +60,8 @@ object CLIVersionProvider : IVersionProvider {
subcommands = [
ListPatchesCommand::class,
PatchCommand::class,
UninstallCommand::class,
OptionsCommand::class,
UtilityCommand::class,
]
)
internal object MainCommand
private object MainCommand

View File

@@ -6,45 +6,41 @@ import app.revanced.utils.Options.setOptions
import picocli.CommandLine
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
import java.util.logging.Logger
@CommandLine.Command(
name = "options",
description = ["Generate options file from patches"],
)
internal object OptionsCommand : Runnable {
private val logger = Logger.getLogger(OptionsCommand::class.java.name)
@CommandLine.Parameters(
description = ["Paths to patch bundles"],
arity = "1..*"
description = ["Paths to patch bundles"], arity = "1..*"
)
lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Array<File>
@CommandLine.Option(
names = ["-p", "--path"],
description = ["Path to patch options JSON file"],
showDefaultValue = ALWAYS
names = ["-p", "--path"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
)
var path: File = File("options.json")
private var filePath: File = File("options.json")
@CommandLine.Option(
names = ["-o", "--overwrite"],
description = ["Overwrite existing options file"],
showDefaultValue = ALWAYS
names = ["-o", "--overwrite"], description = ["Overwrite existing options file"], showDefaultValue = ALWAYS
)
var overwrite: Boolean = false
private var overwrite: Boolean = false
@CommandLine.Option(
names = ["-u", "--update"],
description = ["Update existing options by adding missing and removing non-existent options"],
showDefaultValue = ALWAYS
)
var update: Boolean = false
private var update: Boolean = false
override fun run() = if (!path.exists() || overwrite)
with(PatchBundleLoader.Jar(*patchBundles)) {
if (update) setOptions(path, logger)
override fun run() = if (!filePath.exists() || overwrite) with(PatchBundleLoader.Jar(*patchBundles)) {
if (update && filePath.exists()) setOptions(filePath)
Options.serialize(this, prettyPrint = true)
.let(path::writeText)
}
else logger.error("Options file already exists, use --override to override it")
Options.serialize(this, prettyPrint = true).let(filePath::writeText)
}
else logger.severe("Options file already exists, use --override to override it")
}

View File

@@ -1,6 +1,5 @@
package app.revanced.cli.command
import app.revanced.cli.patcher.logging.impl.PatcherLogger
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherOptions
@@ -20,85 +19,75 @@ import kotlinx.coroutines.runBlocking
import picocli.CommandLine
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.util.logging.Logger
@CommandLine.Command(
name = "patch",
description = ["Patch the supplied APK file with the supplied patches and integrations"]
name = "patch", description = ["Patch an APK file"]
)
internal object PatchCommand: Runnable {
internal object PatchCommand : Runnable {
private val logger = Logger.getLogger(PatchCommand::class.java.name)
@CommandLine.Parameters(
description = ["APK file to be patched"],
arity = "1..1"
description = ["APK file to be patched"], arity = "1..1"
)
lateinit var apk: File
private lateinit var apk: File
@CommandLine.Option(
names = ["-b", "--patch-bundle"],
description = ["One or more bundles of patches"],
required = true
names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true
)
var patchBundles = emptyList<File>()
private var patchBundles = emptyList<File>()
@CommandLine.Option(
names = ["-m", "--merge"],
description = ["One or more DEX files or containers to merge into the APK"]
names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]
)
var integrations = listOf<File>()
private var integrations = listOf<File>()
@CommandLine.Option(
names = ["-i", "--include"],
description = ["List of patches to include"]
names = ["-i", "--include"], description = ["List of patches to include"]
)
var includedPatches = arrayOf<String>()
private var includedPatches = arrayOf<String>()
@CommandLine.Option(
names = ["-e", "--exclude"],
description = ["List of patches to exclude"]
names = ["-e", "--exclude"], description = ["List of patches to exclude"]
)
var excludedPatches = arrayOf<String>()
private var excludedPatches = arrayOf<String>()
@CommandLine.Option(
names = ["--options"],
description = ["Path to patch options JSON file"],
showDefaultValue = ALWAYS
names = ["--options"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
)
var optionsFile: File = File("options.json")
private var optionsFile: File = File("options.json")
@CommandLine.Option(
names = ["--exclusive"],
description = ["Only include patches that are explicitly specified to be included"],
showDefaultValue = ALWAYS
)
var exclusive = false
private var exclusive = false
@CommandLine.Option(
names = ["--experimental"],
description = ["Ignore patches incompatibility to versions"],
names = ["-f","--force"],
description = ["Force inclusion of patches that are incompatible with the supplied APK file's version"],
showDefaultValue = ALWAYS
)
var experimental: Boolean = false
private var force: Boolean = false
@CommandLine.Option(
names = ["-o", "--out"],
description = ["Path to save the patched APK file to"],
required = true
names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true
)
lateinit var outputFilePath: File
private lateinit var outputFilePath: File
@CommandLine.Option(
names = ["-d", "--device-serial"],
description = ["ADB device serial to install to"],
showDefaultValue = ALWAYS
names = ["-d", "--device-serial"], description = ["ADB device serial to install to"], showDefaultValue = ALWAYS
)
var deviceSerial: String? = null
private var deviceSerial: String? = null
@CommandLine.Option(
names = ["--mount"],
description = ["Install by mounting the patched package"],
showDefaultValue = ALWAYS
names = ["--mount"], description = ["Install by mounting the patched APK file"], showDefaultValue = ALWAYS
)
var mount: Boolean = false
private var mount: Boolean = false
@CommandLine.Option(
names = ["--common-name"],
@@ -106,53 +95,57 @@ internal object PatchCommand: Runnable {
showDefaultValue = ALWAYS
)
var commonName = "ReVanced"
private var commonName = "ReVanced"
@CommandLine.Option(
names = ["--keystore"],
description = ["Path to the keystore to sign the patched APK file with"]
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]
)
var keystorePath: String? = null
private var keystorePath: String? = null
@CommandLine.Option(
names = ["--password"],
description = ["The password of the keystore to sign the patched APK file with"]
names = ["--password"], description = ["The password of the keystore to sign the patched APK file with"]
)
var password = "ReVanced"
private var password = "ReVanced"
@CommandLine.Option(
names = ["-r", "--resource-cache"],
description = ["Path to temporary resource cache directory"],
showDefaultValue = ALWAYS
)
var resourceCachePath = File("revanced-resource-cache")
private var resourceCachePath = File("revanced-resource-cache")
@CommandLine.Option(
names = ["--custom-aapt2-binary"],
description = ["Path to a custom AAPT binary to compile resources with"]
names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with"]
)
var aaptBinaryPath = File("")
private var aaptBinaryPath = File("")
@CommandLine.Option(
names = ["-p", "--purge"],
description = ["Purge the temporary resource cache directory after patching"],
showDefaultValue = ALWAYS
)
var purge: Boolean = false
private var purge: Boolean = false
override fun run() {
// region Prepare
if (!apk.exists()) {
logger.error("Input file ${apk.name} does not exist")
logger.severe("APK file ${apk.name} does not exist")
return
}
integrations.filter { !it.exists() }.let {
if (it.isEmpty()) return@let
it.forEach { integration ->
logger.severe("Integration file ${integration.name} does not exist")
}
return
}
val adbManager = deviceSerial?.let { serial ->
if (mount) AdbManager.RootAdbManager(serial, logger) else AdbManager.UserAdbManager(
serial,
logger
)
if (mount) AdbManager.RootAdbManager(serial)
else AdbManager.UserAdbManager(serial)
}
// endregion
@@ -167,7 +160,7 @@ internal object PatchCommand: Runnable {
logger.info("Setting patch options")
optionsFile.let {
if (it.exists()) patches.setOptions(it, logger)
if (it.exists()) patches.setOptions(it)
else Options.serialize(patches, prettyPrint = true).let(it::writeText)
}
@@ -181,7 +174,6 @@ internal object PatchCommand: Runnable {
resourceCachePath,
aaptBinaryPath.path,
resourceCachePath.absolutePath,
PatcherLogger
)
)
@@ -193,7 +185,10 @@ internal object PatchCommand: Runnable {
runBlocking {
apply(false).collect { patchResult ->
patchResult.exception?.let {
logger.error("${patchResult.patchName} failed:\n${patchResult.exception}")
StringWriter().use { writer ->
it.printStackTrace(PrintWriter(writer))
logger.severe("${patchResult.patchName} failed: $writer")
}
} ?: logger.info("${patchResult.patchName} succeeded")
}
}
@@ -207,8 +202,7 @@ internal object PatchCommand: Runnable {
val alignAndSignedFile = sign(
apk.newAlignedFile(
result,
resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk")
result, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk")
)
)
@@ -219,7 +213,6 @@ internal object PatchCommand: Runnable {
if (purge) {
logger.info("Purging temporary files")
outputFilePath.delete()
purge(resourceCachePath)
}
@@ -232,104 +225,59 @@ internal object PatchCommand: Runnable {
* - [includedPatches] (explicitly included)
* - [excludedPatches] (explicitly excluded)
* - [exclusive] (only include patches that are explicitly included)
* - [experimental] (ignore patches incompatibility to versions)
* - package name and version of the input APK file (if [experimental] is false)
* - [force] (ignore patches incompatibility to versions)
* - Package name and version of the input APK file (if [force] is false)
*
* @param patches The patches to filter.
* @return The filtered patches.
*/
private fun Patcher.filterPatchSelection(patches: PatchList) = buildList {
// TODO: Remove this eventually because
// patches named "patch-name" and "patch name" will conflict.
fun String.format() = lowercase().replace(" ", "-")
val formattedExcludedPatches = excludedPatches.map { it.format() }
val formattedIncludedPatches = includedPatches.map { it.format() }
val packageName = context.packageMetadata.packageName
val packageVersion = context.packageMetadata.packageVersion
patches.forEach patch@{ patch ->
val formattedPatchName = patch.patchName.lowercase().replace(" ", "-")
val formattedPatchName = patch.patchName.format()
/**
* Check if the patch is explicitly excluded.
*
* Cases:
* 1. -e patch.name
* 2. -i patch.name -e patch.name
*/
/**
* Check if the patch is explicitly excluded.
*
* Cases:
* 1. -e patch.name
* 2. -i patch.name -e patch.name
*/
val excluded = excludedPatches.contains(formattedPatchName)
if (excluded) return@patch logger.info("Excluding ${patch.patchName}")
/**
* Check if the patch is constrained to packages.
*/
/**
* Check if the patch is constrained to packages.
*/
val explicitlyExcluded = formattedExcludedPatches.contains(formattedPatchName)
if (explicitlyExcluded) return@patch logger.info("Excluding ${patch.patchName}")
// Make sure the patch is compatible with the supplied APK files package name and version.
patch.compatiblePackages?.let { packages ->
packages.singleOrNull { it.name == packageName }?.let { `package` ->
/**
* Check if the package version matches.
* If experimental is true, version matching will be skipped.
*/
/**
* Check if the package version matches.
* If experimental is true, version matching will be skipped.
*/
val matchesVersion = experimental || `package`.versions.let {
val matchesVersion = force || `package`.versions.let {
it.isEmpty() || it.any { version -> version == packageVersion }
}
if (!matchesVersion) return@patch logger.warn(
"${patch.patchName} is incompatible with version $packageVersion. " +
"This patch is only compatible with version " +
packages.joinToString(";") { pkg ->
"${pkg.name}: ${pkg.versions.joinToString(", ")}"
}
if (!matchesVersion) return@patch logger.warning(
"${patch.patchName} is incompatible with version $packageVersion. "
+ "This patch is only compatible with version "
+ packages.joinToString(";") { pkg ->
"${pkg.name}: ${pkg.versions.joinToString(", ")}"
}
)
} ?: return@patch logger.trace(
"${patch.patchName} is incompatible with $packageName. " +
"This patch is only compatible with " +
packages.joinToString(", ") { `package` -> `package`.name }
)
} ?: return@patch logger.fine("${patch.patchName} is incompatible with $packageName. "
+ "This patch is only compatible with "
+ packages.joinToString(", ") { `package` -> `package`.name })
return@let
} ?: logger.trace("$formattedPatchName: No constraint on packages.")
} ?: logger.fine("$formattedPatchName: No constraint on packages.")
/**
* Check if the patch is explicitly included.
*
* Cases:
* 1. --exclusive
* 2. --exclusive -i patch.name
*/
// If the patch is implicitly included, it will be only included if [exclusive] is false.
val implicitlyIncluded = !exclusive && patch.include
// If the patch is explicitly included, it will be included even if [exclusive] is false.
val explicitlyIncluded = formattedIncludedPatches.contains(formattedPatchName)
/**
* Check if the patch is explicitly included.
*
* Cases:
* 1. --exclusive
* 2. --exclusive -i patch.name
*/
val explicitlyIncluded = includedPatches.contains(formattedPatchName)
val implicitlyIncluded = !exclusive && patch.include // Case 3.
val exclusivelyIncluded = exclusive && explicitlyIncluded // Case 2.
val included = implicitlyIncluded || exclusivelyIncluded
val included = implicitlyIncluded || explicitlyIncluded
if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1.
logger.trace("Adding $formattedPatchName")
logger.fine("Adding $formattedPatchName")
add(patch)
}
@@ -342,8 +290,7 @@ internal object PatchCommand: Runnable {
* @param outputFile The file to save the aligned APK to.
*/
private fun File.newAlignedFile(
result: PatcherResult,
outputFile: File
result: PatcherResult, outputFile: File
): File {
logger.info("Aligning $name")
@@ -352,23 +299,20 @@ internal object PatchCommand: Runnable {
ZipFile(outputFile).use { file ->
result.dexFiles.forEach {
file.addEntryCompressData(
ZipEntry.createWithName(it.name),
it.stream.readBytes()
ZipEntry.createWithName(it.name), it.stream.readBytes()
)
}
result.resourceFile?.let {
file.copyEntriesFromFileAligned(
ZipFile(it),
ZipAligner::getEntryAlignment
ZipFile(it), ZipAligner::getEntryAlignment
)
}
// TODO: Do not compress result.doNotCompress
file.copyEntriesFromFileAligned(
ZipFile(this),
ZipAligner::getEntryAlignment
ZipFile(this), ZipAligner::getEntryAlignment
)
}
@@ -381,32 +325,25 @@ internal object PatchCommand: Runnable {
* @param inputFile The APK file to sign.
* @return The signed APK file. If [mount] is true, the input file will be returned.
*/
private fun sign(inputFile: File) = if (mount)
inputFile
private fun sign(inputFile: File) = if (mount) inputFile
else {
logger.info("Signing ${inputFile.name}")
val keyStoreFilePath = keystorePath ?: outputFilePath
.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath
val keyStoreFilePath = keystorePath
?: outputFilePath.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath
val options = SigningOptions(
commonName,
password,
keyStoreFilePath
commonName, password, keyStoreFilePath
)
ApkSigner(options)
.signApk(
inputFile,
resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk")
ApkSigner(options).signApk(
inputFile, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk")
)
}
private fun purge(resourceCachePath: File) {
val result = if (resourceCachePath.deleteRecursively())
"Purged resource cache directory"
else
"Failed to purge resource cache directory"
val result = if (resourceCachePath.deleteRecursively()) "Purged resource cache directory"
else "Failed to purge resource cache directory"
logger.info(result)
}
}

View File

@@ -1,44 +0,0 @@
package app.revanced.cli.command
import app.revanced.utils.adb.AdbManager
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
@Command(
name = "uninstall",
description = ["Uninstall a patched APK file from the devices with the supplied ADB device serials"]
)
internal object UninstallCommand : Runnable {
@Parameters(
description = ["ADB device serials"],
arity = "1..*"
)
lateinit var deviceSerials: Array<String>
@Option(
names = ["-p", "--package-name"],
description = ["Package name to uninstall"],
required = true
)
lateinit var packageName: String
@Option(
names = ["-u", "--unmount"],
description = ["Uninstall by unmounting the patched package"],
showDefaultValue = ALWAYS
)
var unmount: Boolean = false
override fun run() = try {
deviceSerials.forEach {deviceSerial ->
if (unmount) {
AdbManager.RootAdbManager(deviceSerial, logger)
} else {
AdbManager.UserAdbManager(deviceSerial, logger)
}.uninstall(packageName)
}
} catch (e: AdbManager.DeviceNotFoundException) {
logger.error(e.toString())
}
}

View File

@@ -0,0 +1,42 @@
package app.revanced.cli.command.utility
import app.revanced.utils.adb.AdbManager
import picocli.CommandLine.*
import java.io.File
import java.util.logging.Logger
@Command(
name = "install", description = ["Install an APK file to devices with the supplied ADB device serials"]
)
internal object InstallCommand : Runnable {
private val logger = Logger.getLogger(InstallCommand::class.java.name)
@Parameters(
description = ["ADB device serials"], arity = "1..*"
)
private lateinit var deviceSerials: Array<String>
@Option(
names = ["-a", "--apk"], description = ["APK file to be installed"], required = true
)
private lateinit var apk: File
@Option(
names = ["-m", "--mount"],
description = ["Mount the supplied APK file over the app with the supplied package name"],
)
private var packageName: String? = null
override fun run() = try {
deviceSerials.forEach { deviceSerial ->
if (packageName != null) {
AdbManager.RootAdbManager(deviceSerial)
} else {
AdbManager.UserAdbManager(deviceSerial)
}.install(AdbManager.Apk(apk, packageName))
}
} catch (e: AdbManager.DeviceNotFoundException) {
logger.severe(e.toString())
}
}

View File

@@ -0,0 +1,40 @@
package app.revanced.cli.command.utility
import app.revanced.utils.adb.AdbManager
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.util.logging.Logger
@Command(
name = "uninstall",
description = ["Uninstall a patched app from the devices with the supplied ADB device serials"]
)
internal object UninstallCommand : Runnable {
private val logger = Logger.getLogger(UninstallCommand::class.java.name)
@Parameters(description = ["ADB device serials"], arity = "1..*")
private lateinit var deviceSerials: Array<String>
@Option(names = ["-p", "--package-name"], description = ["Package name of the app to uninstall"], required = true)
private lateinit var packageName: String
@Option(
names = ["-u", "--unmount"],
description = ["Uninstall by unmounting the patched APK file"],
showDefaultValue = ALWAYS
)
private var unmount: Boolean = false
override fun run() = try {
deviceSerials.forEach { deviceSerial ->
if (unmount) {
AdbManager.RootAdbManager(deviceSerial)
} else {
AdbManager.UserAdbManager(deviceSerial)
}.uninstall(packageName)
}
} catch (e: AdbManager.DeviceNotFoundException) {
logger.severe(e.toString())
}
}

View File

@@ -0,0 +1,10 @@
package app.revanced.cli.command.utility
import picocli.CommandLine
@CommandLine.Command(
name = "utility",
description = ["Commands for utility purposes"],
subcommands = [InstallCommand::class, UninstallCommand::class],
)
internal object UtilityCommand

View File

@@ -1,8 +0,0 @@
package app.revanced.cli.logging
internal interface CliLogger {
fun error(msg: String)
fun info(msg: String)
fun trace(msg: String)
fun warn(msg: String)
}

View File

@@ -1,30 +0,0 @@
package app.revanced.cli.logging.impl
import app.revanced.cli.command.MainCommand
import app.revanced.cli.logging.CliLogger
import java.util.logging.Logger
import java.util.logging.SimpleFormatter
internal class DefaultCliLogger(
private val logger: Logger = Logger.getLogger(MainCommand::class.java.name),
private val errorLogger: Logger = Logger.getLogger(logger.name + "Err")
) : CliLogger {
init {
logger.useParentHandlers = false
if (logger.handlers.isEmpty()) {
logger.addHandler(FlushingStreamHandler(System.out, SimpleFormatter()))
}
}
companion object {
init {
System.setProperty("java.util.logging.SimpleFormatter.format", "%4\$s: %5\$s %n")
}
}
override fun error(msg: String) = errorLogger.severe(msg)
override fun info(msg: String) = logger.info(msg)
override fun trace(msg: String) = logger.finest(msg)
override fun warn(msg: String) = errorLogger.warning(msg)
}

View File

@@ -1,13 +0,0 @@
package app.revanced.cli.logging.impl
import java.io.OutputStream
import java.util.logging.Formatter
import java.util.logging.LogRecord
import java.util.logging.StreamHandler
internal class FlushingStreamHandler(out: OutputStream, format: Formatter) : StreamHandler(out, format) {
override fun publish(record: LogRecord) {
super.publish(record)
flush()
}
}

View File

@@ -1,13 +0,0 @@
package app.revanced.cli.patcher.logging.impl
import app.revanced.cli.logging.impl.DefaultCliLogger
import java.util.logging.Logger
internal object PatcherLogger : app.revanced.patcher.logging.Logger{
private val logger = DefaultCliLogger(Logger.getLogger(app.revanced.patcher.Patcher::class.java.name))
override fun error(msg: String) = logger.error(msg)
override fun info(msg: String) = logger.info(msg)
override fun warn(msg: String)= logger.warn(msg)
override fun trace(msg: String)= logger.trace(msg)
}

View File

@@ -1,16 +1,18 @@
package app.revanced.utils
import app.revanced.cli.command.PatchList
import app.revanced.cli.logging.CliLogger
import app.revanced.patcher.extensions.PatchExtensions.options
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.patch.NoSuchOptionException
import app.revanced.utils.Options.PatchOption.Option
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import java.io.File
import java.util.logging.Logger
internal object Options {
private val logger = Logger.getLogger(Options::class.java.name)
private var mapper = jacksonObjectMapper()
/**
@@ -53,22 +55,24 @@ internal object Options {
* Sets the options for the patches in the list.
*
* @param json The JSON string containing the options.
* @param logger The logger to use for logging.
*/
fun PatchList.setOptions(json: String, logger: CliLogger? = null) {
fun PatchList.setOptions(json: String) {
filter { it.options?.any() == true }.let { patches ->
if (patches.isEmpty()) return
val patchOptions = deserialize(json)
patches.forEach { patch ->
patches.forEach patch@{ patch ->
patchOptions.find { option -> option.patchName == patch.patchName }?.let {
it.options.forEach { option ->
try {
patch.options?.set(option.key, option.value)
?: logger?.warn("${patch.patchName} has no options")
?: run{
logger.warning("${patch.patchName} has no options")
return@patch
}
} catch (e: NoSuchOptionException) {
logger?.error(e.message ?: "Unknown error")
logger.info(e.toString())
}
}
}
@@ -80,10 +84,9 @@ internal object Options {
* Sets the options for the patches in the list.
*
* @param file The file containing the JSON string containing the options.
* @param logger The logger to use for logging.
* @see setOptions
*/
fun PatchList.setOptions(file: File, logger: CliLogger? = null) = setOptions(file.readText(), logger)
fun PatchList.setOptions(file: File) = setOptions(file.readText())
/**
* Data class for a patch and its [Option]s.

View File

@@ -1,36 +1,38 @@
package app.revanced.utils.adb
import app.revanced.cli.logging.CliLogger
import app.revanced.utils.adb.AdbManager.Apk
import app.revanced.utils.adb.Constants.COMMAND_CREATE_DIR
import app.revanced.utils.adb.Constants.COMMAND_DELETE
import app.revanced.utils.adb.Constants.COMMAND_INSTALL_MOUNT
import app.revanced.utils.adb.Constants.COMMAND_PREPARE_MOUNT_APK
import app.revanced.utils.adb.Constants.COMMAND_RESTART
import app.revanced.utils.adb.Constants.COMMAND_UMOUNT
import app.revanced.utils.adb.Constants.CONTENT_MOUNT_SCRIPT
import app.revanced.utils.adb.Constants.PATH_INIT_PUSH
import app.revanced.utils.adb.Constants.PATH_INSTALLATION
import app.revanced.utils.adb.Constants.PATH_MOUNT
import app.revanced.utils.adb.Constants.PATH_PATCHED_APK
import app.revanced.utils.adb.Constants.CREATE_DIR
import app.revanced.utils.adb.Constants.DELETE
import app.revanced.utils.adb.Constants.INSTALLATION_PATH
import app.revanced.utils.adb.Constants.INSTALL_MOUNT
import app.revanced.utils.adb.Constants.INSTALL_PATCHED_APK
import app.revanced.utils.adb.Constants.MOUNT_PATH
import app.revanced.utils.adb.Constants.MOUNT_SCRIPT
import app.revanced.utils.adb.Constants.PATCHED_APK_PATH
import app.revanced.utils.adb.Constants.PLACEHOLDER
import app.revanced.utils.adb.Constants.RESTART
import app.revanced.utils.adb.Constants.TMP_PATH
import app.revanced.utils.adb.Constants.UMOUNT
import se.vidstige.jadb.JadbConnection
import se.vidstige.jadb.managers.Package
import se.vidstige.jadb.managers.PackageManager
import java.io.Closeable
import java.io.File
import java.util.logging.Logger
/**
* Adb manager. Used to install and uninstall [Apk] files.
*
* @param deviceSerial The serial of the device.
*/
internal sealed class AdbManager(deviceSerial: String? = null, protected val logger: CliLogger? = null) : Closeable {
internal sealed class AdbManager(deviceSerial: String? = null) : Closeable {
protected val logger: Logger = Logger.getLogger(AdbManager::class.java.name)
protected val device = JadbConnection().devices.find { device -> device.serial == deviceSerial }
?: throw DeviceNotFoundException(deviceSerial)
init {
logger?.trace("Established connection to $deviceSerial")
logger.fine("Established connection to $deviceSerial")
}
/**
@@ -39,7 +41,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log
* @param apk The [Apk] file.
*/
open fun install(apk: Apk) {
logger?.info("Finished installing ${apk.file.name}")
logger.info("Finished installing ${apk.file.name}")
}
/**
@@ -48,51 +50,53 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log
* @param packageName The package name.
*/
open fun uninstall(packageName: String) {
logger?.info("Finished uninstalling $packageName")
logger.info("Finished uninstalling $packageName")
}
/**
* Closes the [AdbManager] instance.
*/
override fun close() {
logger?.trace("Closed")
logger.fine("Closed")
}
class RootAdbManager(deviceSerial: String, logger: CliLogger? = null) : AdbManager(deviceSerial, logger) {
class RootAdbManager(deviceSerial: String) : AdbManager(deviceSerial) {
init {
if (!device.hasSu()) throw IllegalArgumentException("Root required on $deviceSerial. Task failed")
}
override fun install(apk: Apk) {
logger?.info("Installing by mounting")
logger.info("Installing by mounting")
val applyReplacement = getPlaceholderReplacement(
apk.packageName ?: throw IllegalArgumentException("Package name is required")
)
device.copyFile(apk.file, PATH_INIT_PUSH)
device.push(apk.file, TMP_PATH)
device.run("$COMMAND_CREATE_DIR $PATH_INSTALLATION")
device.run(COMMAND_PREPARE_MOUNT_APK.applyReplacement())
device.run("$CREATE_DIR $INSTALLATION_PATH")
device.run(INSTALL_PATCHED_APK.applyReplacement())
device.createFile(PATH_INIT_PUSH, CONTENT_MOUNT_SCRIPT.applyReplacement())
device.createFile(TMP_PATH, MOUNT_SCRIPT.applyReplacement())
device.run(COMMAND_INSTALL_MOUNT.applyReplacement())
device.run(COMMAND_UMOUNT.applyReplacement()) // Sanity check.
device.run(PATH_MOUNT.applyReplacement())
device.run(COMMAND_RESTART.applyReplacement())
device.run(INSTALL_MOUNT.applyReplacement())
device.run(UMOUNT.applyReplacement()) // Sanity check.
device.run(MOUNT_PATH.applyReplacement())
device.run(RESTART.applyReplacement())
device.run(DELETE.applyReplacement(TMP_PATH).applyReplacement())
super.install(apk)
}
override fun uninstall(packageName: String) {
logger?.info("Uninstalling $packageName by unmounting and deleting the package")
logger.info("Uninstalling $packageName by unmounting")
val applyReplacement = getPlaceholderReplacement(packageName)
device.run(COMMAND_UMOUNT.applyReplacement(packageName))
device.run(COMMAND_DELETE.applyReplacement(PATH_PATCHED_APK).applyReplacement())
device.run(COMMAND_DELETE.applyReplacement(PATH_MOUNT).applyReplacement())
device.run(UMOUNT.applyReplacement(packageName))
device.run(DELETE.applyReplacement(PATCHED_APK_PATH).applyReplacement())
device.run(DELETE.applyReplacement(MOUNT_PATH).applyReplacement())
device.run(DELETE.applyReplacement(TMP_PATH).applyReplacement())
super.uninstall(packageName)
}
@@ -103,7 +107,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log
}
}
class UserAdbManager(deviceSerial: String, logger: CliLogger? = null) : AdbManager(deviceSerial, logger) {
class UserAdbManager(deviceSerial: String) : AdbManager(deviceSerial) {
private val packageManager = PackageManager(device)
override fun install(apk: Apk) {
@@ -113,7 +117,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log
}
override fun uninstall(packageName: String) {
logger?.info("Uninstalling $packageName")
logger.info("Uninstalling $packageName")
packageManager.uninstall(Package(packageName))
@@ -125,6 +129,7 @@ internal sealed class AdbManager(deviceSerial: String? = null, protected val log
* Apk file for [AdbManager].
*
* @param file The [Apk] file.
* @param packageName The package name of the [Apk] file.
*/
internal class Apk(val file: File, val packageName: String? = null)

View File

@@ -2,24 +2,28 @@ package app.revanced.utils.adb
import se.vidstige.jadb.JadbDevice
import se.vidstige.jadb.RemoteFile
import se.vidstige.jadb.ShellProcessBuilder
import java.io.File
import java.util.concurrent.Callable
import java.util.concurrent.Executors
// return the input or output stream, depending on which first returns a value
internal fun JadbDevice.run(command: String, su: Boolean = false) = with(this.startCommand(command, su)) {
Executors.newFixedThreadPool(2).let { service ->
arrayOf(inputStream, errorStream).map { stream ->
Callable { stream.bufferedReader().use { it.readLine() } }
}.let { tasks -> service.invokeAny(tasks).also { service.shutdown() } }
}
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())
}
internal fun JadbDevice.run(command: String, su: Boolean = true): Int {
return this.buildCommand(command, su).start().waitFor()
}
internal fun JadbDevice.hasSu() =
this.startCommand("su -h", false).waitFor() == 0
internal fun JadbDevice.copyFile(file: File, targetFile: String) =
push(file, RemoteFile(targetFile))
internal fun JadbDevice.push(file: File, targetFilePath: String) =
push(file, RemoteFile(targetFilePath))
internal fun JadbDevice.createFile(targetFile: String, content: String) =
push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))

View File

@@ -1,40 +1,40 @@
package app.revanced.utils.adb
internal object Constants {
internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME"
internal const val PLACEHOLDER = "PLACEHOLDER"
internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete"
internal const val PATH_INSTALLATION = "/data/adb/revanced/"
internal const val PATH_PATCHED_APK = "$PATH_INSTALLATION$PLACEHOLDER.apk"
internal const val PATH_MOUNT = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
internal const val TMP_PATH = "/data/local/tmp/revanced.tmp"
internal const val INSTALLATION_PATH = "/data/adb/revanced/"
internal const val PATCHED_APK_PATH = "$INSTALLATION_PATH$PLACEHOLDER.apk"
internal const val MOUNT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
internal const val COMMAND_DELETE = "rm -rf $PLACEHOLDER"
internal const val COMMAND_CREATE_DIR = "mkdir -p"
internal const val COMMAND_RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " +
internal const val DELETE = "rm -rf $PLACEHOLDER"
internal const val CREATE_DIR = "mkdir -p"
internal const val RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " +
"xargs am start -n && kill ${'$'}(pidof -s $PLACEHOLDER)"
internal const val COMMAND_PREPARE_MOUNT_APK = "base_path=\"$PATH_PATCHED_APK\" && " +
"mv $PATH_INIT_PUSH ${'$'}base_path && " +
internal const val INSTALL_PATCHED_APK = "base_path=\"$PATCHED_APK_PATH\" && " +
"mv $TMP_PATH ${'$'}base_path && " +
"chmod 644 ${'$'}base_path && " +
"chown system:system ${'$'}base_path && " +
"chcon u:object_r:apk_data_file:s0 ${'$'}base_path"
internal const val COMMAND_UMOUNT =
internal const val UMOUNT =
"grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d \" \" -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done"
internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && chmod +x $PATH_MOUNT"
internal const val INSTALL_MOUNT = "mv $TMP_PATH $MOUNT_PATH && chmod +x $MOUNT_PATH"
internal const val CONTENT_MOUNT_SCRIPT =
internal val MOUNT_SCRIPT =
"""
#!/system/bin/sh
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
base_path="$PATH_PATCHED_APK"
base_path="$PATCHED_APK_PATH"
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
"""
""".trimIndent()
}

View File

@@ -1,6 +1,5 @@
package app.revanced.utils.signing
import app.revanced.cli.command.logger
import com.android.apksig.ApkSigner
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
@@ -16,10 +15,13 @@ import java.math.BigInteger
import java.security.*
import java.security.cert.X509Certificate
import java.util.*
import java.util.logging.Logger
internal class ApkSigner(
private val signingOptions: SigningOptions
) {
private val logger = Logger.getLogger(ApkSigner::class.java.name)
private val signer: ApkSigner.Builder
private val passwordCharArray = signingOptions.password.toCharArray()