mirror of
https://github.com/ReVanced/revanced-library.git
synced 2026-01-10 21:36:17 +00:00
chore: Merge branch dev to main (#60)
This commit is contained in:
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -70,7 +70,8 @@ body:
|
||||
|
||||
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).
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -100,7 +101,7 @@ body:
|
||||
label: Acknowledgements
|
||||
description: Your bug report will be closed if you don't follow the checklist below.
|
||||
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
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
6
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -70,8 +70,8 @@ body:
|
||||
|
||||
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).
|
||||
- **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).
|
||||
- **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 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).
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -98,7 +98,7 @@ body:
|
||||
label: Acknowledgements
|
||||
description: Your feature request will be closed if you don't follow the checklist below.
|
||||
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
|
||||
- label: I have chosen an appropriate title.
|
||||
required: true
|
||||
|
||||
3
.github/workflows/build_pull_request.yml
vendored
3
.github/workflows/build_pull_request.yml
vendored
@@ -19,6 +19,9 @@ jobs:
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
|
||||
- name: Setup Java
|
||||
run: echo "JAVA_HOME=$JAVA_HOME_17_X64" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -23,6 +23,9 @@ jobs:
|
||||
- name: Cache Gradle
|
||||
uses: burrunan/gradle-cache-action@v1
|
||||
|
||||
- name: Setup Java
|
||||
run: echo "JAVA_HOME=$JAVA_HOME_17_X64" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -42,7 +45,7 @@ jobs:
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
fingerprint: ${{ env.GPG_FINGERPRINT }}
|
||||
fingerprint: ${{ vars.GPG_FINGERPRINT }}
|
||||
|
||||
- name: Release
|
||||
env:
|
||||
|
||||
123
.gitignore
vendored
123
.gitignore
vendored
@@ -1,118 +1,11 @@
|
||||
### Java template
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
.idea
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Gradle template
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
|
||||
# gradle/wrapper/gradle-wrapper.properties
|
||||
|
||||
# Dependency directories
|
||||
.idea
|
||||
.DS_Store
|
||||
build
|
||||
captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
xcuserdata
|
||||
node_modules/
|
||||
31
CHANGELOG.md
31
CHANGELOG.md
@@ -1,3 +1,34 @@
|
||||
# [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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add local Android installer ([#25](https://github.com/ReVanced/revanced-library/issues/25)) ([43d655a](https://github.com/ReVanced/revanced-library/commit/43d655aea5d86288ae9916630e0f30de219d5cfb))
|
||||
|
||||
# [2.3.0](https://github.com/ReVanced/revanced-library/compare/v2.2.1...v2.3.0) (2024-03-14)
|
||||
|
||||
|
||||
|
||||
221
api/android/revanced-library.api
Normal file
221
api/android/revanced-library.api
Normal file
@@ -0,0 +1,221 @@
|
||||
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/ILocalShellCommandRunnerRootService : android/os/IInterface {
|
||||
public static final field DESCRIPTOR Ljava/lang/String;
|
||||
public abstract fun getFileSystemService ()Landroid/os/IBinder;
|
||||
}
|
||||
|
||||
public class app/revanced/library/installation/command/ILocalShellCommandRunnerRootService$Default : app/revanced/library/installation/command/ILocalShellCommandRunnerRootService {
|
||||
public fun <init> ()V
|
||||
public fun asBinder ()Landroid/os/IBinder;
|
||||
public fun getFileSystemService ()Landroid/os/IBinder;
|
||||
}
|
||||
|
||||
public abstract class app/revanced/library/installation/command/ILocalShellCommandRunnerRootService$Stub : android/os/Binder, app/revanced/library/installation/command/ILocalShellCommandRunnerRootService {
|
||||
public fun <init> ()V
|
||||
public fun asBinder ()Landroid/os/IBinder;
|
||||
public static fun asInterface (Landroid/os/IBinder;)Lapp/revanced/library/installation/command/ILocalShellCommandRunnerRootService;
|
||||
public fun onTransact (ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/command/LocalShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner, android/content/ServiceConnection, java/io/Closeable {
|
||||
public fun close ()V
|
||||
public fun onServiceConnected (Landroid/content/ComponentName;Landroid/os/IBinder;)V
|
||||
public fun onServiceDisconnected (Landroid/content/ComponentName;)V
|
||||
}
|
||||
|
||||
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/LocalInstaller : app/revanced/library/installation/installer/Installer, java/io/Closeable {
|
||||
public static final field Companion Lapp/revanced/library/installation/installer/LocalInstaller$Companion;
|
||||
public fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V
|
||||
public fun close ()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 final class app/revanced/library/installation/installer/LocalInstaller$Companion {
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/LocalInstallerResult {
|
||||
public final fun getExtra ()Ljava/lang/String;
|
||||
public final fun getPackageName ()Ljava/lang/String;
|
||||
public final fun getPmStatus ()I
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/LocalInstallerService : android/app/Service {
|
||||
public fun <init> ()V
|
||||
public fun onBind (Landroid/content/Intent;)Landroid/os/IBinder;
|
||||
public fun onStartCommand (Landroid/content/Intent;II)I
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/LocalRootInstaller : app/revanced/library/installation/installer/RootInstaller, java/io/Closeable {
|
||||
public fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V
|
||||
public synthetic fun <init> (Landroid/content/Context;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun close ()V
|
||||
}
|
||||
|
||||
public final class app/revanced/library/installation/installer/RootInstallation : app/revanced/library/installation/installer/Installation {
|
||||
public final 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
|
||||
}
|
||||
|
||||
167
api/jvm/revanced-library.api
Normal file
167
api/jvm/revanced-library.api
Normal 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
|
||||
}
|
||||
|
||||
@@ -1,191 +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 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 final fun getDevice ()Lse/vidstige/jadb/JadbDevice;
|
||||
protected final fun getLogger ()Ljava/util/logging/Logger;
|
||||
public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)V
|
||||
public fun uninstall (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
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 fun install (Lapp/revanced/library/adb/AdbManager$Apk;)V
|
||||
public fun uninstall (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
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 fun install (Lapp/revanced/library/adb/AdbManager$Apk;)V
|
||||
public fun uninstall (Ljava/lang/String;)V
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
133
build.gradle.kts
133
build.gradle.kts
@@ -1,61 +1,93 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin)
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.binary.compatibility.validator)
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
`maven-publish`
|
||||
signing
|
||||
}
|
||||
|
||||
group = "app.revanced"
|
||||
|
||||
// Because access to the project is necessary to authenticate with GitHub,
|
||||
// the following block must be placed in the root build.gradle.kts file
|
||||
// instead of the settings.gradle.kts file inside the dependencyResolutionManagement block.
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
google()
|
||||
maven {
|
||||
// A repository must be speficied for some reason. "registry" is a dummy.
|
||||
// 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") }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.revanced.patcher)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.jadb) // Fork with Shell v2 support.
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
implementation(libs.apkzlib)
|
||||
implementation(libs.apksig)
|
||||
implementation(libs.bcpkix.jdk15on)
|
||||
implementation(libs.guava)
|
||||
kotlin {
|
||||
jvm {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
}
|
||||
}
|
||||
|
||||
testImplementation(libs.revanced.patcher)
|
||||
testImplementation(libs.kotlin.test)
|
||||
}
|
||||
androidTarget {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_11
|
||||
}
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events("PASSED", "SKIPPED", "FAILED")
|
||||
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.jdk15on)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_11)
|
||||
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
|
||||
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
@@ -64,43 +96,40 @@ publishing {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-library")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
|
||||
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publications {
|
||||
create<MavenPublication>("revanced-library-publication") {
|
||||
from(components["java"])
|
||||
// KMP plugin creates a publication already, so just configure the POM.
|
||||
publications.all {
|
||||
if (this !is MavenPublication) return@all
|
||||
|
||||
version = project.version.toString()
|
||||
pom {
|
||||
name = "ReVanced Library"
|
||||
description = "Library containing common utilities for ReVanced"
|
||||
url = "https://revanced.app"
|
||||
|
||||
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"
|
||||
}
|
||||
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"
|
||||
}
|
||||
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"
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,5 +137,5 @@ publishing {
|
||||
|
||||
signing {
|
||||
useGpgCmd()
|
||||
sign(publishing.publications["revanced-library-publication"])
|
||||
sign(publishing.publications)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
org.gradle.parallel = true
|
||||
version = 3.0.0-dev.1
|
||||
#Gradle
|
||||
org.gradle.jvmargs = -Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options="-Xmx2048M"
|
||||
org.gradle.caching = true
|
||||
org.gradle.configuration-cache = true
|
||||
org.gradle.parallel = true
|
||||
#Kotlin
|
||||
kotlin.code.style = official
|
||||
version = 2.3.0
|
||||
#Android
|
||||
android.useAndroidX = true
|
||||
android.nonTransitiveRClass = true
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
[versions]
|
||||
jackson-module-kotlin = "2.15.0"
|
||||
jadb = "1.2.1"
|
||||
kotlin = "1.9.22"
|
||||
revanced-patcher = "19.3.1"
|
||||
binary-compatibility-validator = "0.14.0"
|
||||
apkzlib = "8.3.0"
|
||||
android = "8.5.1"
|
||||
bcpkix-jdk15on = "1.70"
|
||||
binary-compatibility-validator = "0.15.1"
|
||||
core-ktx = "1.13.1"
|
||||
guava = "33.0.0-jre"
|
||||
apksig = "8.3.0"
|
||||
jadb = "1.2.1"
|
||||
kotlin = "2.0.0"
|
||||
kotlinx-coroutines = "1.8.1"
|
||||
kotlinx-serialization = "1.7.1"
|
||||
libsu = "5.2.2"
|
||||
revanced-patcher = "20.0.0"
|
||||
|
||||
[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 = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||
apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "apkzlib" }
|
||||
apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "android" }
|
||||
apksig = { module = "com.android.tools.build:apksig", version.ref = "android" }
|
||||
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkix-jdk15on" }
|
||||
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" }
|
||||
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-nio = { module = "com.github.topjohnwu.libsu:nio", version.ref = "libsu" }
|
||||
libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" }
|
||||
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
|
||||
|
||||
[plugins]
|
||||
android-library = { id = "com.android.library", version.ref = "android" }
|
||||
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
8
gradle/wrapper/gradle-wrapper.properties
vendored
8
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,8 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
|
||||
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dist
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
22
gradlew
vendored
22
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# 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/.
|
||||
@@ -83,7 +85,9 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -201,11 +205,11 @@ 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, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# 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" \
|
||||
|
||||
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
// TODO: Figure out why this causes problems.
|
||||
rootProject.name = "revanced-library"
|
||||
|
||||
buildCache {
|
||||
local {
|
||||
isEnabled = !System.getenv().containsKey("CI")
|
||||
isEnabled = "CI" !in System.getenv()
|
||||
}
|
||||
}
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package app.revanced.library.installation.command;
|
||||
|
||||
interface ILocalShellCommandRunnerRootService {
|
||||
IBinder getFileSystemService();
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package app.revanced.library.installation.command
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.internal.BuilderImpl
|
||||
import com.topjohnwu.superuser.ipc.RootService
|
||||
import com.topjohnwu.superuser.nio.FileSystemManager
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* The [LocalShellCommandRunner] for running commands locally on the device.
|
||||
*
|
||||
* @param context The [Context] to use for binding to the [RootService].
|
||||
* @param onReady A callback to be invoked when [LocalShellCommandRunner] is ready to be used.
|
||||
* @throws IllegalStateException If the main shell was already created
|
||||
*
|
||||
* @see ShellCommandRunner
|
||||
*/
|
||||
class LocalShellCommandRunner internal constructor(
|
||||
private val context: Context,
|
||||
private val onReady: () -> Unit,
|
||||
) : ShellCommandRunner(), ServiceConnection, Closeable {
|
||||
private var fileSystemManager: FileSystemManager? = null
|
||||
|
||||
init {
|
||||
logger.info("Binding to RootService")
|
||||
val intent = Intent(context, LocalShellCommandRunnerRootService::class.java)
|
||||
RootService.bind(intent, this)
|
||||
}
|
||||
|
||||
override fun runCommand(command: String) = shell.newJob().add(command).exec().let {
|
||||
object : RunResult {
|
||||
override val exitCode = it.code
|
||||
override val output by lazy { it.out.joinToString("\n") }
|
||||
override val error by lazy { it.err.joinToString("\n") }
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasRootPermission() = shell.isRoot
|
||||
|
||||
/**
|
||||
* Writes the given [content] to the given [targetFilePath].
|
||||
*
|
||||
* @param content The [InputStream] to write.
|
||||
* @param targetFilePath The path to write to.
|
||||
* @throws NotReadyException If the [LocalShellCommandRunner] is not ready yet.
|
||||
*/
|
||||
override fun write(content: InputStream, targetFilePath: String) {
|
||||
fileSystemManager?.let {
|
||||
it.getFile(targetFilePath).newOutputStream().use { outputStream ->
|
||||
content.copyTo(outputStream)
|
||||
}
|
||||
} ?: throw NotReadyException("FileSystemManager service is not ready yet")
|
||||
}
|
||||
|
||||
override fun move(file: File, targetFilePath: String) {
|
||||
invoke("mv ${file.absolutePath} $targetFilePath")
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
val ipc = ILocalShellCommandRunnerRootService.Stub.asInterface(service)
|
||||
fileSystemManager = FileSystemManager.getRemote(ipc.fileSystemService)
|
||||
|
||||
logger.info("LocalShellCommandRunner service is ready")
|
||||
|
||||
onReady()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
fileSystemManager = null
|
||||
|
||||
logger.info("LocalShellCommandRunner service is disconnected")
|
||||
}
|
||||
|
||||
override fun close() = RootService.unbind(this)
|
||||
|
||||
private companion object {
|
||||
private val shell = BuilderImpl.create().setFlags(Shell.FLAG_MOUNT_MASTER).build()
|
||||
}
|
||||
|
||||
internal class NotReadyException internal constructor(message: String) : Exception(message)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.library.installation.command
|
||||
|
||||
import android.content.Intent
|
||||
import com.topjohnwu.superuser.ipc.RootService
|
||||
import com.topjohnwu.superuser.nio.FileSystemManager
|
||||
|
||||
/**
|
||||
* The [RootService] for the [LocalShellCommandRunner].
|
||||
*/
|
||||
internal class LocalShellCommandRunnerRootService : RootService() {
|
||||
override fun onBind(intent: Intent) = object : ILocalShellCommandRunnerRootService.Stub() {
|
||||
override fun getFileSystemService() =
|
||||
FileSystemManager.getService()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* [LocalInstaller] for installing and uninstalling [Apk] files locally.
|
||||
*
|
||||
* @param context The [Context] to use for installing and uninstalling.
|
||||
* @param onResult The callback to be invoked when the [Apk] is installed or uninstalled.
|
||||
*
|
||||
* @see Installer
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class LocalInstaller(
|
||||
private val context: Context,
|
||||
onResult: (result: LocalInstallerResult) -> Unit,
|
||||
) : Installer<Unit, Installation>(), Closeable {
|
||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val pmStatus = intent.getIntExtra(LocalInstallerService.EXTRA_STATUS, -999)
|
||||
val extra = intent.getStringExtra(LocalInstallerService.EXTRA_STATUS_MESSAGE)!!
|
||||
val packageName = intent.getStringExtra(LocalInstallerService.EXTRA_PACKAGE_NAME)!!
|
||||
|
||||
onResult.invoke(LocalInstallerResult(pmStatus, extra, packageName))
|
||||
}
|
||||
}
|
||||
|
||||
private val intentSender
|
||||
get() = PendingIntent.getService(
|
||||
context,
|
||||
0,
|
||||
Intent(context, LocalInstallerService::class.java),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
).intentSender
|
||||
|
||||
init {
|
||||
ContextCompat.registerReceiver(
|
||||
context,
|
||||
broadcastReceiver,
|
||||
IntentFilter().apply {
|
||||
addAction(LocalInstallerService.ACTION)
|
||||
},
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun install(apk: Apk) {
|
||||
logger.info("Installing ${apk.file.name}")
|
||||
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
|
||||
packageInstaller.openSession(packageInstaller.createSession(sessionParams)).use { session ->
|
||||
session.writeApk(apk.file)
|
||||
session.commit(intentSender)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override suspend fun uninstall(packageName: String) {
|
||||
logger.info("Uninstalling $packageName")
|
||||
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
|
||||
packageInstaller.uninstall(packageName, intentSender)
|
||||
}
|
||||
|
||||
override suspend fun getInstallation(packageName: String) = try {
|
||||
val packageInfo = context.packageManager.getPackageInfo(packageName, 0)
|
||||
|
||||
Installation(packageInfo.applicationInfo.sourceDir)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
||||
override fun close() = context.unregisterReceiver(broadcastReceiver)
|
||||
|
||||
companion object {
|
||||
private val sessionParams = PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL,
|
||||
).apply {
|
||||
setInstallReason(PackageManager.INSTALL_REASON_USER)
|
||||
}
|
||||
|
||||
private fun PackageInstaller.Session.writeApk(apk: File) {
|
||||
apk.inputStream().use { inputStream ->
|
||||
openWrite(apk.name, 0, apk.length()).use { outputStream ->
|
||||
inputStream.copyTo(outputStream, 1024 * 1024)
|
||||
fsync(outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
|
||||
/**
|
||||
* The result of installing or uninstalling an [Apk] locally using [LocalInstaller].
|
||||
*
|
||||
* @param pmStatus The status code returned by the package manager.
|
||||
* @param extra The extra information returned by the package manager.
|
||||
* @param packageName The package name of the installed app.
|
||||
*
|
||||
* @see LocalInstaller
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class LocalInstallerResult internal constructor(val pmStatus: Int, val extra: String, val packageName: String)
|
||||
@@ -0,0 +1,57 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
|
||||
class LocalInstallerService : Service() {
|
||||
override fun onStartCommand(
|
||||
intent: Intent, flags: Int, startId: Int
|
||||
): Int {
|
||||
val extraStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0)
|
||||
val extraStatusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
||||
val extraPackageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
|
||||
|
||||
when (extraStatus) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
startActivity(
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_INTENT)
|
||||
}?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
sendBroadcast(
|
||||
Intent().apply {
|
||||
action = ACTION
|
||||
`package` = packageName
|
||||
|
||||
putExtra(EXTRA_STATUS, extraStatus)
|
||||
putExtra(EXTRA_STATUS_MESSAGE, extraStatusMessage)
|
||||
putExtra(EXTRA_PACKAGE_NAME, extraPackageName)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
stopSelf()
|
||||
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
internal companion object {
|
||||
internal const val ACTION = "PACKAGE_INSTALLER_ACTION"
|
||||
|
||||
internal const val EXTRA_STATUS = "EXTRA_STATUS"
|
||||
internal const val EXTRA_STATUS_MESSAGE = "EXTRA_STATUS_MESSAGE"
|
||||
internal const val EXTRA_PACKAGE_NAME = "EXTRA_PACKAGE_NAME"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import android.content.Context
|
||||
import app.revanced.library.installation.command.LocalShellCommandRunner
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
|
||||
import com.topjohnwu.superuser.ipc.RootService
|
||||
import java.io.Closeable
|
||||
|
||||
/**
|
||||
* [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 onReady A callback to be invoked when [LocalRootInstaller] is ready to be used.
|
||||
*
|
||||
* @throws NoRootPermissionException If the device does not have root permission.
|
||||
*
|
||||
* @see Installer
|
||||
* @see LocalShellCommandRunner
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class LocalRootInstaller(
|
||||
context: Context,
|
||||
onReady: LocalRootInstaller.() -> Unit = {},
|
||||
) : RootInstaller(
|
||||
{ installer ->
|
||||
LocalShellCommandRunner(context) {
|
||||
(installer as LocalRootInstaller).onReady()
|
||||
}
|
||||
},
|
||||
),
|
||||
Closeable {
|
||||
override fun close() = (shellCommandRunner as LocalShellCommandRunner).close()
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.library
|
||||
|
||||
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.x509.SubjectPublicKeyInfo
|
||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
||||
@@ -13,7 +10,6 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.math.BigInteger
|
||||
import java.security.*
|
||||
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.
|
||||
*
|
||||
@@ -322,48 +219,11 @@ object ApkSigner {
|
||||
val certificate: X509Certificate,
|
||||
)
|
||||
|
||||
class Signer {
|
||||
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
|
||||
}
|
||||
|
||||
class Signer internal constructor(private val signerBuilder: com.android.apksig.ApkSigner.Builder) {
|
||||
fun signApk(inputApkFile: File, outputApkFile: File) {
|
||||
logger.info("Signing APK")
|
||||
|
||||
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)
|
||||
signerBuilder.setInputApk(inputApkFile)?.setOutputApk(outputApkFile)?.build()?.sign()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,8 +103,7 @@ object ApkUtils {
|
||||
*
|
||||
* @return The newly created private key and certificate pair.
|
||||
*/
|
||||
@Deprecated("This method will be removed in the future.")
|
||||
fun newPrivateKeyCertificatePair(
|
||||
private fun newPrivateKeyCertificatePair(
|
||||
privateKeyCertificatePairDetails: PrivateKeyCertificatePairDetails,
|
||||
keyStoreDetails: KeyStoreDetails,
|
||||
) = newPrivateKeyCertificatePair(
|
||||
@@ -132,8 +131,7 @@ object ApkUtils {
|
||||
*
|
||||
* @return The private key and certificate pair.
|
||||
*/
|
||||
@Deprecated("This method will be removed in the future.")
|
||||
fun readPrivateKeyCertificatePairFromKeyStore(
|
||||
private fun readPrivateKeyCertificatePairFromKeyStore(
|
||||
keyStoreDetails: KeyStoreDetails,
|
||||
) = ApkSigner.readPrivateKeyCertificatePair(
|
||||
ApkSigner.readKeyStore(
|
||||
@@ -168,91 +166,6 @@ object ApkUtils {
|
||||
},
|
||||
).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.
|
||||
*
|
||||
31
src/commonMain/kotlin/app/revanced/library/Options.kt
Normal file
31
src/commonMain/kotlin/app/revanced/library/Options.kt
Normal file
@@ -0,0 +1,31 @@
|
||||
@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 ->
|
||||
val patchOptions = options[patch.name] ?: return@forEach
|
||||
|
||||
patch.options.forEach option@{ option ->
|
||||
try {
|
||||
patch.options[option.key] = patchOptions[option.key] ?: return@option
|
||||
} catch (e: OptionException) {
|
||||
logger.warning("Could not set option value for the \"${patch.name}\" patch: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/commonMain/kotlin/app/revanced/library/Patch.kt
Normal file
52
src/commonMain/kotlin/app/revanced/library/Patch.kt
Normal 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
|
||||
}
|
||||
}
|
||||
114
src/commonMain/kotlin/app/revanced/library/Serialization.kt
Normal file
114
src/commonMain/kotlin/app/revanced/library/Serialization.kt
Normal file
@@ -0,0 +1,114 @@
|
||||
package app.revanced.library
|
||||
|
||||
import app.revanced.patcher.patch.*
|
||||
import kotlinx.serialization.*
|
||||
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.*
|
||||
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<Set<Package>?>("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,
|
||||
SetSerializer(PairSerializer(String.serializer(), SetSerializer(String.serializer()).nullable)),
|
||||
value.compatiblePackages,
|
||||
)
|
||||
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)
|
||||
18
src/commonMain/kotlin/app/revanced/library/Utils.kt
Normal file
18
src/commonMain/kotlin/app/revanced/library/Utils.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package app.revanced.library
|
||||
|
||||
/**
|
||||
* Utils for the library.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
object Utils {
|
||||
/**
|
||||
* True if the environment is Android.
|
||||
*/
|
||||
val isAndroidEnvironment =
|
||||
try {
|
||||
Class.forName("android.app.Application")
|
||||
true
|
||||
} catch (e: ClassNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package app.revanced.library.installation.command
|
||||
|
||||
import app.revanced.library.installation.installer.getDevice
|
||||
import se.vidstige.jadb.JadbDevice
|
||||
import se.vidstige.jadb.RemoteFile
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* [AdbShellCommandRunner] for running commands on a device remotely using ADB.
|
||||
*
|
||||
* @see ShellCommandRunner
|
||||
*/
|
||||
class AdbShellCommandRunner : ShellCommandRunner {
|
||||
private val device: JadbDevice
|
||||
|
||||
/**
|
||||
* Creates a [AdbShellCommandRunner] for the given device.
|
||||
*
|
||||
* @param device The device.
|
||||
*/
|
||||
internal constructor(device: JadbDevice) {
|
||||
this.device = device
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [AdbShellCommandRunner] for the device with the given serial.
|
||||
*
|
||||
* @param deviceSerial deviceSerial The device serial. If null, the first connected device will be used.
|
||||
*/
|
||||
internal constructor(deviceSerial: String?) {
|
||||
device = getDevice(deviceSerial, logger)
|
||||
}
|
||||
|
||||
override fun runCommand(command: String) = device.shellProcessBuilder(command).start().let { process ->
|
||||
object : RunResult {
|
||||
override val exitCode by lazy { process.waitFor() }
|
||||
override val output by lazy { process.inputStream.bufferedReader().readText() }
|
||||
override val error by lazy { process.errorStream.bufferedReader().readText() }
|
||||
|
||||
override fun waitFor() {
|
||||
process.waitFor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasRootPermission(): Boolean = invoke("whoami").exitCode == 0
|
||||
|
||||
override fun write(content: InputStream, targetFilePath: String) =
|
||||
device.push(content, System.currentTimeMillis(), 644, RemoteFile(targetFilePath))
|
||||
|
||||
/**
|
||||
* Moves the given [file] from the local to the target file path on the device.
|
||||
*
|
||||
* @param file The file to move.
|
||||
* @param targetFilePath The target file path.
|
||||
*/
|
||||
override fun move(file: File, targetFilePath: String) = device.push(file, RemoteFile(targetFilePath))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package app.revanced.library.installation.command
|
||||
|
||||
/**
|
||||
* The result of a command execution.
|
||||
*/
|
||||
interface RunResult {
|
||||
/**
|
||||
* The exit code of the command.
|
||||
*/
|
||||
val exitCode: Int
|
||||
|
||||
/**
|
||||
* The output of the command.
|
||||
*/
|
||||
val output: String
|
||||
|
||||
/**
|
||||
* The error of the command.
|
||||
*/
|
||||
val error: String
|
||||
|
||||
/**
|
||||
* Waits for the command to finish.
|
||||
*/
|
||||
fun waitFor() {}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package app.revanced.library.installation.command
|
||||
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.util.logging.Logger
|
||||
|
||||
/**
|
||||
* [ShellCommandRunner] for running commands on a device.
|
||||
*/
|
||||
abstract class ShellCommandRunner internal constructor() {
|
||||
protected val logger: Logger = Logger.getLogger(this::class.java.name)
|
||||
|
||||
/**
|
||||
* Writes the given [content] to the file at the given [targetFilePath] path.
|
||||
*
|
||||
* @param content The content of the file.
|
||||
* @param targetFilePath The target file path.
|
||||
*/
|
||||
internal abstract fun write(
|
||||
content: InputStream,
|
||||
targetFilePath: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Moves the given [file] to the given [targetFilePath] path.
|
||||
*
|
||||
* @param file The file to move.
|
||||
* @param targetFilePath The target file path.
|
||||
*/
|
||||
internal abstract fun move(
|
||||
file: File,
|
||||
targetFilePath: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Runs the given [command] on the device as root.
|
||||
*
|
||||
* @param command The command to run.
|
||||
* @return The [RunResult].
|
||||
*/
|
||||
protected abstract fun runCommand(command: String): RunResult
|
||||
|
||||
/**
|
||||
* Checks if the device has root permission.
|
||||
*
|
||||
* @return True if the device has root permission, false otherwise.
|
||||
*/
|
||||
internal abstract fun hasRootPermission(): Boolean
|
||||
|
||||
/**
|
||||
* Runs a command on the device as root.
|
||||
*
|
||||
* @param command The command to run.
|
||||
* @return The [RunResult].
|
||||
*/
|
||||
internal operator fun invoke(
|
||||
command: String,
|
||||
) = runCommand("su -c \'$command\'")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.command.AdbShellCommandRunner
|
||||
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.managers.Package
|
||||
import se.vidstige.jadb.managers.PackageManager
|
||||
|
||||
/**
|
||||
* [AdbInstaller] for installing and uninstalling [Apk] files using ADB.
|
||||
*
|
||||
* @param deviceSerial The device serial. If null, the first connected device will be used.
|
||||
*
|
||||
* @see Installer
|
||||
*/
|
||||
class AdbInstaller(
|
||||
deviceSerial: String? = null,
|
||||
) : Installer<AdbInstallerResult, Installation>() {
|
||||
private val device = getDevice(deviceSerial, logger)
|
||||
private val adbShellCommandRunner = AdbShellCommandRunner(device)
|
||||
private val packageManager = PackageManager(device)
|
||||
|
||||
init {
|
||||
logger.fine("Connected to $deviceSerial")
|
||||
}
|
||||
|
||||
override suspend fun install(apk: Apk): AdbInstallerResult {
|
||||
logger.info("Installing ${apk.file.name}")
|
||||
|
||||
return runPackageManager { install(apk.file) }
|
||||
}
|
||||
|
||||
override suspend fun uninstall(packageName: String): AdbInstallerResult {
|
||||
logger.info("Uninstalling $packageName")
|
||||
|
||||
return runPackageManager { uninstall(Package(packageName)) }
|
||||
}
|
||||
|
||||
override suspend fun getInstallation(packageName: String): Installation? = packageManager.packages.find {
|
||||
it.toString() == packageName
|
||||
}?.let { Installation(adbShellCommandRunner(INSTALLED_APK_PATH).output) }
|
||||
|
||||
private fun runPackageManager(block: PackageManager.() -> Unit) = try {
|
||||
packageManager.run(block)
|
||||
|
||||
AdbInstallerResult.Success
|
||||
} catch (e: JadbException) {
|
||||
AdbInstallerResult.Failure(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
|
||||
/**
|
||||
* The result of installing or uninstalling an [Apk] via ADB using [AdbInstaller].
|
||||
*
|
||||
* @see AdbInstaller
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
interface AdbInstallerResult {
|
||||
/**
|
||||
* The result of installing an [Apk] successfully.
|
||||
*/
|
||||
object Success : AdbInstallerResult
|
||||
|
||||
/**
|
||||
* The result of installing an [Apk] unsuccessfully.
|
||||
*
|
||||
* @param exception The exception that caused the installation to fail.
|
||||
*/
|
||||
class Failure internal constructor(val exception: Exception) : AdbInstallerResult
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.command.AdbShellCommandRunner
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
|
||||
|
||||
/**
|
||||
* [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.
|
||||
*
|
||||
* @throws NoRootPermissionException If the device does not have root permission.
|
||||
*
|
||||
* @see RootInstaller
|
||||
* @see AdbShellCommandRunner
|
||||
*/
|
||||
class AdbRootInstaller(
|
||||
deviceSerial: String? = null,
|
||||
) : RootInstaller({ AdbShellCommandRunner(deviceSerial) }) {
|
||||
init {
|
||||
logger.fine("Connected to $deviceSerial")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
internal object Constants {
|
||||
const val PLACEHOLDER = "PLACEHOLDER"
|
||||
|
||||
const val TMP_FILE_PATH = "/data/local/tmp/revanced.tmp"
|
||||
const val MOUNT_PATH = "/data/adb/revanced/"
|
||||
const val MOUNTED_APK_PATH = "$MOUNT_PATH$PLACEHOLDER.apk"
|
||||
const val MOUNT_SCRIPT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
|
||||
|
||||
const val EXISTS = "[[ -f $PLACEHOLDER ]] || exit 1"
|
||||
const val MOUNT_GREP = "grep $PLACEHOLDER /proc/mounts"
|
||||
const val DELETE = "rm -rf $PLACEHOLDER"
|
||||
const val CREATE_DIR = "mkdir -p"
|
||||
const val RESTART = "am start -S $PLACEHOLDER"
|
||||
const val KILL = "am force-stop $PLACEHOLDER"
|
||||
const val INSTALLED_APK_PATH = "pm path $PLACEHOLDER"
|
||||
const val CREATE_INSTALLATION_PATH = "$CREATE_DIR $MOUNT_PATH"
|
||||
|
||||
const val MOUNT_APK =
|
||||
"base_path=\"$MOUNTED_APK_PATH\" && " +
|
||||
"mv $TMP_FILE_PATH \$base_path && " +
|
||||
"chmod 644 \$base_path && " +
|
||||
"chown system:system \$base_path && " +
|
||||
"chcon u:object_r:apk_data_file:s0 \$base_path"
|
||||
|
||||
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"
|
||||
|
||||
const val INSTALL_MOUNT_SCRIPT = "mv $TMP_FILE_PATH $MOUNT_SCRIPT_PATH && chmod +x $MOUNT_SCRIPT_PATH"
|
||||
|
||||
val MOUNT_SCRIPT =
|
||||
"""
|
||||
#!/system/bin/sh
|
||||
until [ "$( getprop sys.boot_completed )" = 1 ]; do sleep 3; done
|
||||
until [ -d "/sdcard/Android" ]; do sleep 1; done
|
||||
|
||||
stock_path=$( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
|
||||
|
||||
# Make sure the app is installed.
|
||||
if [ -z "${'$'}stock_path" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unmount any existing installations to prevent multiple unnecessary mounts.
|
||||
$UMOUNT
|
||||
|
||||
base_path="$MOUNTED_APK_PATH"
|
||||
|
||||
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
|
||||
|
||||
# Use Magisk mirror, if possible.
|
||||
if command -v magisk &> /dev/null; then
|
||||
MIRROR="${'$'}(magisk --path)/.magisk/mirror"
|
||||
fi
|
||||
|
||||
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
|
||||
|
||||
# Kill the app to force it to restart the mounted APK in case it's currently running.
|
||||
$KILL
|
||||
""".trimIndent()
|
||||
|
||||
/**
|
||||
* Replaces the [PLACEHOLDER] with the given [replacement].
|
||||
*
|
||||
* @param replacement The replacement to use.
|
||||
* @return The replaced string.
|
||||
*/
|
||||
operator fun String.invoke(replacement: String) = replace(PLACEHOLDER, replacement)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
/**
|
||||
* [Installation] of an apk file.
|
||||
*
|
||||
* @param apkFilePath The apk file path.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open class Installation internal constructor(
|
||||
val apkFilePath: String,
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import java.io.File
|
||||
import java.util.logging.Logger
|
||||
|
||||
/**
|
||||
* [Installer] for installing and uninstalling [Apk] files.
|
||||
*
|
||||
* @param TInstallerResult The type of the result of the installation.
|
||||
* @param TInstallation The type of the installation.
|
||||
*/
|
||||
abstract class Installer<TInstallerResult, TInstallation : Installation> internal constructor() {
|
||||
/**
|
||||
* The [Logger].
|
||||
*/
|
||||
protected val logger: Logger = Logger.getLogger(this::class.java.name)
|
||||
|
||||
/**
|
||||
* Installs the [Apk] file.
|
||||
*
|
||||
* @param apk The [Apk] file.
|
||||
*
|
||||
* @return The result of the installation.
|
||||
*/
|
||||
abstract suspend fun install(apk: Apk): TInstallerResult
|
||||
|
||||
/**
|
||||
* Uninstalls the package.
|
||||
*
|
||||
* @param packageName The package name.
|
||||
*
|
||||
* @return The result of the uninstallation.
|
||||
*/
|
||||
abstract suspend fun uninstall(packageName: String): TInstallerResult
|
||||
|
||||
/**
|
||||
* Gets the current installation or null if not installed.
|
||||
*
|
||||
* @param packageName The package name.
|
||||
*
|
||||
* @return The installation.
|
||||
*/
|
||||
abstract suspend fun getInstallation(packageName: String): TInstallation?
|
||||
|
||||
/**
|
||||
* Apk file for [Installer].
|
||||
*
|
||||
* @param file The [Apk] file.
|
||||
* @param packageName The package name of the [Apk] file.
|
||||
*/
|
||||
class Apk(val file: File, val packageName: String? = null)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
/**
|
||||
* [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 apkFilePath The mounting apk file path.
|
||||
* @param mounted Whether the apk is mounted to [installedApkFilePath].
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class RootInstallation internal constructor(
|
||||
val installedApkFilePath: String?,
|
||||
apkFilePath: String,
|
||||
val mounted: Boolean,
|
||||
) : Installation(apkFilePath)
|
||||
@@ -0,0 +1,135 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.command.ShellCommandRunner
|
||||
import app.revanced.library.installation.installer.Constants.CREATE_INSTALLATION_PATH
|
||||
import app.revanced.library.installation.installer.Constants.DELETE
|
||||
import app.revanced.library.installation.installer.Constants.EXISTS
|
||||
import app.revanced.library.installation.installer.Constants.INSTALLED_APK_PATH
|
||||
import app.revanced.library.installation.installer.Constants.INSTALL_MOUNT_SCRIPT
|
||||
import app.revanced.library.installation.installer.Constants.KILL
|
||||
import app.revanced.library.installation.installer.Constants.MOUNTED_APK_PATH
|
||||
import app.revanced.library.installation.installer.Constants.MOUNT_APK
|
||||
import app.revanced.library.installation.installer.Constants.MOUNT_GREP
|
||||
import app.revanced.library.installation.installer.Constants.MOUNT_SCRIPT
|
||||
import app.revanced.library.installation.installer.Constants.MOUNT_SCRIPT_PATH
|
||||
import app.revanced.library.installation.installer.Constants.RESTART
|
||||
import app.revanced.library.installation.installer.Constants.TMP_FILE_PATH
|
||||
import app.revanced.library.installation.installer.Constants.UMOUNT
|
||||
import app.revanced.library.installation.installer.Constants.invoke
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
import app.revanced.library.installation.installer.RootInstaller.NoRootPermissionException
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* [RootInstaller] for installing and uninstalling [Apk] files using root permissions by mounting.
|
||||
*
|
||||
* @param shellCommandRunnerSupplier A supplier for the [ShellCommandRunner] to use.
|
||||
*
|
||||
* @throws NoRootPermissionException If the device does not have root permission.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
abstract class RootInstaller internal constructor(
|
||||
shellCommandRunnerSupplier: (RootInstaller) -> ShellCommandRunner,
|
||||
) : Installer<RootInstallerResult, RootInstallation>() {
|
||||
|
||||
/**
|
||||
* The command runner used to run commands on the device.
|
||||
*/
|
||||
@Suppress("LeakingThis")
|
||||
protected val shellCommandRunner = shellCommandRunnerSupplier(this)
|
||||
|
||||
init {
|
||||
if (!shellCommandRunner.hasRootPermission()) throw NoRootPermissionException()
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given [apk] by mounting.
|
||||
*
|
||||
* @param apk The [Apk] to install.
|
||||
*
|
||||
* @throws PackageNameRequiredException If the [Apk] does not have a package name.
|
||||
*/
|
||||
override suspend fun install(apk: Apk): RootInstallerResult {
|
||||
logger.info("Installing ${apk.packageName} by mounting")
|
||||
|
||||
val packageName = apk.packageName?.also { it.assertInstalled() } ?: throw PackageNameRequiredException()
|
||||
|
||||
// Setup files.
|
||||
apk.file.move(TMP_FILE_PATH)
|
||||
CREATE_INSTALLATION_PATH().waitFor()
|
||||
MOUNT_APK(packageName)().waitFor()
|
||||
|
||||
// Install and run.
|
||||
TMP_FILE_PATH.write(MOUNT_SCRIPT(packageName))
|
||||
INSTALL_MOUNT_SCRIPT(packageName)().waitFor()
|
||||
MOUNT_SCRIPT_PATH(packageName)().waitFor()
|
||||
RESTART(packageName)()
|
||||
|
||||
DELETE(TMP_FILE_PATH)()
|
||||
|
||||
return RootInstallerResult.SUCCESS
|
||||
}
|
||||
|
||||
override suspend fun uninstall(packageName: String): RootInstallerResult {
|
||||
logger.info("Uninstalling $packageName by unmounting")
|
||||
|
||||
UMOUNT(packageName)()
|
||||
|
||||
DELETE(MOUNTED_APK_PATH)(packageName)()
|
||||
DELETE(MOUNT_SCRIPT_PATH)(packageName)()
|
||||
DELETE(TMP_FILE_PATH)() // Remove residual.
|
||||
|
||||
KILL(packageName)()
|
||||
|
||||
return RootInstallerResult.SUCCESS
|
||||
}
|
||||
|
||||
override suspend fun getInstallation(packageName: String): RootInstallation? {
|
||||
val patchedApkPath = MOUNTED_APK_PATH(packageName)
|
||||
|
||||
val patchedApkExists = EXISTS(patchedApkPath)().exitCode == 0
|
||||
if (patchedApkExists) return null
|
||||
|
||||
return RootInstallation(
|
||||
INSTALLED_APK_PATH(packageName)().output.ifEmpty { null },
|
||||
patchedApkPath,
|
||||
MOUNT_GREP(patchedApkPath)().exitCode == 0,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a command on the device.
|
||||
*/
|
||||
protected operator fun String.invoke() = shellCommandRunner(this)
|
||||
|
||||
/**
|
||||
* Moves the given file to the given [targetFilePath].
|
||||
*
|
||||
* @param targetFilePath The target file path.
|
||||
*/
|
||||
protected fun File.move(targetFilePath: String) = shellCommandRunner.move(this, targetFilePath)
|
||||
|
||||
/**
|
||||
* Writes the given [content] to the file.
|
||||
*
|
||||
* @param content The content of the file.
|
||||
*/
|
||||
protected fun String.write(content: String) = shellCommandRunner.write(content.byteInputStream(), this)
|
||||
|
||||
/**
|
||||
* Asserts that the package is installed.
|
||||
*
|
||||
* @throws FailedToFindInstalledPackageException If the package is not installed.
|
||||
*/
|
||||
private fun String.assertInstalled() {
|
||||
if (INSTALLED_APK_PATH(this)().output.isNotEmpty()) {
|
||||
throw FailedToFindInstalledPackageException(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal class FailedToFindInstalledPackageException internal constructor(packageName: String) :
|
||||
Exception("Failed to find installed package \"$packageName\" because no activity was found")
|
||||
|
||||
internal class PackageNameRequiredException internal constructor() : Exception("Package name is required")
|
||||
internal class NoRootPermissionException internal constructor() : Exception("No root permission")
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package app.revanced.library.installation.installer
|
||||
|
||||
import app.revanced.library.installation.installer.Installer.Apk
|
||||
|
||||
/**
|
||||
* The result of installing or uninstalling an [Apk] with root permissions using [RootInstaller].
|
||||
*
|
||||
* @see RootInstaller
|
||||
*/
|
||||
enum class RootInstallerResult {
|
||||
/**
|
||||
* The result of installing an [Apk] successfully.
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* The result of installing an [Apk] unsuccessfully.
|
||||
*/
|
||||
FAILURE,
|
||||
}
|
||||
@@ -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",
|
||||
)
|
||||
@@ -5,15 +5,17 @@ import java.util.logging.Level
|
||||
import java.util.logging.LogRecord
|
||||
import java.util.logging.SimpleFormatter
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||
object Logger {
|
||||
/**
|
||||
* Rules for allowed loggers.
|
||||
*/
|
||||
private val allowedLoggersRules =
|
||||
arrayOf<String.() -> Boolean>(
|
||||
{ startsWith("app.revanced") }, // ReVanced loggers.
|
||||
{ this == "" }, // Logs warnings when compiling resources (Logger in class brut.util.OS).
|
||||
// ReVanced loggers.
|
||||
{ startsWith("app.revanced") },
|
||||
// Logs warnings when compiling resources (Logger in class brut.util.OS).
|
||||
{ this == "" },
|
||||
)
|
||||
|
||||
private val rootLogger = java.util.logging.Logger.getLogger("")
|
||||
@@ -1,27 +1,19 @@
|
||||
package app.revanced.library
|
||||
|
||||
import app.revanced.patcher.PatchSet
|
||||
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 org.junit.jupiter.api.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import app.revanced.patcher.patch.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal object PatchUtilsTest {
|
||||
internal class MostCommonCompatibleVersionsTest {
|
||||
private val patches =
|
||||
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", "c"), 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.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("b")),
|
||||
newPatch("some.other.other.other.package", use = false),
|
||||
@@ -141,38 +133,24 @@ internal object PatchUtilsTest {
|
||||
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(
|
||||
expected: PackageNameMap,
|
||||
patches: PatchSet,
|
||||
patches: Set<Patch<*>>,
|
||||
compatiblePackageNames: Set<String>?,
|
||||
countUnusedPatches: Boolean = false,
|
||||
) = assertEquals(
|
||||
expected,
|
||||
PatchUtils.getMostCommonCompatibleVersions(patches, compatiblePackageNames, countUnusedPatches),
|
||||
patches.mostCommonCompatibleVersions(compatiblePackageNames, countUnusedPatches),
|
||||
)
|
||||
|
||||
private fun assertEqualsVersion(
|
||||
expected: String?,
|
||||
patches: PatchSet,
|
||||
patches: Set<Patch<*>>,
|
||||
compatiblePackageName: String,
|
||||
) {
|
||||
assertEquals(
|
||||
expected,
|
||||
PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName))
|
||||
patches.mostCommonCompatibleVersions(setOf(compatiblePackageName))
|
||||
.entries.firstOrNull()?.value?.keys?.firstOrNull(),
|
||||
)
|
||||
}
|
||||
@@ -181,19 +159,23 @@ internal object PatchUtilsTest {
|
||||
packageName: String,
|
||||
versions: Set<String>? = null,
|
||||
use: Boolean = true,
|
||||
options: Patch<*>.() -> Unit = {},
|
||||
) = object : BytecodePatch(
|
||||
options: PatchBuilder<*>.() -> Unit = {},
|
||||
) = bytecodePatch(
|
||||
name = "test",
|
||||
compatiblePackages = setOf(CompatiblePackage(packageName, versions?.toSet())),
|
||||
use = use,
|
||||
) {
|
||||
init {
|
||||
options()
|
||||
if (versions == null) {
|
||||
compatibleWith(packageName)
|
||||
} else {
|
||||
compatibleWith(
|
||||
if (versions.isEmpty()) {
|
||||
packageName()
|
||||
} else {
|
||||
packageName(*versions.toTypedArray())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {}
|
||||
|
||||
// Needed to make the patches unique.
|
||||
override fun equals(other: Any?) = false
|
||||
options()
|
||||
}
|
||||
}
|
||||
36
src/commonTest/kotlin/app/revanced/library/OptionsTest.kt
Normal file
36
src/commonTest/kotlin/app/revanced/library/OptionsTest.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package app.revanced.library
|
||||
|
||||
import app.revanced.patcher.patch.*
|
||||
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"]!!.jsonArray.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)
|
||||
}
|
||||
}
|
||||
@@ -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?)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package app.revanced.library.adb
|
||||
|
||||
import app.revanced.library.adb.AdbManager.Apk
|
||||
import app.revanced.library.adb.Constants.CREATE_DIR
|
||||
import app.revanced.library.adb.Constants.DELETE
|
||||
import app.revanced.library.adb.Constants.GET_INSTALLED_PATH
|
||||
import app.revanced.library.adb.Constants.INSTALLATION_PATH
|
||||
import app.revanced.library.adb.Constants.INSTALL_MOUNT_SCRIPT
|
||||
import app.revanced.library.adb.Constants.INSTALL_PATCHED_APK
|
||||
import app.revanced.library.adb.Constants.KILL
|
||||
import app.revanced.library.adb.Constants.MOUNT_SCRIPT
|
||||
import app.revanced.library.adb.Constants.MOUNT_SCRIPT_PATH
|
||||
import app.revanced.library.adb.Constants.PATCHED_APK_PATH
|
||||
import app.revanced.library.adb.Constants.PLACEHOLDER
|
||||
import app.revanced.library.adb.Constants.RESTART
|
||||
import app.revanced.library.adb.Constants.TMP_PATH
|
||||
import app.revanced.library.adb.Constants.UMOUNT
|
||||
import se.vidstige.jadb.JadbConnection
|
||||
import se.vidstige.jadb.JadbDevice
|
||||
import se.vidstige.jadb.managers.Package
|
||||
import se.vidstige.jadb.managers.PackageManager
|
||||
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. If null, the first connected device will be used.
|
||||
*/
|
||||
sealed class AdbManager private constructor(deviceSerial: String?) {
|
||||
protected val logger: Logger = Logger.getLogger(AdbManager::class.java.name)
|
||||
|
||||
protected val device =
|
||||
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}")
|
||||
}
|
||||
}!!
|
||||
|
||||
init {
|
||||
logger.fine("Connected to ${device.serial}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the [Apk] file.
|
||||
*
|
||||
* @param apk The [Apk] file.
|
||||
*/
|
||||
open fun install(apk: Apk) {
|
||||
logger.info("Finished installing ${apk.file.name}")
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls the package.
|
||||
*
|
||||
* @param packageName The package name.
|
||||
*/
|
||||
open fun uninstall(packageName: String) {
|
||||
logger.info("Finished uninstalling $packageName")
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
class RootAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) {
|
||||
init {
|
||||
if (!device.hasSu()) throw IllegalArgumentException("Root required on ${device.serial}. Task failed")
|
||||
}
|
||||
|
||||
override fun install(apk: Apk) {
|
||||
logger.info("Installing by mounting")
|
||||
|
||||
val packageName = apk.packageName ?: throw PackageNameRequiredException()
|
||||
|
||||
device.run(GET_INSTALLED_PATH, packageName).inputStream.bufferedReader().readLine().let { line ->
|
||||
if (line != null) return@let
|
||||
throw throw FailedToFindInstalledPackageException(packageName)
|
||||
}
|
||||
|
||||
device.push(apk.file, TMP_PATH)
|
||||
|
||||
device.run("$CREATE_DIR $INSTALLATION_PATH").waitFor()
|
||||
device.run(INSTALL_PATCHED_APK, packageName).waitFor()
|
||||
|
||||
device.createFile(TMP_PATH, MOUNT_SCRIPT.applyReplacement(packageName))
|
||||
|
||||
device.run(INSTALL_MOUNT_SCRIPT, packageName).waitFor()
|
||||
device.run(MOUNT_SCRIPT_PATH, packageName).waitFor()
|
||||
device.run(RESTART, packageName)
|
||||
device.run(DELETE, TMP_PATH)
|
||||
|
||||
super.install(apk)
|
||||
}
|
||||
|
||||
override fun uninstall(packageName: String) {
|
||||
logger.info("Uninstalling $packageName by unmounting")
|
||||
|
||||
device.run(UMOUNT, packageName)
|
||||
device.run(DELETE.applyReplacement(PATCHED_APK_PATH), packageName)
|
||||
device.run(DELETE, MOUNT_SCRIPT_PATH.applyReplacement(packageName))
|
||||
device.run(DELETE, TMP_PATH)
|
||||
device.run(KILL, packageName)
|
||||
|
||||
super.uninstall(packageName)
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
class UserAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) {
|
||||
private val packageManager = PackageManager(device)
|
||||
|
||||
override fun install(apk: Apk) {
|
||||
logger.info("Installing ${apk.file.name}")
|
||||
|
||||
PackageManager(device).install(apk.file)
|
||||
|
||||
super.install(apk)
|
||||
}
|
||||
|
||||
override fun uninstall(packageName: String) {
|
||||
logger.info("Uninstalling $packageName")
|
||||
|
||||
packageManager.uninstall(Package(packageName))
|
||||
|
||||
super.uninstall(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apk file for [AdbManager].
|
||||
*
|
||||
* @param file The [Apk] file.
|
||||
* @param packageName The package name of the [Apk] file.
|
||||
*/
|
||||
class Apk(val file: File, val packageName: String? = null)
|
||||
|
||||
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",
|
||||
)
|
||||
|
||||
class FailedToFindInstalledPackageException internal constructor(packageName: String) :
|
||||
Exception("Failed to find installed package \"$packageName\" because no activity was found")
|
||||
|
||||
class PackageNameRequiredException internal constructor() :
|
||||
Exception("Package name is required")
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package app.revanced.library.adb
|
||||
|
||||
import se.vidstige.jadb.JadbDevice
|
||||
import se.vidstige.jadb.RemoteFile
|
||||
import se.vidstige.jadb.ShellProcessBuilder
|
||||
import java.io.File
|
||||
|
||||
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,
|
||||
) = this.buildCommand(command, su).start()
|
||||
|
||||
internal fun JadbDevice.hasSu() = this.run("whoami", true).waitFor() == 0
|
||||
|
||||
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))
|
||||
@@ -1,54 +0,0 @@
|
||||
package app.revanced.library.adb
|
||||
|
||||
internal object Constants {
|
||||
internal const val PLACEHOLDER = "PLACEHOLDER"
|
||||
|
||||
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_SCRIPT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
|
||||
|
||||
internal const val DELETE = "rm -rf $PLACEHOLDER"
|
||||
internal const val CREATE_DIR = "mkdir -p"
|
||||
internal const val RESTART = "am start -S $PLACEHOLDER"
|
||||
internal const val KILL = "am force-stop $PLACEHOLDER"
|
||||
internal const val GET_INSTALLED_PATH = "pm path $PLACEHOLDER"
|
||||
|
||||
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 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 INSTALL_MOUNT_SCRIPT = "mv $TMP_PATH $MOUNT_SCRIPT_PATH && chmod +x $MOUNT_SCRIPT_PATH"
|
||||
|
||||
internal val MOUNT_SCRIPT =
|
||||
"""
|
||||
#!/system/bin/sh
|
||||
|
||||
# Use Magisk mirror, if possible.
|
||||
if command -v magisk &> /dev/null; then
|
||||
MIRROR="${'$'}(magisk --path)/.magisk/mirror"
|
||||
fi
|
||||
|
||||
# Wait for the system to boot.
|
||||
until [ "$( getprop sys.boot_completed )" = 1 ]; do sleep 3; done
|
||||
until [ -d "/sdcard/Android" ]; do sleep 1; done
|
||||
|
||||
# Unmount any existing mount as a safety measure.
|
||||
$UMOUNT
|
||||
|
||||
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
|
||||
|
||||
# Kill the app to force it to restart the mounted APK in case it's currently running.
|
||||
$KILL
|
||||
""".trimIndent()
|
||||
}
|
||||
@@ -1,48 +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 org.junit.jupiter.api.MethodOrderer
|
||||
import org.junit.jupiter.api.Order
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestMethodOrder
|
||||
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
|
||||
internal object PatchOptionsTest {
|
||||
private var patches = setOf(PatchOptionsTestPatch)
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
fun serializeTest() {
|
||||
assert(SERIALIZED_JSON == Options.serialize(patches))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
fun loadOptionsTest() {
|
||||
patches.setOptions(CHANGED_JSON)
|
||||
|
||||
assert(PatchOptionsTestPatch.option1 == "test")
|
||||
assert(PatchOptionsTestPatch.option2 == false)
|
||||
}
|
||||
|
||||
private const val SERIALIZED_JSON =
|
||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":null},{\"key\":\"key2\",\"value\":true}]}]"
|
||||
|
||||
private const val CHANGED_JSON =
|
||||
"[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2\",\"value\":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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user