From 43d655aea5d86288ae9916630e0f30de219d5cfb Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 7 Apr 2024 18:30:43 +0200 Subject: [PATCH 01/11] feat: Add local Android installer (#25) --- .github/workflows/build_pull_request.yml | 3 + .github/workflows/release.yml | 3 + .gitignore | 123 +------ api/android/revanced-library.api | 337 ++++++++++++++++++ api/jvm/revanced-library.api | 283 +++++++++++++++ build.gradle.kts | 87 +++-- gradle.properties | 16 +- gradle/libs.versions.toml | 19 +- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 59203 bytes gradlew | 281 ++++++--------- gradlew.bat | 15 +- settings.gradle.kts | 10 +- .../ILocalShellCommandRunnerRootService.aidl | 5 + .../command/LocalShellCommandRunner.kt | 88 +++++ .../LocalShellCommandRunnerRootService.kt | 15 + .../installation/installer/LocalInstaller.kt | 104 ++++++ .../installer/LocalInstallerResult.kt | 15 + .../installer/LocalInstallerService.kt | 57 +++ .../installer/LocalRootInstaller.kt | 34 ++ .../kotlin/app/revanced/library/ApkSigner.kt | 0 .../kotlin/app/revanced/library/ApkUtils.kt | 0 .../kotlin/app/revanced/library/Commands.kt | 43 +++ .../kotlin/app/revanced/library/Options.kt | 0 .../kotlin/app/revanced/library/PatchUtils.kt | 0 .../kotlin/app/revanced/library/Utils.kt | 18 + .../app/revanced/library/adb/AdbManager.kt | 144 ++++++++ .../command/AdbShellCommandRunner.kt | 59 +++ .../library/installation/command/RunResult.kt | 26 ++ .../command/ShellCommandRunner.kt | 59 +++ .../installation/installer/AdbInstaller.kt | 51 +++ .../installer/AdbInstallerResult.kt | 23 ++ .../installer/AdbRootInstaller.kt | 23 ++ .../installation/installer/Constants.kt | 75 ++++ .../installation/installer/Installation.kt | 11 + .../installation/installer/Installer.kt | 53 +++ .../installer/RootInstallation.kt | 15 + .../installation/installer/RootInstaller.kt | 135 +++++++ .../installer/RootInstallerResult.kt | 20 ++ .../library/installation/installer/Utils.kt | 40 +++ .../app/revanced/library/logging/Logger.kt | 8 +- .../app/revanced/library/PatchOptionsTest.kt | 33 +- .../app/revanced/library/PatchUtilsTest.kt | 4 +- .../app/revanced/library/adb/AdbManager.kt | 183 ---------- .../app/revanced/library/adb/Commands.kt | 35 -- .../app/revanced/library/adb/Constants.kt | 54 --- 45 files changed, 1976 insertions(+), 631 deletions(-) create mode 100644 api/android/revanced-library.api create mode 100644 api/jvm/revanced-library.api create mode 100644 src/androidMain/aidl/app/revanced/library/installation/command/ILocalShellCommandRunnerRootService.aidl create mode 100644 src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunner.kt create mode 100644 src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunnerRootService.kt create mode 100644 src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstaller.kt create mode 100644 src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerResult.kt create mode 100644 src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerService.kt create mode 100644 src/androidMain/kotlin/app/revanced/library/installation/installer/LocalRootInstaller.kt rename src/{main => commonMain}/kotlin/app/revanced/library/ApkSigner.kt (100%) rename src/{main => commonMain}/kotlin/app/revanced/library/ApkUtils.kt (100%) create mode 100644 src/commonMain/kotlin/app/revanced/library/Commands.kt rename src/{main => commonMain}/kotlin/app/revanced/library/Options.kt (100%) rename src/{main => commonMain}/kotlin/app/revanced/library/PatchUtils.kt (100%) create mode 100644 src/commonMain/kotlin/app/revanced/library/Utils.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/adb/AdbManager.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/command/RunResult.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/command/ShellCommandRunner.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstallerResult.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/AdbRootInstaller.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/Constants.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/Installation.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/Installer.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallation.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstaller.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallerResult.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt rename src/{main => commonMain}/kotlin/app/revanced/library/logging/Logger.kt (91%) rename src/{test => commonTest}/kotlin/app/revanced/library/PatchOptionsTest.kt (61%) rename src/{test => commonTest}/kotlin/app/revanced/library/PatchUtilsTest.kt (99%) delete mode 100644 src/main/kotlin/app/revanced/library/adb/AdbManager.kt delete mode 100644 src/main/kotlin/app/revanced/library/adb/Commands.kt delete mode 100644 src/main/kotlin/app/revanced/library/adb/Constants.kt diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 250871b..cab9162 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -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 }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2cb2b20..edb9d1b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 }} diff --git a/.gitignore b/.gitignore index 8e77bd2..83f36ae 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ \ No newline at end of file diff --git a/api/android/revanced-library.api b/api/android/revanced-library.api new file mode 100644 index 0000000..9f89984 --- /dev/null +++ b/api/android/revanced-library.api @@ -0,0 +1,337 @@ +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 (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 (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 (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (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 ()V + public fun (Ljava/lang/String;Ljava/util/Date;)V + public synthetic fun (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 (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getAlias ()Ljava/lang/String; + public final fun getKeyStore ()Ljava/io/File; + public final fun getKeyStorePassword ()Ljava/lang/String; + public final fun getPassword ()Ljava/lang/String; + public final fun getSigner ()Ljava/lang/String; +} + +public final class app/revanced/library/Options { + public static final field INSTANCE Lapp/revanced/library/Options; + public final fun deserialize (Ljava/lang/String;)[Lapp/revanced/library/Options$Patch; + public final fun serialize (Ljava/util/Set;Z)Ljava/lang/String; + public static synthetic fun serialize$default (Lapp/revanced/library/Options;Ljava/util/Set;ZILjava/lang/Object;)Ljava/lang/String; + public final fun setOptions (Ljava/util/Set;Ljava/io/File;)V + public final fun setOptions (Ljava/util/Set;Ljava/lang/String;)V +} + +public final class app/revanced/library/Options$Patch { + public final fun getOptions ()Ljava/util/List; + public final fun getPatchName ()Ljava/lang/String; +} + +public final class app/revanced/library/Options$Patch$Option { + public final fun getKey ()Ljava/lang/String; + public final fun getValue ()Ljava/lang/Object; +} + +public final class app/revanced/library/PatchUtils { + public static final field INSTANCE Lapp/revanced/library/PatchUtils; + public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map; + public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map; +} + +public final class app/revanced/library/PatchUtils$Json { + public static final field INSTANCE Lapp/revanced/library/PatchUtils$Json; + public final fun deserialize (Ljava/io/InputStream;Ljava/lang/Class;)Ljava/util/Set; + public final fun serialize (Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;)V + public static synthetic fun serialize$default (Lapp/revanced/library/PatchUtils$Json;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;ILjava/lang/Object;)V +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch : app/revanced/library/PatchUtils$Json$JsonPatch { + public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$Companion; + public final fun getCompatiblePackages ()Ljava/util/Set; + public final fun getDependencies ()Ljava/util/Set; + public final fun getDescription ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getOptions ()Ljava/util/Map; + public final fun getRequiresIntegrations ()Z + public final fun getUse ()Z + public final fun setRequiresIntegrations (Z)V +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$Companion { + public final fun fromPatch (Lapp/revanced/patcher/patch/Patch;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch; +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption { + public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion; + public final fun getDefault ()Ljava/lang/Object; + public final fun getDescription ()Ljava/lang/String; + public final fun getKey ()Ljava/lang/String; + public final fun getRequired ()Z + public final fun getTitle ()Ljava/lang/String; + public final fun getValueType ()Ljava/lang/String; + public final fun getValues ()Ljava/util/Map; +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion { + public final fun fromPatchOption (Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption; +} + +public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { +} + +public final class app/revanced/library/Utils { + public static final field INSTANCE Lapp/revanced/library/Utils; + public final fun isAndroidEnvironment ()Z +} + +public abstract class app/revanced/library/adb/AdbManager { + public static final field Companion Lapp/revanced/library/adb/AdbManager$Companion; + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + protected abstract fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; + public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; + public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; +} + +public final class app/revanced/library/adb/AdbManager$Apk { + public fun (Ljava/io/File;Ljava/lang/String;)V + public synthetic fun (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 ()V +} + +public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception { +} + +public final class app/revanced/library/adb/AdbManager$PackageNameRequiredException : java/lang/Exception { +} + +public final class app/revanced/library/adb/AdbManager$RootAdbManager : app/revanced/library/adb/AdbManager { + public static final field Utils Lapp/revanced/library/adb/AdbManager$RootAdbManager$Utils; + public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; + public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; + public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; +} + +public final class app/revanced/library/adb/AdbManager$RootAdbManager$Utils { +} + +public final class app/revanced/library/adb/AdbManager$UserAdbManager : app/revanced/library/adb/AdbManager { + public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; + public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; + public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; +} + +public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner { +} + +public abstract interface class app/revanced/library/installation/command/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 ()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 ()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 ()V + public fun (Ljava/lang/String;)V + public synthetic fun (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 ()V + public fun (Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +} + +public class app/revanced/library/installation/installer/Installation { + public final fun getApkFilePath ()Ljava/lang/String; +} + +public abstract class app/revanced/library/installation/installer/Installer { + public abstract fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + protected final fun getLogger ()Ljava/util/logging/Logger; + public abstract fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class app/revanced/library/installation/installer/Installer$Apk { + public fun (Ljava/io/File;Ljava/lang/String;)V + public synthetic fun (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 (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 ()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 (Landroid/content/Context;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (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 +} + diff --git a/api/jvm/revanced-library.api b/api/jvm/revanced-library.api new file mode 100644 index 0000000..5a525d9 --- /dev/null +++ b/api/jvm/revanced-library.api @@ -0,0 +1,283 @@ +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 (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 (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 (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (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 ()V + public fun (Ljava/lang/String;Ljava/util/Date;)V + public synthetic fun (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 (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getAlias ()Ljava/lang/String; + public final fun getKeyStore ()Ljava/io/File; + public final fun getKeyStorePassword ()Ljava/lang/String; + public final fun getPassword ()Ljava/lang/String; + public final fun getSigner ()Ljava/lang/String; +} + +public final class app/revanced/library/Options { + public static final field INSTANCE Lapp/revanced/library/Options; + public final fun deserialize (Ljava/lang/String;)[Lapp/revanced/library/Options$Patch; + public final fun serialize (Ljava/util/Set;Z)Ljava/lang/String; + public static synthetic fun serialize$default (Lapp/revanced/library/Options;Ljava/util/Set;ZILjava/lang/Object;)Ljava/lang/String; + public final fun setOptions (Ljava/util/Set;Ljava/io/File;)V + public final fun setOptions (Ljava/util/Set;Ljava/lang/String;)V +} + +public final class app/revanced/library/Options$Patch { + public final fun getOptions ()Ljava/util/List; + public final fun getPatchName ()Ljava/lang/String; +} + +public final class app/revanced/library/Options$Patch$Option { + public final fun getKey ()Ljava/lang/String; + public final fun getValue ()Ljava/lang/Object; +} + +public final class app/revanced/library/PatchUtils { + public static final field INSTANCE Lapp/revanced/library/PatchUtils; + public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map; + public static synthetic fun getMostCommonCompatibleVersions$default (Lapp/revanced/library/PatchUtils;Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map; +} + +public final class app/revanced/library/PatchUtils$Json { + public static final field INSTANCE Lapp/revanced/library/PatchUtils$Json; + public final fun deserialize (Ljava/io/InputStream;Ljava/lang/Class;)Ljava/util/Set; + public final fun serialize (Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;)V + public static synthetic fun serialize$default (Lapp/revanced/library/PatchUtils$Json;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ZLjava/io/OutputStream;ILjava/lang/Object;)V +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch : app/revanced/library/PatchUtils$Json$JsonPatch { + public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$Companion; + public final fun getCompatiblePackages ()Ljava/util/Set; + public final fun getDependencies ()Ljava/util/Set; + public final fun getDescription ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getOptions ()Ljava/util/Map; + public final fun getRequiresIntegrations ()Z + public final fun getUse ()Z + public final fun setRequiresIntegrations (Z)V +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$Companion { + public final fun fromPatch (Lapp/revanced/patcher/patch/Patch;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch; +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption { + public static final field Companion Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion; + public final fun getDefault ()Ljava/lang/Object; + public final fun getDescription ()Ljava/lang/String; + public final fun getKey ()Ljava/lang/String; + public final fun getRequired ()Z + public final fun getTitle ()Ljava/lang/String; + public final fun getValueType ()Ljava/lang/String; + public final fun getValues ()Ljava/util/Map; +} + +public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption$Companion { + public final fun fromPatchOption (Lapp/revanced/patcher/patch/options/PatchOption;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption; +} + +public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { +} + +public final class app/revanced/library/Utils { + public static final field INSTANCE Lapp/revanced/library/Utils; + public final fun isAndroidEnvironment ()Z +} + +public abstract class app/revanced/library/adb/AdbManager { + public static final field Companion Lapp/revanced/library/adb/AdbManager$Companion; + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + protected abstract fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; + public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; + public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; +} + +public final class app/revanced/library/adb/AdbManager$Apk { + public fun (Ljava/io/File;Ljava/lang/String;)V + public synthetic fun (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 ()V +} + +public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception { +} + +public final class app/revanced/library/adb/AdbManager$PackageNameRequiredException : java/lang/Exception { +} + +public final class app/revanced/library/adb/AdbManager$RootAdbManager : app/revanced/library/adb/AdbManager { + public static final field Utils Lapp/revanced/library/adb/AdbManager$RootAdbManager$Utils; + public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; + public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; + public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; +} + +public final class app/revanced/library/adb/AdbManager$RootAdbManager$Utils { +} + +public final class app/revanced/library/adb/AdbManager$UserAdbManager : app/revanced/library/adb/AdbManager { + public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; + public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; + public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; +} + +public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner { +} + +public abstract interface class app/revanced/library/installation/command/RunResult { + public abstract fun getError ()Ljava/lang/String; + public abstract fun getExitCode ()I + public abstract fun getOutput ()Ljava/lang/String; + public abstract fun waitFor ()V +} + +public final class app/revanced/library/installation/command/RunResult$DefaultImpls { + public static fun waitFor (Lapp/revanced/library/installation/command/RunResult;)V +} + +public abstract class app/revanced/library/installation/command/ShellCommandRunner { + protected final fun getLogger ()Ljava/util/logging/Logger; + protected abstract fun runCommand (Ljava/lang/String;)Lapp/revanced/library/installation/command/RunResult; +} + +public final class app/revanced/library/installation/installer/AdbInstaller : app/revanced/library/installation/installer/Installer { + public fun ()V + public fun (Ljava/lang/String;)V + public synthetic fun (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 ()V + public fun (Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +} + +public class app/revanced/library/installation/installer/Installation { + public final fun getApkFilePath ()Ljava/lang/String; +} + +public abstract class app/revanced/library/installation/installer/Installer { + public abstract fun getInstallation (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + protected final fun getLogger ()Ljava/util/logging/Logger; + public abstract fun install (Lapp/revanced/library/installation/installer/Installer$Apk;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun uninstall (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class app/revanced/library/installation/installer/Installer$Apk { + public fun (Ljava/io/File;Ljava/lang/String;)V + public synthetic fun (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 +} + diff --git a/build.gradle.kts b/build.gradle.kts index 0de3754..643dd26 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,6 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - alias(libs.plugins.kotlin) + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.android.library) alias(libs.plugins.binary.compatibility.validator) `maven-publish` signing @@ -9,53 +8,87 @@ plugins { 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 { + compilations.all { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + } + } - testImplementation(libs.revanced.patcher) - testImplementation(libs.kotlin.test) -} + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + } -tasks { - test { - useJUnitPlatform() - testLogging { - events("PASSED", "SKIPPED", "FAILED") + publishLibraryVariants("release") + } + + sourceSets { + androidMain.dependencies { + implementation(libs.libsu.nio) + implementation(libs.libsu.service) + implementation(libs.core.ktx) + } + + commonMain.dependencies { + implementation(libs.revanced.patcher) + implementation(libs.kotlin.reflect) + implementation(libs.jadb) // Fork with Shell v2 support. + implementation(libs.bcpkix.jdk15on) + implementation(libs.jackson.module.kotlin) + implementation(libs.apkzlib) + implementation(libs.apksig) + implementation(libs.guava) + } + + commonTest.dependencies { + implementation(libs.revanced.patcher) + implementation(libs.kotlin.test.junit) } } } -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 { @@ -72,8 +105,6 @@ publishing { publications { create("revanced-library-publication") { - from(components["java"]) - version = project.version.toString() pom { diff --git a/gradle.properties b/gradle.properties index abd574e..1e54a84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,14 @@ -org.gradle.parallel = true -org.gradle.caching = true -kotlin.code.style = official version = 2.3.0 + +#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 + +#Android +android.useAndroidX=true +android.nonTransitiveRClass=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 04a06fc..03a7a0b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,22 +4,29 @@ 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.3.0" bcpkix-jdk15on = "1.70" guava = "33.0.0-jre" -apksig = "8.3.0" +libsu = "5.2.2" +core-ktx = "1.12.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" } +kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } -apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "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" } guava = { module = "com.google.guava:guava", version.ref = "guava" } -apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" } +libsu-core = { module = "com.github.topjohnwu.libsu:core", version.ref = "libsu" } +libsu-nio = { module = "com.github.topjohnwu.libsu:nio", version.ref = "libsu" } +libsu-service = { module = "com.github.topjohnwu.libsu:service", version.ref = "libsu" } +core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } [plugins] binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } -kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +android-library = { id = "com.android.library", version.ref = "android" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 39466 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 delta 43784 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 diff --git a/gradlew b/gradlew index fcb6fca..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/bin/sh +#!/usr/bin/env sh # -# Copyright © 2015-2021 the original authors. +# Copyright 2015 the original author or authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,98 +17,67 @@ # ############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# +## +## Gradle start up script for UN*X +## ############################################################################## # Attempt to set APP_HOME - # Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# 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"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum +MAX_FD="maximum" warn () { echo "$*" -} >&2 +} die () { echo echo "$*" echo exit 1 -} >&2 +} # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -118,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java + JAVACMD="$JAVA_HOME/jre/sh/java" else - JAVACMD=$JAVA_HOME/bin/java + JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -129,120 +98,88 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." - fi fi # Increase the maximum file descriptors if we can. -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 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi fi -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi # For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=$( cygpath --unix "$JAVACMD" ) + JAVACMD=`cygpath --unix "$JAVACMD"` - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac fi +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` -# 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. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..107acd3 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%"=="" @echo off +@if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,8 +25,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused +if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd +if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle.kts b/settings.gradle.kts index 1df3126..e164b40 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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() } } diff --git a/src/androidMain/aidl/app/revanced/library/installation/command/ILocalShellCommandRunnerRootService.aidl b/src/androidMain/aidl/app/revanced/library/installation/command/ILocalShellCommandRunnerRootService.aidl new file mode 100644 index 0000000..39861ec --- /dev/null +++ b/src/androidMain/aidl/app/revanced/library/installation/command/ILocalShellCommandRunnerRootService.aidl @@ -0,0 +1,5 @@ +package app.revanced.library.installation.command; + +interface ILocalShellCommandRunnerRootService { + IBinder getFileSystemService(); +} diff --git a/src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunner.kt b/src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunner.kt new file mode 100644 index 0000000..f757ba3 --- /dev/null +++ b/src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunner.kt @@ -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) +} diff --git a/src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunnerRootService.kt b/src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunnerRootService.kt new file mode 100644 index 0000000..e221257 --- /dev/null +++ b/src/androidMain/kotlin/app/revanced/library/installation/command/LocalShellCommandRunnerRootService.kt @@ -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() + } +} diff --git a/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstaller.kt b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstaller.kt new file mode 100644 index 0000000..cb3bf48 --- /dev/null +++ b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstaller.kt @@ -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(), 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) + } + } + } + } +} diff --git a/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerResult.kt b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerResult.kt new file mode 100644 index 0000000..a326181 --- /dev/null +++ b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerResult.kt @@ -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) diff --git a/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerService.kt b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerService.kt new file mode 100644 index 0000000..dc15569 --- /dev/null +++ b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalInstallerService.kt @@ -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" + } +} diff --git a/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalRootInstaller.kt b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalRootInstaller.kt new file mode 100644 index 0000000..4247e83 --- /dev/null +++ b/src/androidMain/kotlin/app/revanced/library/installation/installer/LocalRootInstaller.kt @@ -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() +} diff --git a/src/main/kotlin/app/revanced/library/ApkSigner.kt b/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt similarity index 100% rename from src/main/kotlin/app/revanced/library/ApkSigner.kt rename to src/commonMain/kotlin/app/revanced/library/ApkSigner.kt diff --git a/src/main/kotlin/app/revanced/library/ApkUtils.kt b/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt similarity index 100% rename from src/main/kotlin/app/revanced/library/ApkUtils.kt rename to src/commonMain/kotlin/app/revanced/library/ApkUtils.kt diff --git a/src/commonMain/kotlin/app/revanced/library/Commands.kt b/src/commonMain/kotlin/app/revanced/library/Commands.kt new file mode 100644 index 0000000..3f25439 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/Commands.kt @@ -0,0 +1,43 @@ +@file:Suppress("DeprecatedCallableAddReplaceWith") + +package app.revanced.library + +import app.revanced.library.installation.command.AdbShellCommandRunner +import se.vidstige.jadb.JadbDevice +import se.vidstige.jadb.ShellProcessBuilder +import java.io.File + +@Deprecated("Do not use this anymore. Instead use AdbCommandRunner.") +internal fun JadbDevice.buildCommand( + command: String, + su: Boolean = true, +): ShellProcessBuilder { + if (su) return shellProcessBuilder("su -c \'$command\'") + + val args = command.split(" ") as ArrayList + val cmd = args.removeFirst() + + return shellProcessBuilder(cmd, *args.toTypedArray()) +} + +@Suppress("DEPRECATION") +@Deprecated("Use AdbShellCommandRunner instead.") +internal fun JadbDevice.run( + command: String, + su: Boolean = true, +) = buildCommand(command, su).start() + +@Deprecated("Use AdbShellCommandRunner instead.") +internal fun JadbDevice.hasSu() = AdbShellCommandRunner(this).hasRootPermission() + +@Deprecated("Use AdbShellCommandRunner instead.") +internal fun JadbDevice.push( + file: File, + targetFilePath: String, +) = AdbShellCommandRunner(this).move(file, targetFilePath) + +@Deprecated("Use AdbShellCommandRunner instead.") +internal fun JadbDevice.createFile( + targetFile: String, + content: String, +) = AdbShellCommandRunner(this).write(content.byteInputStream(), targetFile) diff --git a/src/main/kotlin/app/revanced/library/Options.kt b/src/commonMain/kotlin/app/revanced/library/Options.kt similarity index 100% rename from src/main/kotlin/app/revanced/library/Options.kt rename to src/commonMain/kotlin/app/revanced/library/Options.kt diff --git a/src/main/kotlin/app/revanced/library/PatchUtils.kt b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt similarity index 100% rename from src/main/kotlin/app/revanced/library/PatchUtils.kt rename to src/commonMain/kotlin/app/revanced/library/PatchUtils.kt diff --git a/src/commonMain/kotlin/app/revanced/library/Utils.kt b/src/commonMain/kotlin/app/revanced/library/Utils.kt new file mode 100644 index 0000000..557831f --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/Utils.kt @@ -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 + } +} diff --git a/src/commonMain/kotlin/app/revanced/library/adb/AdbManager.kt b/src/commonMain/kotlin/app/revanced/library/adb/AdbManager.kt new file mode 100644 index 0000000..1eefdd0 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/adb/AdbManager.kt @@ -0,0 +1,144 @@ +@file:Suppress("DEPRECATION") + +package app.revanced.library.adb + +import app.revanced.library.adb.AdbManager.Apk +import app.revanced.library.installation.installer.AdbInstaller +import app.revanced.library.installation.installer.AdbRootInstaller +import app.revanced.library.installation.installer.Constants.PLACEHOLDER +import app.revanced.library.installation.installer.Installer +import app.revanced.library.run +import se.vidstige.jadb.JadbDevice +import java.io.File + +/** + * [AdbManager] to install and uninstall [Apk] files. + * + * @param deviceSerial The serial of the device. If null, the first connected device will be used. + */ +@Deprecated("Use an implementation of Installer instead.") +@Suppress("unused") +sealed class AdbManager private constructor( + @Suppress("UNUSED_PARAMETER") deviceSerial: String?, +) { + protected abstract val installer: Installer<*, *> + + /** + * Installs the [Apk] file. + * + * @param apk The [Apk] file. + */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use Installer.install instead.") + open fun install(apk: Apk) = suspend { + installer.install(Installer.Apk(apk.file, apk.packageName)) + } + + /** + * Uninstalls the package. + * + * @param packageName The package name. + */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use Installer.uninstall instead.") + open fun uninstall(packageName: String) = suspend { + installer.uninstall(packageName) + } + + @Deprecated("Use Installer instead.") + companion object { + /** + * Gets an [AdbManager] for the supplied device serial. + * + * @param deviceSerial The device serial. If null, the first connected device will be used. + * @param root Whether to use root or not. + * @return The [AdbManager]. + * @throws DeviceNotFoundException If the device can not be found. + */ + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("This is deprecated.") + fun getAdbManager( + deviceSerial: String? = null, + root: Boolean = false, + ): AdbManager = if (root) RootAdbManager(deviceSerial) else UserAdbManager(deviceSerial) + } + + /** + * Adb manager for rooted devices. + * + * @param deviceSerial The device serial. If null, the first connected device will be used. + */ + @Deprecated("Use AdbRootInstaller instead.", ReplaceWith("AdbRootInstaller(deviceSerial)")) + class RootAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) { + override val installer = AdbRootInstaller(deviceSerial) + + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use AdbRootInstaller.install instead.") + override fun install(apk: Apk) = suspend { + installer.install(Installer.Apk(apk.file, apk.packageName)) + } + + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use AdbRootInstaller.uninstall instead.") + override fun uninstall(packageName: String) = suspend { + installer.uninstall(packageName) + } + + @Deprecated("This is deprecated.") + companion object Utils { + private fun JadbDevice.run( + command: String, + with: String, + ) = run(command.applyReplacement(with)) + + private fun String.applyReplacement(with: String) = replace(PLACEHOLDER, with) + } + } + + /** + * Adb manager for non-rooted devices. + * + * @param deviceSerial The device serial. If null, the first connected device will be used. + */ + @Deprecated("Use AdbInstaller instead.") + class UserAdbManager internal constructor(deviceSerial: String?) : AdbManager(deviceSerial) { + override val installer = AdbInstaller(deviceSerial) + + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use AdbInstaller.install instead.") + override fun install(apk: Apk) = suspend { + installer.install(Installer.Apk(apk.file, apk.packageName)) + } + + @Suppress("DeprecatedCallableAddReplaceWith") + @Deprecated("Use AdbInstaller.uninstall instead.") + override fun uninstall(packageName: String) = suspend { + installer.uninstall(packageName) + } + } + + /** + * Apk file for [AdbManager]. + * + * @param file The [Apk] file. + * @param packageName The package name of the [Apk] file. + */ + @Deprecated("Use Installer.Apk instead.") + class Apk(val file: File, val packageName: String? = null) + + @Deprecated("Use AdbCommandRunner.DeviceNotFoundException instead.") + class DeviceNotFoundException internal constructor(deviceSerial: String? = null) : + Exception( + deviceSerial?.let { + "The device with the ADB device serial \"$deviceSerial\" can not be found" + } ?: "No ADB device found", + ) + + @Deprecated("Use RootInstaller.FailedToFindInstalledPackageException instead.") + class FailedToFindInstalledPackageException internal constructor(packageName: String) : + Exception("Failed to find installed package \"$packageName\" because no activity was found") + + @Deprecated("Use RootInstaller.PackageNameRequiredException instead.") + class PackageNameRequiredException internal constructor() : + Exception("Package name is required") +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt b/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt new file mode 100644 index 0000000..df60d1b --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt @@ -0,0 +1,59 @@ +package app.revanced.library.installation.command + +import app.revanced.library.installation.installer.Utils +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 = Utils.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)) +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/command/RunResult.kt b/src/commonMain/kotlin/app/revanced/library/installation/command/RunResult.kt new file mode 100644 index 0000000..c5b0183 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/command/RunResult.kt @@ -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() {} +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/command/ShellCommandRunner.kt b/src/commonMain/kotlin/app/revanced/library/installation/command/ShellCommandRunner.kt new file mode 100644 index 0000000..554ac0d --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/command/ShellCommandRunner.kt @@ -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\'") +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt new file mode 100644 index 0000000..4358e64 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt @@ -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() { + private val device = Utils.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) + } +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstallerResult.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstallerResult.kt new file mode 100644 index 0000000..e519492 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstallerResult.kt @@ -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 +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbRootInstaller.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbRootInstaller.kt new file mode 100644 index 0000000..d2d771f --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbRootInstaller.kt @@ -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") + } +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/Constants.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/Constants.kt new file mode 100644 index 0000000..24d9da1 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/Constants.kt @@ -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) +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/Installation.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/Installation.kt new file mode 100644 index 0000000..54efc6f --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/Installation.kt @@ -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, +) diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/Installer.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/Installer.kt new file mode 100644 index 0000000..da709ea --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/Installer.kt @@ -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 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) +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallation.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallation.kt new file mode 100644 index 0000000..95c7257 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallation.kt @@ -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) diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstaller.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstaller.kt new file mode 100644 index 0000000..9fd4fac --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstaller.kt @@ -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() { + + /** + * 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") +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallerResult.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallerResult.kt new file mode 100644 index 0000000..f9fe940 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/RootInstallerResult.kt @@ -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, +} diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt new file mode 100644 index 0000000..2082e95 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt @@ -0,0 +1,40 @@ +package app.revanced.library.installation.installer + +import se.vidstige.jadb.JadbConnection +import java.util.logging.Logger + +/** + * Utility functions for [Installer]. + * + * @see Installer + */ +internal object Utils { + /** + * Gets the device with the given serial. + * + * @param deviceSerial The device serial. If null, the first connected device will be used. + * @param logger The logger. + * @return The device. + * @throws DeviceNotFoundException If no device with the given serial is found. + */ + internal fun getDevice( + deviceSerial: String? = null, + logger: Logger, + ) = with(JadbConnection().devices) { + if (isEmpty()) throw DeviceNotFoundException() + + deviceSerial?.let { + firstOrNull { it.serial == deviceSerial } ?: throw DeviceNotFoundException( + deviceSerial, + ) + } ?: first().also { + logger.warning("No device serial supplied. Using device with serial ${it.serial}") + } + }!! + + class DeviceNotFoundException internal constructor(deviceSerial: String? = null) : Exception( + deviceSerial?.let { + "The device with the ADB device serial \"$deviceSerial\" can not be found" + } ?: "No ADB device found", + ) +} diff --git a/src/main/kotlin/app/revanced/library/logging/Logger.kt b/src/commonMain/kotlin/app/revanced/library/logging/Logger.kt similarity index 91% rename from src/main/kotlin/app/revanced/library/logging/Logger.kt rename to src/commonMain/kotlin/app/revanced/library/logging/Logger.kt index 04669fa..1495d78 100644 --- a/src/main/kotlin/app/revanced/library/logging/Logger.kt +++ b/src/commonMain/kotlin/app/revanced/library/logging/Logger.kt @@ -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 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("") diff --git a/src/test/kotlin/app/revanced/library/PatchOptionsTest.kt b/src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt similarity index 61% rename from src/test/kotlin/app/revanced/library/PatchOptionsTest.kt rename to src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt index c0c03be..cc70e5e 100644 --- a/src/test/kotlin/app/revanced/library/PatchOptionsTest.kt +++ b/src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt @@ -6,36 +6,29 @@ 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 +import kotlin.test.Test -@TestMethodOrder(MethodOrderer.OrderAnnotation::class) -internal object PatchOptionsTest { +class PatchOptionsTest { private var patches = setOf(PatchOptionsTestPatch) - @Test - @Order(1) - fun serializeTest() { - assert(SERIALIZED_JSON == Options.serialize(patches)) - } + private val serializedJson = + "[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":null},{\"key\":\"key2\"," + + "\"value\":true}]}]" + + private val changedJson = + "[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2" + + "\",\"value\":false}]}]" @Test - @Order(2) - fun loadOptionsTest() { - patches.setOptions(CHANGED_JSON) + fun `serializes and deserializes`() { + assert(serializedJson == Options.serialize(patches)) + + patches.setOptions(changedJson) 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") diff --git a/src/test/kotlin/app/revanced/library/PatchUtilsTest.kt b/src/commonTest/kotlin/app/revanced/library/PatchUtilsTest.kt similarity index 99% rename from src/test/kotlin/app/revanced/library/PatchUtilsTest.kt rename to src/commonTest/kotlin/app/revanced/library/PatchUtilsTest.kt index da4a609..dfb3f36 100644 --- a/src/test/kotlin/app/revanced/library/PatchUtilsTest.kt +++ b/src/commonTest/kotlin/app/revanced/library/PatchUtilsTest.kt @@ -7,12 +7,12 @@ 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 kotlin.test.Test import kotlin.test.assertEquals -internal object PatchUtilsTest { +internal class PatchUtilsTest { private val patches = arrayOf( newPatch("some.package", setOf("a")) { stringPatchOption("string", "value") }, diff --git a/src/main/kotlin/app/revanced/library/adb/AdbManager.kt b/src/main/kotlin/app/revanced/library/adb/AdbManager.kt deleted file mode 100644 index 82a71f5..0000000 --- a/src/main/kotlin/app/revanced/library/adb/AdbManager.kt +++ /dev/null @@ -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") -} diff --git a/src/main/kotlin/app/revanced/library/adb/Commands.kt b/src/main/kotlin/app/revanced/library/adb/Commands.kt deleted file mode 100644 index 7db6774..0000000 --- a/src/main/kotlin/app/revanced/library/adb/Commands.kt +++ /dev/null @@ -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 - 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)) diff --git a/src/main/kotlin/app/revanced/library/adb/Constants.kt b/src/main/kotlin/app/revanced/library/adb/Constants.kt deleted file mode 100644 index fcf4971..0000000 --- a/src/main/kotlin/app/revanced/library/adb/Constants.kt +++ /dev/null @@ -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() -} From 825e6490fe5ce8276d488f719efa8fe374e4cf3c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 7 Apr 2024 16:33:09 +0000 Subject: [PATCH 02/11] chore(release): 2.4.0-dev.1 [skip ci] # [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)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 17 +++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 623eef1..32d2595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [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) diff --git a/gradle.properties b/gradle.properties index 1e54a84..9dd1435 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,14 +1,11 @@ -version = 2.3.0 - +version = 2.4.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.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 - +kotlin.code.style = official #Android -android.useAndroidX=true -android.nonTransitiveRClass=true +android.useAndroidX = true +android.nonTransitiveRClass = true From fe3e1c9dc8ee51300cf107858a8be85387212521 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 7 Apr 2024 18:33:48 +0200 Subject: [PATCH 03/11] chore: Remove unnecessary file [skip ci] --- api/revanced-library.api | 191 --------------------------------------- 1 file changed, 191 deletions(-) delete mode 100644 api/revanced-library.api diff --git a/api/revanced-library.api b/api/revanced-library.api deleted file mode 100644 index 54f5e86..0000000 --- a/api/revanced-library.api +++ /dev/null @@ -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 (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 (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 (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (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 ()V - public fun (Ljava/lang/String;Ljava/util/Date;)V - public synthetic fun (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 (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (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 (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 (Ljava/io/File;Ljava/lang/String;)V - public synthetic fun (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 ()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 -} - From db59d2cd0b48b722c1bb4f2b7454ff392859bb4c Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 26 May 2024 00:43:37 +0200 Subject: [PATCH 04/11] docs: Improve issue templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 5 +++-- .github/ISSUE_TEMPLATE/feature_request.yml | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 6b2286d..85858f6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -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 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 6ec076b..b874069 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -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 From 8aca650ebc132d1db8b622bfb436236d27e1d6f0 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 13 Jul 2024 00:45:18 +0200 Subject: [PATCH 05/11] ci: Correct usage of repository variable --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index edb9d1b..b06d6b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,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: From 7f5d6dad7ba73e2ee53010241ba3204d04860a22 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 16 Jun 2024 16:02:38 +0200 Subject: [PATCH 06/11] build: Refactor to DSL to bump ReVanced Patcher BREAKING CHANGE: The signature of some functions has changed. --- api/android/revanced-library.api | 11 +++-- api/jvm/revanced-library.api | 11 +++-- gradle/libs.versions.toml | 13 ++---- .../kotlin/app/revanced/library/Options.kt | 17 ++++--- .../kotlin/app/revanced/library/PatchUtils.kt | 45 +++++++++--------- .../command/AdbShellCommandRunner.kt | 4 +- .../installation/installer/AdbInstaller.kt | 2 +- .../library/installation/installer/Utils.kt | 46 ++++++++----------- 8 files changed, 69 insertions(+), 80 deletions(-) diff --git a/api/android/revanced-library.api b/api/android/revanced-library.api index 9f89984..9233cad 100644 --- a/api/android/revanced-library.api +++ b/api/android/revanced-library.api @@ -104,13 +104,10 @@ public final class app/revanced/library/PatchUtils$Json { 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 { @@ -124,12 +121,12 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa 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 getType ()Lkotlin/reflect/KType; 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 final fun fromPatchOption (Lapp/revanced/patcher/patch/Option;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption; } public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { @@ -255,6 +252,10 @@ public final class app/revanced/library/installation/installer/AdbRootInstaller public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } +public final class app/revanced/library/installation/installer/DeviceNotFoundException : java/lang/Exception { + public fun ()V +} + public class app/revanced/library/installation/installer/Installation { public final fun getApkFilePath ()Ljava/lang/String; } diff --git a/api/jvm/revanced-library.api b/api/jvm/revanced-library.api index 5a525d9..739e443 100644 --- a/api/jvm/revanced-library.api +++ b/api/jvm/revanced-library.api @@ -104,13 +104,10 @@ public final class app/revanced/library/PatchUtils$Json { 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 { @@ -124,12 +121,12 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa 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 getType ()Lkotlin/reflect/KType; 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 final fun fromPatchOption (Lapp/revanced/patcher/patch/Option;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption; } public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { @@ -231,6 +228,10 @@ public final class app/revanced/library/installation/installer/AdbRootInstaller public synthetic fun (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } +public final class app/revanced/library/installation/installer/DeviceNotFoundException : java/lang/Exception { + public fun ()V +} + public class app/revanced/library/installation/installer/Installation { public final fun getApkFilePath ()Ljava/lang/String; } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 03a7a0b..a7c7c9a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,11 @@ [versions] -jackson-module-kotlin = "2.15.0" +jackson-module-kotlin = "2.16.1" jadb = "1.2.1" -kotlin = "1.9.22" -revanced-patcher = "19.3.1" -binary-compatibility-validator = "0.14.0" -android = "8.3.0" -bcpkix-jdk15on = "1.70" -guava = "33.0.0-jre" +kotlin = "2.0.0" +kotlinx-coroutines = "1.8.1" +kotlinx-serialization = "1.7.1" libsu = "5.2.2" -core-ktx = "1.12.0" +revanced-patcher = "20.0.0" [libraries] jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } diff --git a/src/commonMain/kotlin/app/revanced/library/Options.kt b/src/commonMain/kotlin/app/revanced/library/Options.kt index 2233d9e..98bd2f6 100644 --- a/src/commonMain/kotlin/app/revanced/library/Options.kt +++ b/src/commonMain/kotlin/app/revanced/library/Options.kt @@ -2,9 +2,8 @@ package app.revanced.library -import app.revanced.library.Options.Patch.Option -import app.revanced.patcher.PatchSet -import app.revanced.patcher.patch.options.PatchOptionException +import app.revanced.patcher.patch.OptionException +import app.revanced.patcher.patch.Patch import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import java.io.File import java.util.logging.Logger @@ -23,7 +22,7 @@ object Options { * @return The JSON string containing the options. */ fun serialize( - patches: PatchSet, + patches: Set>, prettyPrint: Boolean = false, ): String = patches @@ -35,12 +34,12 @@ object Options { val optionValue = try { option.value - } catch (e: PatchOptionException) { + } catch (e: OptionException) { logger.warning("Using default option value for the ${patch.name} patch: ${e.message}") option.default } - Option(option.key, optionValue) + Patch.Option(option.key, optionValue) }, ) } @@ -68,7 +67,7 @@ object Options { * * @param json The JSON string containing the options. */ - fun PatchSet.setOptions(json: String) { + fun Set>.setOptions(json: String) { filter { it.options.any() }.let { patches -> if (patches.isEmpty()) return @@ -82,7 +81,7 @@ object Options { jsonPatchOptions.forEach { (option, value) -> try { patch.options[option] = value - } catch (e: PatchOptionException) { + } catch (e: OptionException) { logger.warning("Could not set option value for the ${patch.name} patch: ${e.message}") } } @@ -97,7 +96,7 @@ object Options { * @param file The file containing the JSON string containing the options. * @see setOptions */ - fun PatchSet.setOptions(file: File) = setOptions(file.readText()) + fun Set>.setOptions(file: File) = setOptions(file.readText()) /** * Data class for a patch and its [Option]s. diff --git a/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt index fc26311..588620a 100644 --- a/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt +++ b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt @@ -1,12 +1,12 @@ package app.revanced.library -import app.revanced.patcher.PatchSet +import app.revanced.patcher.patch.Option +import app.revanced.patcher.patch.Package 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 +import kotlin.reflect.KType typealias PackageName = String typealias Version = String @@ -29,31 +29,29 @@ object PatchUtils { * @return A map of package names to a map of versions to their count. */ fun getMostCommonCompatibleVersions( - patches: PatchSet, + patches: Set>, packageNames: Set? = null, countUnusedPatches: Boolean = false, ): PackageNameMap = buildMap { - fun filterWantedPackages(compatiblePackages: Iterable): Iterable { + fun filterWantedPackages(compatiblePackages: Iterable): Iterable { val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages - return compatiblePackages.filter { it.name in wantedPackages } + return compatiblePackages.filter { (name, _) -> name in wantedPackages } } patches .filter { it.use || countUnusedPatches } .flatMap { it.compatiblePackages ?: emptyList() } .let(::filterWantedPackages) - .forEach { compatiblePackage -> - if (compatiblePackage.versions?.isEmpty() == true) { + .forEach { (name, versions) -> + if (versions?.isEmpty() == true) { return@forEach } - val versionMap = getOrPut(compatiblePackage.name) { linkedMapOf() } + val versionMap = getOrPut(name) { linkedMapOf() } - compatiblePackage.versions?.let { versions -> - versions.forEach { version -> - versionMap[version] = versionMap.getOrDefault(version, 0) + 1 - } + versions?.forEach { version -> + versionMap[version] = versionMap.getOrDefault(version, 0) + 1 } } @@ -79,7 +77,7 @@ object PatchUtils { * @param outputStream The output stream to write the JSON to. */ fun serialize( - patches: PatchSet, + patches: Set>, transform: (Patch<*>) -> JsonPatch = { patch -> FullJsonPatch.fromPatch(patch) }, prettyPrint: Boolean = false, outputStream: OutputStream, @@ -119,10 +117,10 @@ object PatchUtils { class FullJsonPatch internal constructor( val name: String?, val description: String?, - val compatiblePackages: Set?, - val dependencies: Set?, + val compatiblePackages: Set?, + // Cannot serialize dependencies, because they are references to other patches and patch names are nullable. + // val dependencies: Set, val use: Boolean, - var requiresIntegrations: Boolean, val options: Map>, ) : JsonPatch { companion object { @@ -131,16 +129,15 @@ object PatchUtils { patch.name, patch.description, patch.compatiblePackages, - buildSet { patch.dependencies?.forEach { add(it.jvmName) } }, + // buildSet { patch.dependencies.forEach { add(it.name) } }, patch.use, - patch.requiresIntegrations, patch.options.mapValues { FullJsonPatchOption.fromPatchOption(it.value) }, ) } /** - * A JSON representation of a [PatchOption]. - * @see PatchOption + * A JSON representation of a [Option]. + * @see Option */ class FullJsonPatchOption internal constructor( val key: String, @@ -149,10 +146,10 @@ object PatchUtils { val title: String?, val description: String?, val required: Boolean, - val valueType: String, + val type: KType, ) { companion object { - fun fromPatchOption(option: PatchOption<*>) = + fun fromPatchOption(option: Option<*>) = FullJsonPatchOption( option.key, option.default, @@ -160,7 +157,7 @@ object PatchUtils { option.title, option.description, option.required, - option.valueType, + option.type, ) } } diff --git a/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt b/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt index df60d1b..aec0aa7 100644 --- a/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt +++ b/src/commonMain/kotlin/app/revanced/library/installation/command/AdbShellCommandRunner.kt @@ -1,6 +1,6 @@ package app.revanced.library.installation.command -import app.revanced.library.installation.installer.Utils +import app.revanced.library.installation.installer.getDevice import se.vidstige.jadb.JadbDevice import se.vidstige.jadb.RemoteFile import java.io.File @@ -29,7 +29,7 @@ class AdbShellCommandRunner : ShellCommandRunner { * @param deviceSerial deviceSerial The device serial. If null, the first connected device will be used. */ internal constructor(deviceSerial: String?) { - device = Utils.getDevice(deviceSerial, logger) + device = getDevice(deviceSerial, logger) } override fun runCommand(command: String) = device.shellProcessBuilder(command).start().let { process -> diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt index 4358e64..c8e389c 100644 --- a/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/AdbInstaller.kt @@ -17,7 +17,7 @@ import se.vidstige.jadb.managers.PackageManager class AdbInstaller( deviceSerial: String? = null, ) : Installer() { - private val device = Utils.getDevice(deviceSerial, logger) + private val device = getDevice(deviceSerial, logger) private val adbShellCommandRunner = AdbShellCommandRunner(device) private val packageManager = PackageManager(device) diff --git a/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt b/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt index 2082e95..3d0e890 100644 --- a/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt +++ b/src/commonMain/kotlin/app/revanced/library/installation/installer/Utils.kt @@ -4,37 +4,31 @@ import se.vidstige.jadb.JadbConnection import java.util.logging.Logger /** - * Utility functions for [Installer]. + * Gets the device with the given serial. * - * @see Installer + * @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 object Utils { - /** - * Gets the device with the given serial. - * - * @param deviceSerial The device serial. If null, the first connected device will be used. - * @param logger The logger. - * @return The device. - * @throws DeviceNotFoundException If no device with the given serial is found. - */ - internal fun getDevice( - deviceSerial: String? = null, - logger: Logger, - ) = with(JadbConnection().devices) { - if (isEmpty()) throw DeviceNotFoundException() +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}") - } - }!! + 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( +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", ) -} From 893d22d7938fa1c7544795635ed2ffacdd0cbf0d Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 1 Aug 2024 15:42:29 +0200 Subject: [PATCH 07/11] fix: Make functions internal which are supposed to be internal BREAKING CHANGE: Some functions are not available anymore. --- api/android/revanced-library.api | 10 ---------- api/jvm/revanced-library.api | 10 ---------- .../kotlin/app/revanced/library/PatchUtils.kt | 8 ++++---- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/api/android/revanced-library.api b/api/android/revanced-library.api index 9233cad..728d350 100644 --- a/api/android/revanced-library.api +++ b/api/android/revanced-library.api @@ -102,7 +102,6 @@ public final class app/revanced/library/PatchUtils$Json { } 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 getDescription ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; @@ -110,12 +109,7 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch : app/reva public final fun getUse ()Z } -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; @@ -125,10 +119,6 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa 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/Option;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption; -} - public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { } diff --git a/api/jvm/revanced-library.api b/api/jvm/revanced-library.api index 739e443..6587605 100644 --- a/api/jvm/revanced-library.api +++ b/api/jvm/revanced-library.api @@ -102,7 +102,6 @@ public final class app/revanced/library/PatchUtils$Json { } 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 getDescription ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; @@ -110,12 +109,7 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch : app/reva public final fun getUse ()Z } -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; @@ -125,10 +119,6 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa 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/Option;)Lapp/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption; -} - public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { } diff --git a/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt index 588620a..a8dc271 100644 --- a/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt +++ b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt @@ -123,8 +123,8 @@ object PatchUtils { val use: Boolean, val options: Map>, ) : JsonPatch { - companion object { - fun fromPatch(patch: Patch<*>) = + internal companion object { + internal fun fromPatch(patch: Patch<*>) = FullJsonPatch( patch.name, patch.description, @@ -148,8 +148,8 @@ object PatchUtils { val required: Boolean, val type: KType, ) { - companion object { - fun fromPatchOption(option: Option<*>) = + internal companion object { + internal fun fromPatchOption(option: Option<*>) = FullJsonPatchOption( option.key, option.default, From 27b3359d66e82c8cb8a14037d47c71a7e94c99c3 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sat, 3 Aug 2024 16:06:47 +0200 Subject: [PATCH 08/11] refactor: Move functions to top level --- api/android/revanced-library.api | 14 +++ api/jvm/revanced-library.api | 14 +++ build.gradle.kts | 20 +-- gradle/libs.versions.toml | 22 ++-- .../kotlin/app/revanced/library/Options.kt | 50 +++++--- .../kotlin/app/revanced/library/Patch.kt | 52 ++++++++ .../kotlin/app/revanced/library/PatchUtils.kt | 60 ++------- .../app/revanced/library/Serialization.kt | 114 ++++++++++++++++++ ...kt => MostCommonCompatibleVersionsTest.kt} | 62 ++++------ .../app/revanced/library/OptionsTest.kt | 36 ++++++ .../app/revanced/library/PatchOptionsTest.kt | 41 ------- .../app/revanced/library/SerializationTest.kt | 55 +++++++++ 12 files changed, 376 insertions(+), 164 deletions(-) create mode 100644 src/commonMain/kotlin/app/revanced/library/Patch.kt create mode 100644 src/commonMain/kotlin/app/revanced/library/Serialization.kt rename src/commonTest/kotlin/app/revanced/library/{PatchUtilsTest.kt => MostCommonCompatibleVersionsTest.kt} (73%) create mode 100644 src/commonTest/kotlin/app/revanced/library/OptionsTest.kt delete mode 100644 src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt create mode 100644 src/commonTest/kotlin/app/revanced/library/SerializationTest.kt diff --git a/api/android/revanced-library.api b/api/android/revanced-library.api index 728d350..cf91bf6 100644 --- a/api/android/revanced-library.api +++ b/api/android/revanced-library.api @@ -88,6 +88,15 @@ public final class app/revanced/library/Options$Patch$Option { public final fun getValue ()Ljava/lang/Object; } +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/PatchUtils { public static final field INSTANCE Lapp/revanced/library/PatchUtils; public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map; @@ -122,6 +131,11 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { } +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 diff --git a/api/jvm/revanced-library.api b/api/jvm/revanced-library.api index 6587605..dff6f4a 100644 --- a/api/jvm/revanced-library.api +++ b/api/jvm/revanced-library.api @@ -88,6 +88,15 @@ public final class app/revanced/library/Options$Patch$Option { public final fun getValue ()Ljava/lang/Object; } +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/PatchUtils { public static final field INSTANCE Lapp/revanced/library/PatchUtils; public final fun getMostCommonCompatibleVersions (Ljava/util/Set;Ljava/util/Set;Z)Ljava/util/Map; @@ -122,6 +131,11 @@ public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPa public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { } +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 diff --git a/build.gradle.kts b/build.gradle.kts index 643dd26..fdad00f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,8 @@ plugins { - alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.android.library) alias(libs.plugins.binary.compatibility.validator) + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.kotlin.serialization) `maven-publish` signing } @@ -47,25 +48,26 @@ kotlin { sourceSets { androidMain.dependencies { + implementation(libs.core.ktx) implementation(libs.libsu.nio) implementation(libs.libsu.service) - implementation(libs.core.ktx) } commonMain.dependencies { - implementation(libs.revanced.patcher) - implementation(libs.kotlin.reflect) - implementation(libs.jadb) // Fork with Shell v2 support. - implementation(libs.bcpkix.jdk15on) - implementation(libs.jackson.module.kotlin) - implementation(libs.apkzlib) implementation(libs.apksig) + implementation(libs.apkzlib) + implementation(libs.bcpkix.jdk15on) implementation(libs.guava) + implementation(libs.jadb) + implementation(libs.jackson.module.kotlin) + implementation(libs.kotlin.reflect) + implementation(libs.kotlinx.serialization.json) + implementation(libs.revanced.patcher) } commonTest.dependencies { - implementation(libs.revanced.patcher) implementation(libs.kotlin.test.junit) + implementation(libs.revanced.patcher) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7c7c9a..3a881e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,9 @@ [versions] +android = "8.5.1" +bcpkix-jdk15on = "1.70" +binary-compatibility-validator = "0.15.1" +core-ktx = "1.13.1" +guava = "33.0.0-jre" jackson-module-kotlin = "2.16.1" jadb = "1.2.1" kotlin = "2.0.0" @@ -8,22 +13,25 @@ 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-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } -revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } apkzlib = { module = "com.android.tools.build:apkzlib", version.ref = "android" } 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" } +jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } +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" } -core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } +revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } [plugins] -binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } 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-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" } diff --git a/src/commonMain/kotlin/app/revanced/library/Options.kt b/src/commonMain/kotlin/app/revanced/library/Options.kt index 98bd2f6..3775f3c 100644 --- a/src/commonMain/kotlin/app/revanced/library/Options.kt +++ b/src/commonMain/kotlin/app/revanced/library/Options.kt @@ -8,7 +8,32 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import java.io.File import java.util.logging.Logger +private val logger = Logger.getLogger("Options") + +typealias PatchName = String +typealias OptionKey = String +typealias OptionValue = Any? +typealias PatchesOptions = Map> + +/** + * 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>.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}") + } + } +} + @Suppress("unused") +@Deprecated("Functions have been moved to top level.") object Options { private val logger = Logger.getLogger(Options::class.java.name) @@ -21,6 +46,7 @@ object Options { * @param prettyPrint Whether to pretty print the JSON. * @return The JSON string containing the options. */ + @Deprecated("Functions have been moved to the Serialization class.") fun serialize( patches: Set>, prettyPrint: Boolean = false, @@ -35,7 +61,7 @@ object Options { try { option.value } catch (e: OptionException) { - logger.warning("Using default option value for the ${patch.name} patch: ${e.message}") + logger.warning("Using default option value for the \"${patch.name}\" patch: ${e.message}") option.default } @@ -60,6 +86,7 @@ object Options { * @return A set of [Patch]s. * @see Patch */ + @Deprecated("Functions have been moved to the Serialization class.") fun deserialize(json: String): Array = mapper.readValue(json, Array::class.java) /** @@ -67,26 +94,16 @@ object Options { * * @param json The JSON string containing the options. */ + @Deprecated("Function has been moved to top level.") fun Set>.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: OptionException) { - logger.warning("Could not set option value for the ${patch.name} patch: ${e.message}") - } - } - } + val jsonPatches = deserialize(json).associate { + it.patchName to it.options.associate { option -> option.key to option.value } } + + setOptions(jsonPatches) } } @@ -96,6 +113,7 @@ object Options { * @param file The file containing the JSON string containing the options. * @see setOptions */ + @Deprecated("Function has been moved to top level.") fun Set>.setOptions(file: File) = setOptions(file.readText()) /** diff --git a/src/commonMain/kotlin/app/revanced/library/Patch.kt b/src/commonMain/kotlin/app/revanced/library/Patch.kt new file mode 100644 index 0000000..3595c77 --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/Patch.kt @@ -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 +typealias PackageNameMap = Map + +/** + * 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>.mostCommonCompatibleVersions( + packageNames: Set? = null, + countUnusedPatches: Boolean = false, +): PackageNameMap = buildMap { + fun filterWantedPackages(compatiblePackages: List): List { + 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 + } +} diff --git a/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt index a8dc271..23221e4 100644 --- a/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt +++ b/src/commonMain/kotlin/app/revanced/library/PatchUtils.kt @@ -8,63 +8,19 @@ import java.io.InputStream import java.io.OutputStream import kotlin.reflect.KType -typealias PackageName = String -typealias Version = String -typealias Count = Int - -typealias VersionMap = LinkedHashMap -typealias PackageNameMap = Map - -/** - * Utility functions for working with patches. - */ -@Suppress("MemberVisibilityCanBePrivate", "unused") +@Deprecated("Functions have been moved to top level.") 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. - */ + @Deprecated( + "Function has been moved to top level.", + ReplaceWith("patches.mostCommonCompatibleVersions(packageNames, countUnusedPatches)"), + ) fun getMostCommonCompatibleVersions( patches: Set>, packageNames: Set? = null, countUnusedPatches: Boolean = false, - ): PackageNameMap = - buildMap { - fun filterWantedPackages(compatiblePackages: Iterable): Iterable { - val wantedPackages = packageNames?.toHashSet() ?: return compatiblePackages - return compatiblePackages.filter { (name, _) -> name in wantedPackages } - } - - patches - .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 - } - } + ): PackageNameMap = patches.mostCommonCompatibleVersions(packageNames, countUnusedPatches) + @Deprecated("Functions have been moved to the Serialization class.") object Json { private val mapper = jacksonObjectMapper() @@ -76,6 +32,7 @@ object PatchUtils { * @param prettyPrint Whether to pretty print the JSON. * @param outputStream The output stream to write the JSON to. */ + @Deprecated("Functions have been moved to the Serialization class.") fun serialize( patches: Set>, transform: (Patch<*>) -> JsonPatch = { patch -> FullJsonPatch.fromPatch(patch) }, @@ -99,6 +56,7 @@ object PatchUtils { * @return A set of [JsonPatch]es. * @see FullJsonPatch */ + @Deprecated("This function will be removed in the future.") fun deserialize( inputStream: InputStream, jsonPatchElementClass: Class, diff --git a/src/commonMain/kotlin/app/revanced/library/Serialization.kt b/src/commonMain/kotlin/app/revanced/library/Serialization.kt new file mode 100644 index 0000000..1b1641a --- /dev/null +++ b/src/commonMain/kotlin/app/revanced/library/Serialization.kt @@ -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> { + override val descriptor = buildClassSerialDescriptor("Patch") { + element("name") + element("description") + element("use") + element>("dependencies") + element?>("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> { + override val descriptor = buildClassSerialDescriptor("Option") { + element("key") + element("title") + element("description") + element("required") + // Type does not matter for serialization. Using String. + element("type") + element("default") + // Map value type does not matter for serialization. Using String. + element?>("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>.serializeTo( + outputStream: OutputStream, + prettyPrint: Boolean = true, +) = if (prettyPrint) { + patchPrettySerializer +} else { + patchSerializer +}.encodeToStream(SetSerializer(PatchSerializer()), this, outputStream) diff --git a/src/commonTest/kotlin/app/revanced/library/PatchUtilsTest.kt b/src/commonTest/kotlin/app/revanced/library/MostCommonCompatibleVersionsTest.kt similarity index 73% rename from src/commonTest/kotlin/app/revanced/library/PatchUtilsTest.kt rename to src/commonTest/kotlin/app/revanced/library/MostCommonCompatibleVersionsTest.kt index dfb3f36..e025dd2 100644 --- a/src/commonTest/kotlin/app/revanced/library/PatchUtilsTest.kt +++ b/src/commonTest/kotlin/app/revanced/library/MostCommonCompatibleVersionsTest.kt @@ -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 java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream +import app.revanced.patcher.patch.* import kotlin.test.Test import kotlin.test.assertEquals -internal class 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 class 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>, compatiblePackageNames: Set?, countUnusedPatches: Boolean = false, ) = assertEquals( expected, - PatchUtils.getMostCommonCompatibleVersions(patches, compatiblePackageNames, countUnusedPatches), + patches.mostCommonCompatibleVersions(compatiblePackageNames, countUnusedPatches), ) private fun assertEqualsVersion( expected: String?, - patches: PatchSet, + patches: Set>, compatiblePackageName: String, ) { assertEquals( expected, - PatchUtils.getMostCommonCompatibleVersions(patches, setOf(compatiblePackageName)) + patches.mostCommonCompatibleVersions(setOf(compatiblePackageName)) .entries.firstOrNull()?.value?.keys?.firstOrNull(), ) } @@ -181,19 +159,23 @@ internal class PatchUtilsTest { packageName: String, versions: Set? = 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() } } diff --git a/src/commonTest/kotlin/app/revanced/library/OptionsTest.kt b/src/commonTest/kotlin/app/revanced/library/OptionsTest.kt new file mode 100644 index 0000000..ec535c2 --- /dev/null +++ b/src/commonTest/kotlin/app/revanced/library/OptionsTest.kt @@ -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) + } +} diff --git a/src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt b/src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt deleted file mode 100644 index cc70e5e..0000000 --- a/src/commonTest/kotlin/app/revanced/library/PatchOptionsTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package app.revanced.library - -import app.revanced.library.Options.setOptions -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption -import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption -import kotlin.test.Test - -class PatchOptionsTest { - private var patches = setOf(PatchOptionsTestPatch) - - private val serializedJson = - "[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":null},{\"key\":\"key2\"," + - "\"value\":true}]}]" - - private val changedJson = - "[{\"patchName\":\"PatchOptionsTestPatch\",\"options\":[{\"key\":\"key1\",\"value\":\"test\"},{\"key\":\"key2" + - "\",\"value\":false}]}]" - - @Test - fun `serializes and deserializes`() { - assert(serializedJson == Options.serialize(patches)) - - patches.setOptions(changedJson) - - assert(PatchOptionsTestPatch.option1 == "test") - assert(PatchOptionsTestPatch.option2 == false) - } - - @Patch("PatchOptionsTestPatch") - object PatchOptionsTestPatch : BytecodePatch(emptySet()) { - var option1 by stringPatchOption("key1", null, null, "title1", "description1") - var option2 by booleanPatchOption("key2", true, null, "title2", "description2") - - override fun execute(context: BytecodeContext) { - // Do nothing - } - } -} diff --git a/src/commonTest/kotlin/app/revanced/library/SerializationTest.kt b/src/commonTest/kotlin/app/revanced/library/SerializationTest.kt new file mode 100644 index 0000000..7a8727e --- /dev/null +++ b/src/commonTest/kotlin/app/revanced/library/SerializationTest.kt @@ -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(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) + } +} From b9bf3bc88284c0381c7370c3606b662da2ef380d Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 6 Aug 2024 17:35:20 +0200 Subject: [PATCH 09/11] feat: Remove deprecated functions BREAKING CHANGE: Some functions have been removed. --- api/android/revanced-library.api | 121 ------- api/jvm/revanced-library.api | 121 ------- build.gradle.kts | 1 - gradle/libs.versions.toml | 2 - gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 8 +- gradlew | 297 +++++++++++------- gradlew.bat | 37 ++- .../kotlin/app/revanced/library/ApkSigner.kt | 144 +-------- .../kotlin/app/revanced/library/ApkUtils.kt | 91 +----- .../kotlin/app/revanced/library/Commands.kt | 43 --- .../kotlin/app/revanced/library/Options.kt | 110 +------ .../kotlin/app/revanced/library/PatchUtils.kt | 124 -------- .../app/revanced/library/adb/AdbManager.kt | 144 --------- 14 files changed, 214 insertions(+), 1029 deletions(-) delete mode 100644 src/commonMain/kotlin/app/revanced/library/Commands.kt delete mode 100644 src/commonMain/kotlin/app/revanced/library/PatchUtils.kt delete mode 100644 src/commonMain/kotlin/app/revanced/library/adb/AdbManager.kt diff --git a/api/android/revanced-library.api b/api/android/revanced-library.api index cf91bf6..3deedb0 100644 --- a/api/android/revanced-library.api +++ b/api/android/revanced-library.api @@ -1,13 +1,8 @@ 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; } @@ -26,19 +21,12 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair { } 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 } @@ -59,35 +47,6 @@ public final class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetail public final fun getValidUntil ()Ljava/util/Date; } -public final class app/revanced/library/ApkUtils$SigningOptions { - public fun (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (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/OptionsKt { public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V } @@ -97,40 +56,6 @@ public final class app/revanced/library/PatchKt { public static synthetic fun mostCommonCompatibleVersions$default (Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map; } -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 final fun getCompatiblePackages ()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 getUse ()Z -} - -public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption { - 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 getType ()Lkotlin/reflect/KType; - public final fun getValues ()Ljava/util/Map; -} - -public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { -} - 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 @@ -141,52 +66,6 @@ public final class app/revanced/library/Utils { public final fun isAndroidEnvironment ()Z } -public abstract class app/revanced/library/adb/AdbManager { - public static final field Companion Lapp/revanced/library/adb/AdbManager$Companion; - public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - protected abstract fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; - public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; - public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; -} - -public final class app/revanced/library/adb/AdbManager$Apk { - public fun (Ljava/io/File;Ljava/lang/String;)V - public synthetic fun (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 ()V -} - -public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception { -} - -public final class app/revanced/library/adb/AdbManager$PackageNameRequiredException : java/lang/Exception { -} - -public final class app/revanced/library/adb/AdbManager$RootAdbManager : app/revanced/library/adb/AdbManager { - public static final field Utils Lapp/revanced/library/adb/AdbManager$RootAdbManager$Utils; - public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; - public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; - public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; -} - -public final class app/revanced/library/adb/AdbManager$RootAdbManager$Utils { -} - -public final class app/revanced/library/adb/AdbManager$UserAdbManager : app/revanced/library/adb/AdbManager { - public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; - public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; - public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; -} - public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner { } diff --git a/api/jvm/revanced-library.api b/api/jvm/revanced-library.api index dff6f4a..81007f9 100644 --- a/api/jvm/revanced-library.api +++ b/api/jvm/revanced-library.api @@ -1,13 +1,8 @@ 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; } @@ -26,19 +21,12 @@ public final class app/revanced/library/ApkSigner$PrivateKeyCertificatePair { } 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 } @@ -59,35 +47,6 @@ public final class app/revanced/library/ApkUtils$PrivateKeyCertificatePairDetail public final fun getValidUntil ()Ljava/util/Date; } -public final class app/revanced/library/ApkUtils$SigningOptions { - public fun (Ljava/io/File;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (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/OptionsKt { public static final fun setOptions (Ljava/util/Set;Ljava/util/Map;)V } @@ -97,40 +56,6 @@ public final class app/revanced/library/PatchKt { public static synthetic fun mostCommonCompatibleVersions$default (Ljava/util/Set;Ljava/util/Set;ZILjava/lang/Object;)Ljava/util/Map; } -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 final fun getCompatiblePackages ()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 getUse ()Z -} - -public final class app/revanced/library/PatchUtils$Json$FullJsonPatch$FullJsonPatchOption { - 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 getType ()Lkotlin/reflect/KType; - public final fun getValues ()Ljava/util/Map; -} - -public abstract interface class app/revanced/library/PatchUtils$Json$JsonPatch { -} - 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 @@ -141,52 +66,6 @@ public final class app/revanced/library/Utils { public final fun isAndroidEnvironment ()Z } -public abstract class app/revanced/library/adb/AdbManager { - public static final field Companion Lapp/revanced/library/adb/AdbManager$Companion; - public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - protected abstract fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; - public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; - public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; -} - -public final class app/revanced/library/adb/AdbManager$Apk { - public fun (Ljava/io/File;Ljava/lang/String;)V - public synthetic fun (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 ()V -} - -public final class app/revanced/library/adb/AdbManager$FailedToFindInstalledPackageException : java/lang/Exception { -} - -public final class app/revanced/library/adb/AdbManager$PackageNameRequiredException : java/lang/Exception { -} - -public final class app/revanced/library/adb/AdbManager$RootAdbManager : app/revanced/library/adb/AdbManager { - public static final field Utils Lapp/revanced/library/adb/AdbManager$RootAdbManager$Utils; - public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; - public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; - public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; -} - -public final class app/revanced/library/adb/AdbManager$RootAdbManager$Utils { -} - -public final class app/revanced/library/adb/AdbManager$UserAdbManager : app/revanced/library/adb/AdbManager { - public synthetic fun getInstaller ()Lapp/revanced/library/installation/installer/Installer; - public fun install (Lapp/revanced/library/adb/AdbManager$Apk;)Lkotlin/jvm/functions/Function1; - public fun uninstall (Ljava/lang/String;)Lkotlin/jvm/functions/Function1; -} - public final class app/revanced/library/installation/command/AdbShellCommandRunner : app/revanced/library/installation/command/ShellCommandRunner { } diff --git a/build.gradle.kts b/build.gradle.kts index fdad00f..f7586aa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,7 +59,6 @@ kotlin { implementation(libs.bcpkix.jdk15on) implementation(libs.guava) implementation(libs.jadb) - implementation(libs.jackson.module.kotlin) implementation(libs.kotlin.reflect) implementation(libs.kotlinx.serialization.json) implementation(libs.revanced.patcher) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a881e4..3fd7d4a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,6 @@ bcpkix-jdk15on = "1.70" binary-compatibility-validator = "0.15.1" core-ktx = "1.13.1" guava = "33.0.0-jre" -jackson-module-kotlin = "2.16.1" jadb = "1.2.1" kotlin = "2.0.0" kotlinx-coroutines = "1.8.1" @@ -18,7 +17,6 @@ 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" } -jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module-kotlin" } 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" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch literal 43504 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 PP-GC|Wnefl2gCyaq>Ln) literal 59203 zcmWIWW@h1HVBp|jU|?`$00AZt!N9=4$-uzi>l)&y>*?pF&&+_TFn6P!tpfuCgFOQS zg9x%hUq?SrH`m}0JzuxazGqJRccpwh-0K5rCf zo|1Hb%=t$|+Du{N1LhwZM>Yy`a>SMx7Rwi(ySnrHx%2V&>lrG#_A-3lvAJV6oOj?DLgEJ0!Cg-|K0&O+BBYpS(=)dFPpPORuKd_*UgD z;BNdd)$o*D`X&vL!^aEreh1z^WfFW*UE5tbb+LHmJFepoLb5KGUb0HvKEFi6L9J8D zvT@r^(OirH6GsV{CR=xkSuN!5P z*mpC%)i&1)_`mo|?t3TIIR|dt_npdnQAl?~`O#fD51Y+0ZA5=W8Ev*SzV_|Si&vYK zg+H`k+Q?A8^VY5$-i$+!>mPmkvGZAlg}mGK7p_~H%If}0ADRBj^1SU6nbzijc%4LTi$TAfWBoje(Y z^|yv?2y9(gCffOV|}v4_}&f{c`QC??C8tJd7;jH^*`sPvO`=*(hY@Tt>YFnPzaoeuErTQ<}+FEAL&A0b9Ff#y{I89bdmlwRG*tfF9jKuFT(G(^y2|qLKI1%Eb$sthwHGB{QrRx+YT7vd) zFAI^)KE69A7;N=Qgz9=E%e!z?_G5ZU(?u-tb4|H;(eD*g&E6_&3mOszvw2=B_wm+x1SLn{v^*0U$ z{I$LG7SFETs)aPN3z83R4jq_9JEt%BslgoM+2J4&aY~yRLaEh$iKI`nv zC{G=mk8{1Yy>XXWz+b`e=sdgHG`YF!FGtUs*E6s2A@7Fxmz#KBKmS*+rE>J5v^FZOr+c89jzbpF4fTzFs1jraeV8|UPk{N^~w>rtPlG(|bc{apD9 zuANYa08{TsMQav*7QCrJ|mJXLPznwEA?=73}^5yNytB=wb{d`ebBfRlWLB)Hv zZ!48UzpPDeko)`kh4jrM|L(9o3YD25`Byr2asS`_4pLubYfs4SS}K~^At(`GF*jtT z#?`C|4$C>e88T@(^-aE`u&a(t^d(#{EWbVwTl=S7%nj|Feo!XYJ6yI4y&SF^2_sb@)J{Fb!=^qFV`Uhf!6yO zrdzej^qaV^v+ZrNf8Z2T(R8EjhR~!*A=T#MatE9{H6LA*e8$Va?|a<)pq>Mu>xw=lQUm)FYep4@75ee#WQPyV=O0TpOjW#(kVICa z$v-qNE@f8ov}R#oxWNu-eSsP^xV-3+nwD6aQv&mHKv8~rQEG9qPiApRYF=s)q=H!* zdN)`)ROJ8N95e3IY(X9xQ?@m1GiC8w6}Dx`+T6yB42`J)0&A_NKQYVM{pL{`^Iz#- z4}#-=E%Nt^uX)+SadqZ5-}GPNKfW$9wp#qSS^n|y9^<~}afjTOH(WNHIj8#joa%G$ z%k92BJ7_O|pZNpNJtM*6++Q;ea%!?IJuySY@7lvDotG;%GIGwDIBT=oxx^KWhb0Ur zn0}blsS(#VXJ+HC87C4$x<0cVrnx;15cY~eyHd+k4-cCRNavwSbQ)A(p@wB7Y7 z4-?Lx6wK6HoW_otncFr*)Enr>)`+jQn`~|wzud!?U;XmVj}u=PMR4StR~5?* zxtVlzp=RX~-=j;*%vs_N%q^X@(YNch*5(JHC)KmPr(OJN@mJ`=p}LqY7rOR}-MWyM zC~u`B<7&V0mHCIRR!$KenPY<8_hzT_`b6z$(PX}S>66yfQt4Yfzo%SVcxk!%s=S2r z-Mouk<`{mM)q1_~<{{}FIS+i>&isF2rMkD%+^#xQWTn;Ke#3h_Q9)%ZYL9Pkja#+# zosrCH-bqf2KYrqV^mbK2x6e;?M}0wUw_K;w5X_UOXcia*EV{ez2Le% zQRaxa)U2mRE_}VN)82M%!W`+S!zuUsnN|2NUDkPN8ufUk=!TH@MougDrDka6NDIRJ1(Wwwat8*r%~F;>IAhu~f5FM#`jYP#=`o%33SPr^NZ}`M z?6>T_Q&#F0?53w~&Y1b?O`K!xnUZI0Us~$8-8&*J7Vd9no>c4<-|D#~x&8ej#g>;LPRq6K zxm{lHk8_dn+h1H#@9zgbV0O8%YUxLoS04X-T>rL8eb(-KXuj#cr+S?`TYUmc{mPI& z;hkQUXHHo*X;rgX!MTR34wV;q15al+ZJKoNV}$4H zeHSYacQ1Z<%X;;V&0Wv<&7$N3FaEo?@apmGj6$2`7o3j7RN3{bU$xhdwL1~q_G~|D zGxy>yzc)I}3=9X@7#K_l6utpP`4yFpr6n1uc_o?2i6!~a!f0yf-C&_Wk^iwS;@4d} z1A;#*n=*$4{JvxoY*;1P*=IiVNzC=IP6Q30H1VxX$LnljL#0SCHvD=Z>cb za`oPK?C4Hpcazz1{AwU$veLWcm=;J#Xxpxi zRhq>+drgl6f){PtMu7E@RoO3BGp|CZw78 z8aicK&JN`;7P#0OVvu^`VC6+KJ(xqAgcwrhO<8NG(XKxTkEcf2hmzbd;8aId7=9;0Z^CVE-4HZCTv=XM}d_ zH_kiNd3|%yu~nf5Oq`d0KcmFvUdf&t&CK)mIfLYp9rKKZyds{~mdNd7nlrzJ&BUvs z!<|Xad&WUwPJcl`1MNh<&pf?PLw%lJDG4%EarxL2#NL(a(UBPE5Oau0Ofri(m6x$x z;N`dCD$NHwTT<5)Wa+DHS?qj3%lX0oeVWeBALkgkzn=LwsoCQRnw*XEt5=-=trEET-?N0CI0$gz37II)hun=Tev4J zDRf_2FTU;jk{cIXvZtp0=}G&rI)U**li8#)yCTeZ-4`_d^M$AD?mFMSV;7@c`?oUYR%>qDUe&l}qB0Sw-&W<*}uA*e2*d>OCeqHpP)oZL9Qh0Y& z$6v=gtgnvsIyow<pJxHwMzTX`->FkjM&OGac9?S6+TfVrz zQtn~rvU=3qr^0Zu?lC_DLxchYgChRi2dVehMsZe1A1|%zXW8goQ0XY7`l2OOR)qh; zq|!;#awaZP@z~=sVTxwtr0@fiT3C}F9(59#Yti@iMCV(LvK;loIm_~9FUy;4F>wyZ z6xZ8c{U_GmHG3)e-Q)HbkNz#b{@3?T+VG!8Ou@lv%jNaIT5irX&%amwxvxGgyy~%g zc)ao=-9KyE!!EzCd?8W0T4wKdm(RZ!{bX+acfmRDm$KoHZ4Z7WPyKhqx!&u3;LGzO zzl!-oFU429(0{pG?ibVFsTu#GHvH|)_;<~D_seRozjK}H-!HWK#m=w0@O|}*(_i|N ze$99Jd|vBscYEBD`p_5mk9^L(_-A*xA2n}|J&zLIW6HU_uO(;uk1Zl zH#I+~a_xe9Jj*{>@$-JFnrdG0;`N@ur&X=yoI%Z}dbP|ozeu*uwv~AxHZ@<+$V_{W z>%-{Kd#gT~P2IkFkMq;HhmOTMRj)pkt95_br@X1(SJdo2RlZ_RxK2*^4(ErpK2Zj8 z=4<+9%=3}*TxR;aN6=Pu^Y@v;A7`v||D{uW#c0yWbs3k;4xD(x`tr_`dsA%Fj=ar? z*zPN^n!TFu`xm*lmZn#3%5#1D*0A=q+}}0#gf2f`tgCpt>zMoP8Cuq7DlT1n{B44q zx1?6NQRo)_clYn#tk)8;X%l4k?tIm`a@G3%-yQ1HHA+@JR!EuN)p2lF-@SSB&h;^7 zSeOdDdG|(E#^S2%n^#X5j zi$;krJ6jUBmYnpASe0;Ralf>W+?O^UZAsI|KBk?O|1$K{p1ZwkPWhgDvEs_T!&l<= zH?V%*Yd(KdX2;2^iEargrQs%dUzQy6m%i3eXO+hBe|Dm+*^=l$!?F&&YMm~H^-(8M zW@PAP-0a_c_xrBisfosRLbAS*$L_qDl&8N|+-Is{H2?gU*M!gIUFldX))_c|qO|#& zX$Su;Sge}0?M?qNo|TRll?4-}f>uuu)s!%tRA%z-#VW0sZk87|;$}igLfzS$teOjN zZ+KM2!yV*#_?C5+MPx6>e81kFjw>3L_xO%}yi&1Wv1iQ&-{}5Q$&Z26I{uRcPfq$F zpfvOB20mle5QU5nyl`u^{_C30{52gX)@Et!xX6ArZ( zp9x%cwU}vh=DN?}q5@8n3#0AB6N0I#&M4;Dd4WmbXGxJsx$L z%+{Y2b?d!O*sINYEOq~=y2&n|%-(wo%pZm)7HsBOa!mcFh~F93oa;aGu9@2D&N4{O z_|S1|vCXoAkjK?OdX5<;et$0cEX0L%Q{>8)To;kP?3|*e%s{@1s^XWun{8Gfv|p(H zx00Lr(3_S+-?MTGgOX?i+;k`L|y zQej_8ygsn}sWoKo(3+;YxL~RM)L;p&1#YcYm%Hvgci3T_+~THxIkeaQjQqtPfpt7` zVzc=iQgklpF-AQ;{&TU%naCTit!EuAdi6gXpVM+aez)HCMOw_enbXwn^v$dJ5c-3& zcm9+9!}ZO~zh{?c{SmQ`Jup3X*9s0LSy`(a$BCi8jpjLS=mD?-9O-V}d`QTb^sRqw>m$nxSqB>Otk^d|=jO4BUtyaUgqC=^>&T+mi{ zEwmwgjsLba{=Z&dSHJz_zHeo0ul>pSOmjpXXFb?=CpZ79lfo{^?+c?hslITK^IMcu zE|;_aN2#KG&!fsWKTGe13jAv{JU!<`jQ4aA+me}l|M%Zrbu?AYURvEzd_&hE2K{Nb zCV$e2zu+5m>dn9BdoEN?YI`fw+!1ka>w*UVQ!7NWO1Y2ptj^xl{_lbRV? zsos}9^{Rh+ccQQ3u9GXh9&G+lQx;S4@KnIj>T(mwIkuLc^?%kfwHHm((Q<05;|z-k zeUsF)*ZiNvPHxScpPBQE%d~D9vSl7x=34x2UOOM>%~avAD{euW9~=X!Zj~QaYP$WQ zsNeMAjF}?u6O)YiE}nh=((S#>pWNafbGd&y{aH2F+DA&vZ<&mb$MLDk9~OR?p?Dz3 z@&AIETa5SGG3>Z}+0|~v3t3OeM>7OM_uaqbe*7t~^~ZUW%I#Et)JLnWDO=p|C8EgB zF6C~CPVyQTJHab8`#qDQmzi-bxM)8AY-MQUWS4aokFM?aR@ltdlDv6+WVuLWam~G1 zHJYE}?o7Lz!FR6X*!fxM0{dfK=M>gFG&!~R6=(Za|67lKN5^iI?K;n){n}d0%W{XR%<$1tm%lm1>A#NP)C(-S%Zp8BO5bn)`1{kb z_s4(y+w+m#pl(6J8`1U2FRG79&#bX~WAibnbvgT<{tN9F>sS^$YAK7)d&=8;=ZcMM zjb~S7h|#nKxr$Ts79PKleZt_<<1c|f=WdVDO1de$$}7cRLwl;0o`u%D7Osz}70!o6 zrka?gCs%gBA)`jSmqzgTL8wCz==8qRh4 ze{;>IOPilen_m*WvpxE>)cOmHOmy=~{Vz{?n7K;2?a|WUEo+WH_nW-_>eUM`ihVB@ z{it(Y{iwJy{MQtdwV!ronIF1fdhYW3;-xQ_UE6f4s>(Gt^k~N0GXI}_U7uv9?lYP{ zYuW7f?bD~*_#YFEn!fDo{AH^87unt)Gr8)2d0C?V67#s#t0RKy;_e3qhHK8BS~|6R zW#*ngGtW(t(ecs}ylhWR-$oZkQvUkDN&^{|Hy1_)!#iZS!TI7m#ri5v|x$JU-yROP5Or`ZgTWh#7tIRJM_GGxds612C>Xtd{ z*b81OW@(`>vb(15^A@Y+*WTC9QaAh9OWCf&vsl(IuC5f0lu?+yJ)mp*WtR6$O&xzK z#VR+o)=jhPHRalJ-~ar@^a+WaFMe<)xLJOgxAgp?=Z)V~zJ0L?P0(|+Z;ey^_KEjb z<}b;V8`dSpwS1FACWhXuaXEj|{fp7hh(p#dKYu9>;gY@)8GY>b3)Wwi6)U@DzYu>R zK5_0MkA0q^#sOg~FJ!frX@6mD-M8XGH&?CaLVfL$polYi4(E8T>Yq3|>GiW+k`n7h zo-+S9o6yfX`H9a)r)Xua*g1^bu81TT{A*x6%VSolEL(YXMb~+Ez5{po?YSkg<}JH> zm^bs*;f%cF@A>|Gdvv`o{tmsFrKYSX;Dsyuo9`wL?i-f>EG>%tB$w6Vn`!k& zCUcXH!2 z(%mO-{ew?f2mI3FU6#3G`N`^#p7$k*HZiYc`5JG$%1S#U?|u2>HKX~b!`n_KA70vf zZ}+7VCZ6N_-PHAyp59Gb8n@%p^G8W{*6K;FyUb)OxL$~5Z-1Y{kG~cz@&}9`FJ`M~ z3-HrAl_>d5+F?uC!YO?V0)7QbBy04t2Z-@6GHBWVu8DnGWAd!H#uf&y_n}{67U(R# z{z6L0f=xa>ga6T+pHhE?s{S&}j(78aa-wz4dVP=mkN8+Rev1AIy`f=q$3xXPAYSXT z1nZa5K<4FX6KvPXaI$zFo%H(4uE{6QC#p27Sk|ZOVRTIWLiXO3FqL%sI(`TP!zExSBw3kcLybpbc4(Yw0vBgYl&DVR)+pa#fRhCP=H=|<5 z-p8h^cnm(>7nLe|7$_#?HmjxWuH6@r`PL?D%-Y(r@Azg+d3HgxD}GL~f2CwuTx;v) z9T|I@yzj1F(R97QBW7~7^$f!kH)l(;AId**B7@^d{;9o{g+70#-I>jQ{J;4V+oSzj zztpNyCv2WuCv9tPQSYAhFII-D<#fE}gQ!1?cX#c)YN2xEKWhZ9!S_GxMp zyX_YAcbVKO6K~&bIp_NCk1QJw9cX^O(0|5}ReD9Lk1u#d&phiZ#(BKuqt1yZv0Y7T zE#`mAzCS;n<8VNz)uY&1#)-TBu5@23boLm_%8fA_;v?pZi>JF>->$;aDIcGvRd~fOm8@g(U$`n+WO}L2Loc3J%ikXP$TIDtC~y4L z52lUZKAv2-kz4ua$A{d(>F#cy&U2OY#T)6*j=Wv+Y;*9wbj#0wnbtb53;yVPe7@n^ zRU#Q5*G?)~{m;eK;R$Qml-S$ntoMDiz|vhtvnm+kLO5?4EJ=wljK_X*+fHzWFetaI^HA8Iut?J57Y{^aC zxW78~%H-ndDF?$(J(;@csjl&nz+EZ3TU{IPDlT^3+sb;ma7ipDk3pB~RE?eYtku#M zi)$Heb~63_uiu{O?5VgFOBQdez4YnD%+)bj-+h+9n0Dxg^S1VDIdgB8xR*xVy={6q zT1kS7|A}v&&dH*kfz#UGzx{gPOUZx3sV$oKb#(k@JazCneBHFh`LMZZjkiPk&y<_n zJJb(loSIecv;S4#_ag^N^0Ec{Y8M%`@s$)W75;o_zM7Z1&h4;>tWzbMyJS_1EB*Lx z=DFI%Ze5aZJoTitSyQ)wz>ys9g@qzJ@}=e6jGu<>b=rIF_q)C`cB(lG_``W+jwmwk zwNOb|!+-Vw_m6D{m}l>P?{alh_a);GO6w#mIPVL-`0Enuc;@TPR#)cxp%BR08rXJXt{MqMf;M%=mDvgJo^RDVGlfCXQZj<@TDAp|W(&eLU5|_ne_17xQ z+UIdQlX-n>TU|ne&~jF}J<>X?O7|u;%0F^vtLvFBQ6ZIXvM28W?;gzwGx*YdZta~B z*7$$QX9Zt=F78b;;*50XpXBsezct1x=iiq1Z1bLId^6sY`oL;k){mWy+Y6%(>+}2( z3Xi;C?YO>sTdgq@>$f|?MZLdwZw*%7eVea`H_k`D;$6^+NfC271#7HMl+IZ5&FA~G z%taHQPKkW<^qkN4vS#ab7aodq&OCGN^;)6Q4J$uB-mU2Q;J`N99gc!~d#8(ac5Jvf zk2`I7=*LQ*h^eK?t9$wQ&xOY9es%itvi%AL|I7LU=lh-8DpO{_nMpd-@3Hs1aI52Y1;dA z3^(?@+`)BA#x2^7>n}$d->wCA$_;8uCODe2R8@2-H2Rr3?06qy+;U#y{jUj6t7a^Z z{A99Hy!JeA$Glj1g-e@%Gn?!c=dc&+aNSu}c(-3n<^Z3~nSL<^*{+9|fB5`ZDlkLv zpr2${!G8-oZ@pQae z(|(DNEla-rx^1zm=}Auhb7u1o33}7Iu1+|0H29;G9G67+|5;u4kE{#n;SM#4-Q9Wj z{hGQJAz#*+?`=1m+9~y|?A_yk57^B4?m9&&EMM;Nf=lGmY6o@MJ;@47_s{muIX`*F zT;pqoQrl<#-~TxI;xU7pd$oY<)%7YWeoN@$GlCuj^*KqOrBfF-rE>af{CS4~IX_|6}z> z{Md%BLcxPeE?BJP<+(h2vQp=s8?AvG#giW~yti-VtTNT)oBX_xFROKCu}gsD_Kdfj z8DAE-&9=PM(aqMk3gE1tjhF&hwq=>eRCC7b^YLO`=DZ< z)UKJg#EBf$Co=TYFpA~Bft2-{=!A)FBxn*rFUuD zQ|4y5!xek`B~mVEEdOylJbdr+K9+Ka&%ST&cQP-%zt%L^X2E>kNr^2sUk{Xi)mwdf z{&QoYFHhVv-tKI5XSwHC^2c`4;*^}FHFtoAYTz%$V1X}PITAW(sk)NCDlb@emS^$|O z=ncN@ciTb0mRqH)r}4@ZZ!QrpO_eQ2SZ`h8c3qUB@h%sdG&wz4xsIj0P})DmQ1??$OJv)>)a2Ac^R1KPef9O0_x#B_=JEXcHZvYJ z-O>cP*}HPBRBazuPYeEaz@TLI^!tAKxzaD^#?;+CVOXNG)9 z>6MzdUFLLrLPoFpCZ1gHyoqKdOLf}w!}`DN*q;3EX3UEM?qzdLZN1mLPMEzvKU6|U z^Sbs+)$=;HV*bCG`PAt9m#YaoqaWUzRedCQU3G-+m}7yDTph zx4NR)lI`m;f_nvS*|`Z(xHYW!D@C_bs6s!C+i9xI+DKh zhJT0O%s@Whd5yK)8irjD8QvUJ?#k+IVB`tvOW^2#J$paP*@n!uUuAN4e!U-{FK{Yw z^@ng@tM)q_osX=Q7Ol}{)8q`4S(0w!va4OQFF|H;c(baHkXj8d`{C$+i=q#8u+3Z( zKWm2S)ko$9jH=~rWxlGG%U@iu(wwvCHM`OwsojZdr$5R_`2G@Ya%*O?nfY=~1_oX+ z1_mwcO*u#!4u>qn^-avogCyX&5$qKr$3_3K&)u^8s?tK?w;6WH4(fp|nrz)^ikzMW z3Jy$41r1NPc(9jmPs;C~WMS*4dUVOH&db8qvO#{EL>2a0?EU`g%ROhI<0tKRFTXsM z_2udB3wO>n&;J+xe6OTj<)4Sz3}KzDT=Nzt8P8kkbaeBR4>^{1);_S*(o)&UbUyTh z4eR;uk6G*X1PGm5T_G6v=D7Etij_^(Ay>SPMa4|$)(`$zw8!b7cIcUslk0;%X5GczrF8!xsF;}CxX2s^N){5>f){50l+gC>DFups^%q92b@msA! z742Q+k77T)zFSn$FLUqpaaVi2l`D??e)8w9mR$q?suKwjM>*tzJ{q`*)_5FD6@6Xk zdNg*&tK;oY+xmZe3}ls`JYRl#_xym5o;9nu<{wKJu~D+L*H-lWCu67IuD{}k(!M2+ zPXFj?TEFT>-+In7uWsDFB$jEvY5nrr_Y=>(oBP&%SNZp{()U6fE`BeTe_gg?o=nE8 zjcX%{5}FTO2{WF#>Fbic@%_tr8v{;$+xG3=v~?cU(R|DGPH#$&53kPevs&K}wP|Hi zvX10tbM7gv(%-m$M4hekTHSeM(y?_Vk7gu(eRcd%TgauJS0DfG5}o{6=VChlu^=XI zrDdiUOcGxHEnNJvXDL6gWLR*!vF(F%Wv@1>^nOzPp>1GXv*JpM`>s4*f%WH-BOT5v z2O6s9e`qB~vC^Kjm+?c2Mvn~I)^3sj#|&HbBmPVQ67C!yTwH>IuzuQYr7!_0W@ zoBZuM$N#Og-6m(dGg!W;DDI#+OIWLVUsmU;Xtk{`-4sKr&ukGjz4K*WWYNAbk47J6 z{tmOxK1O-3{z`pY>Ulo1%2wQ8Fp;y(KxFFkypDfdvgT_8uBj_t`+1@8v`@>T9M9ulrU(Za#5TSdYbcVy05AsK0`Jbc6Un!H%6wA$q< zT4!`#Se?IC$=vZSX65XTc1@!d%cIsbmK^f9CA@Ol;i-Q=Rpcn-8d{1^zp^7JIYnQi z-0D-x+K)LiD{s$vbim0r;l|ImN#7!NYveBS+IE~fXRb@=Ip;*yn->C1#Am!yb-QPM zsjPdAL}P&=OYMe&NAJ9)D-`%d%AK|gtkKL|I63QiE1US&8J7|&?qAO8PMuu#qwPQ( zYv~&8%5`E>{M{RpEW$2)agNJ;ta)hd&3V~zs@EqLcRvqZc7wH7Zo<*rRg)z}riX8~ zTK4Qn`Lat#ih`oHSIFF*5&rC%&#sMM^!BJ~r`%k6+Im`=q;p~HJ@GXnKd-l|i^l4o z`E@dPX?WJLm40hWeV1P}J8kmXb@t1B3}Stn^OHQ*&Zu3taz^^$h?o2H+F#54j4OQa zTJvhkg1+@a`QN@?i}=|gaxzx6SloAeg}a%E*k=yz8M#)jdpm;Ttka)cuP(n}bpAy3 z;>WU*`{un^@G$0fZ*ZoqAN$!8&&z+`Y-?*yd2rGH1<#$ga`)_?Y&@=AWNh=z%lA&- z)Rbo@kG+vGTNPxMU{SjN!V*L7&oen?HI@ct-afN$-yEN*OCIeD4vK#%{%!6?xhE?P z?{2)H$&a6OH+7}YzY{0EwXf-V_z@HTW$hJvIpuS1^v_oOKb_@+Vh2Z= zQ-XMUaKO>Z@_R9lYs&BKY5Qzj`8o5$=h^0^eS4Rfd<_4veNUl8u%4yPZ;LRCBLd%w zuf(v)91#8y-DxMIB5vMYy&^<3Gu%BuEYgbUz8~My-EM&w&3TsI6#tU?z+T|X)Ofel z_{asTmY)~DP-Qe@mg858oonDr$&#&0MRX-a;u%ppUbvYG5SNqtyDKTt)#xiVk?HU|4rj)X;?`xMl{`txy7C)i4OkcZ|4;HA% zS~98a|M~5No2Azp%}YjBMIsOP9dkHrAmzScirtw>)1FN{CL82?v>|teGyAj)HJMj@ z#JR+#HheCl!KgX9ed8*Lb=LfOVSIh-FY4S`Tr&5{Zu13c8?2g5CD)vNuuYafAuC1X_3vqX zkH4)JxhQjE!og#|4WfmZXV*;MxO4rQjp;1WZH31}tyVl$%n{uoadOpFvz~9=d_}yI z(`C&rM$7Wl&s=<4h>iWvs>D+)D-vb>FWSsoU~qV6=dIF!^L}iRukM{VaJArm=A4IX z`mE1ClG*xxNoe{Q!TotnrAB4JFZeqkyWSM)2}_SQ|E7B?QLAe zv+Zv8%e*I+FHbB8US->)_wv}LJptc?*ECw5*;-hxS5S08G>XlzJ?Y1yvt5z%MT2d- z-ny(`xFYe5M&G%UHalMF%t_GlUbp-|KWdld*CUB%s~8v z`Ol^G3=s?7FMO@2@S}6xlq)uhKFem$iMI@1l+qq6Qm1)2XRfEC~ImP-9wOS54IT_{(DJZtEESdX;Z?@uQBf%1jhI)X}ooC}l}yQIGrf_76($;?HR7 zKYX_Ij?D9Ce3G1deB8|A&Yhk6xj;Pb;Nsn>dxX{}O$t9>z=p1m@Ldzxoo6nOXeY5A!N@q4<-6V`e>u)VV<{LpWw zih2%tj)%>PH6Culc4wsJ6EoFSYC?`#-O<0yX|MM1v5=qFj~0icf>$FFBLc5VT$pVB zvQ2Hng2x-GTzX%GwLF(*JvCEp+44DNcf}&6%)a?6;nqdn&DYJA&G0$f`K4^T@a0q= zv8`vEt88*t`=|%pPQ3Zi<4#y|o0#Ort0_U7m22bf&+_;B^DxQO*7?<{t*@&taW;DS z&s;d`qX)m>jKU-iE9a`Lqh5_qmu}fIThrZ?XW8u~vv+NFeXwrUO%Cq40a??mU#rSz zExWq4n)T1xu%12pHaMGaU00G6u=)FnW|{U=e0-PL=ii<}t}7QU+h(z&>)phye3=&$Ip>$8`mc{s$*{cnbBT&inb1j1nV(5K z;*N`t27BEu&$E}hI_ajwY0j&1*K+!6_N~m^7&6;5Notyu@-fCkQC_x7wu&bE7OqKY zFLN>G=r8%f#V1p*|~mKM_KSf>umz>|H!%b@|CahobGyPxyqbL z9~dI7Q>(lVWj3BJK3Af=tN6nPvDCYttG?`5X_YzqO72|G3F!=}?k2gnXT5ZZI@!5q zU5jl||I}@ZXFXpMlQU~ZkHhA>JyKy^3$CoRbPn@5v|QAt!;tlM;p8UY8*37{&Ceh1 z<=U1syD4mo=8Z<4lc&O$Cdlk&nI_^~5*=lpG;3`FQ@V&v%Vv@5YS|G7(<}Bd&ky}z zpe9<=H~akOTjC#FS>q2)7p-B9pZdX+HU79I>-_`fsy2%rL|23y+-|7((3)#s_j^v3 zS(8@1)%amo=iv5~!C}|x$cOx@e>@KM&wQ=^V`a;H%^#L^3m@3jt$Yw(;dAiukF8Dj zH~hP=vP1HfcQ*A;T%i8Jnsa~n2lhXK2PglS++_b)-zA1=M$3vY?c5`) zd{3|Ro4x(@$tk$W>xJ>{bM}`rfzO>d+^KK=VHu-%}cval_gpQT$~w~QEs9YGi~nYN!fw( zrcaBD(OhVjmA&fmvvmjBCQaRz(mN+`evN62%Jbu<>C;aw|MtmYQRVbm7rHhV{4z_p zsWx@uu~|Z?<;Q}=o*j`W(#iLHaBEgj>i!*d(_gRLCUYUCcx4{z*D2}Goi-J}PB>+B zsc)NiY1;|IER$7BUo4993=ge27as7?@L9mj%~`V}a}Vcaaa;a0vAh?0CfaJ+(#r1S zneA`pC2P7bUVUfz&9Z;28@v6UUHEBYIx+og*0nvA=Voy$Zd}pA=M*$4o$X;|q2`i+ zhGs)O4Usdcs#pB)hYQ^g-Mh_id!g?`=7rmSd}P5Li)s`}E|mg`m9QopO-nAHEnTeH%VRXXC}vH!m%-*zoxE}f)f>c<&o7rW%y zE7yI>d$gB(OFvW%xjfyE`-B~fw$=|#mVo`Lw=XxJc>B`#$+s^$Mb%a27tI&-*I9q4 zd6c*OBEu<#!1~zOzY;F7hb2zEk~;oiHTUo157u+D9}`%?&3;^9jdc4LhQMhL)3Tb6 z@UJOWOazwrLI%UvPAO(MfqY zReSGBed}DkfEmZPb?GX#u3pY@sBw<0-6wZj))xNfZ7D0Wb$eHyj>S$I{ob<);eYs(&`MSdY?Sai#Ti%h4#rSpRx5^Q782_i8D{^bz%37HolFrv5yqWx>>$WT9%i%*!@nw z?IW40qslkb&n=F(X=MB;@~yl6QNxag=vW{5CZAu^6V1N~H2DYleVtThe{$XNI4M@o zOLbo+<}$jKiv2cyRQTocx&s|MrWa+~f0Psd{(vF!&Ed<7g!B(MPAZb)UH60`lUZcZ z_iuU1{wuaz;`zz+&6C6PMI9UWv-=)pmQIHruo4pm(sn;{d+e^e zPP%mpi)y^1wW7IZ2^^L1>``;oH}L(C7tyw9!`~BmtN$tI@3`Qy=;9G?{Xc=nj5m8n zd-0n1o+Kes<#9_xt9Eo9 ztO@-deW!FgUu*4}OS?X7i+T6^Q2LECaUsu7@vhHeRa?U^mv7dZ-a4fYA6w!2{xOAZg?3wx>b1*V+0WTZ;|v8bNE6;xYllzQn#<`&!N6PxZbZ&OlK%X>7f zGcd+DQ7QG1!=)o4?_NcuN9VYAkW@F&Z>_} zCp_3qPJ39JsA;H|FA(;+X=O5t@tFUS^2mSEU(S1(`WfoI-@qBOe5d4^L%c@~QYJTZ zRGKvyl=cbi=sdyZd`K@fQ0n-KKOS{IMWXf%z< z^N4Rjn$Wo`K4#Jj*k>VNq71WIX^5<-*VC-f-a&dL%`|`3q+W-1a zq&}J=q+e>bJN%5EF#GR0p@!{SZq@!u5o|GgC6AhnpOi5bEEZv4(9>mLP{7fRfFxr` z%3T}DSrc;gs=fN=J&6@Moh=4Ad>#tgiSA5ElLb^Hm{fImue|d9G^yu|(EUAzoUvQh zif&!IHY)34*xJ|^O>0-EWN6*G6%{=_D>r-X?z`foJH_We**~9G{k;AE&i4|6x)1lWXX-!q4pulTdC6zuvI3b|D=&$he7;3tYLUm| zmvWEh+ej=?I6qVu=Yu>)O`6R zp%Hv@doSfoTITs9XWGhWrayi!Z>}obeC#&U9@_)fOOn$kWc`f2w1wqm<-MgJ^Lp>= zt`}!{SySgf!R%A3RJ_QNvxjPxr5W?4_M2sfW!;fm@-`>yj^Go&cazkFmdw3WTsWbu zSmQwT6UnJ*sS}cA_zRRy+^sojvCTqL>n5Y$$%-bB zE+{ytxy6AC0{UOeT=kdaw?r*mGyw|Mc-g_@<^5pK= zUCk$>r)QYYR5NvZloqUYEi39a4^J1nP_A1^d!p+T>$@+v?pFG6$LIP~*?8w)3A0nB zw(ZoLv}I=4&a4fWD-SJ{op^3a>WzsmHKi*bfALu4m7}yS}gDy{Ne1%Vj(4Qs?)6#17dSK4b(hTY zl-^?_%hY`6%_`oH@22!l^-)&4cFvwQMd zjeFN${IOW7Hu7=5q3_-+Ta;vLcZ%Ja<-rj7y#G?wnoH{{JRg_D<>fVTxb94dNLRbP z#$)^A7hK z_Vq&An&)EUjKVff*6KTPEvfJF1D3^S{Uiffrxu=cIhAD`>d<3ez5A@$?VNDs#ud$; zE2PSf2d{f&=g94${#MR&)kWoqC6<>Y&EHIEJt{u8<+?;RgNEnjb?;ffvxhK0#vQ{q4NxpD3IA|Almg*$mHirC|-IAuNccb*aF)p0uv+^x# z&?D^&*H}w+gb&yyZ;zh$f#IFhUnxDmqu<>Ye4M$iaN29e`I6h$?pq@~rO|LJSLB*F zHvwbo55&BGZx4Wf#F^%T8c!Lwde{dwu7oXHQj233U4 z<69qlc=GvaC*Rw&o?JdV^K;d7zV(~D!Xj&Ld7R)~{xLJGTYT=eT{C2J!X)o?cKWdH z3pi0y)gyQPW%-xtxpE8GORHwaU3^*nWrJV%1^1F)0<{s#>!dEcl-kF~JwNQK*5q~9 zUux}&TCSZr|CsrUKbrT}e_sC6t=C>zp1ovWXa2RHn`?}-PHcDhos{^^a&G>^=!H(} z+B&{J<29YCQB$(7*S>$jdD*j^Nd*n-YOL!WpX+~`Uh>Z2tcGhq${Yb02!kX)M{N{>DHn>~_bI z!)|-{Q^dAiZ&|Xr_2I8Canjp_AM&Mq+HkWw*YcKl+?=JaqdT;cBX*o!E511T`R<=` zbNdVSpJT3Jzs#yGqO`Y6$Xn&m#_j5j=iGnB^}fGcIbr|NEgzpqb-(8Eo{?6{zxxc+ z^WQ&b_|0E_yR@!B{>snW2Abt>Cu~x`w5+Jw;^IRy(>sX=cD^m{dVKVwSi|k9YH43u zclUUoEm)m-?C>_h*VB{~?g($(EhcvS>dOONt#9=XyCt`Y@3{7l`_SDmCgHdH48FXW zyj7+nY?`{Rdwodk)oS> z<{HkeKjP1|c=?i_A32t8nmYfSf4A$NB>&D$^HWZ^g%w!+4>^&xzf^Gf$MYq1ZT0Iu zZ=Ph`eeBtcEu5!b39Zc7-6b<+XU9RGxzg|C^hyrPa;~=0`T1O2%6|Fh&yOT@C&oy) zA6@>D+iL#_{meg`&aMBj+UozL`qqbX%k8fGy#7aeZvE-~&YrKL1Ydqh|F`#Cy@30_ ziT|f;{4isa@AK;_U;jqlu_@jnY}UWx`Ld_W9^O&h_&@%LZ2i@j@?ZSt{y!Ga#N|Kd z&rGp7NwO`QERA&z_Dm`~`BMIvv0eDOQ&p2Qk44Kg6csqR6)ttxok4(JE zll8^$qv4@I%dL8sMYacp%w<^eMlUmC!t&V%@8r1#FqO~x=J8g2`jozXVl&OUG@dU? z^y++l)#*n{S^BOICF>gRU7PE5P-ElON{!X`KCpTVl-zthRr_AqLbFR*%h$}QW_@XX zNqvjd_07``v6h852(p{+Ud4BabB9~Z7mKG%A~O7}Q`tV6W$d`|^w)wfD%Y*!7OaW= zuyyygki4FtyKmccw~AZIocMWbQKw4PpSLeNK1`c(sx;`|37wVe?2q5>F<6aKN{`ztwL37#xe)!R0uo9Fqz zLnr-A*Pe7SNu4e9e(|y=$GUI6PWe+v+n1#0XzS7D*o2giNrgg@W6TUNVAD%OdclRtW9^p%!8lP>8 zq6@0RPtOfq77(=jMljcvGojHhTCR%}eVz0|Cer)c!dZQmGxNQ7HKfm~_wrtMuArza z<7UJYy@KlEZ|*DRG+x!3ZK_r^;TrGB`kAIhnK9Qd2sEa>f7U0Qu*>nTlbS5g&bZU9 zUPXINKRM+DO`mvY%7!z)R!BBS3vfSvoVI@MKbg1lbC@nB=soAUD1ZBLOi_E=MAK)V zR%?Y{7rb7r-n3yo`|WPe;tc(%LKFL)zA*0UnW1+)T&{Kk*OPOM?^%l&7tRa6WcPJX z*Sce$c`n2`%e($Itcm60i?ew?mG{DqO^#gKM0(CIcW3jkcM?8zs((UQSYgYb)T!Mko0r00Je9tx8Evc*Uis=REN z-axNqDvMy*jNG-%dX6$+G{Y-two}Wa(0! zC;UIPPHD5R*f4*Yori0A-=xhVR?`}bSyR?7k=!CMNk6D4uORI7sh!jE7Wg|V$**1X z_e*90=Ov?Eg5SO-|J|A4Dp0e|2xzh>H60^D7jOUk1 zef^@b`RxLyEwTZtg+&A%o+s_7;GgRm80BCnVboVCwsxUPT4YH7b2hI);jb6`UM&rK zwK$JqOaDD?hkf!JUblAYhpWAB@Z@Hgtbai~agIPmUFSZY;IhYm6?oQO)Qj?qKe%E? zUi~%G-!E4im>lA=jbFT8`{w#3&L!+Ad5b>3kbdy!=&S6PvrE)}a7Ij@XvVUwuJ>D3 z$dZe(Qg_cv-F5z=b~bMP#fb;%HRSJ0wf?j?dws<_-&qESrv5dV(DqEA;+M-mqlydH zLn`%k6RaG9xxDs#56*qE29O+%K_XrKypUyXBLUc6&at^h@mR^R@T;oUm6gt&+jY zBJHQ*G!gf!p>{r|siMqVmfVWgRNVDNEsAH0-tEW@F~MBZ%(quu+_-4x(eRshn67jk zy1LIX>ZZ^q-U~PRWzIc1f3ebj+U|+RlwT}6a6xgG=fX72dFjl?TMwMrs+IEh2j8Xb z3+MA+(SB)NG5JgI^sUaawo8JR?D5?3Y~}X%t2WrJnWXWbo2S_HkDTqC(wcc)4||)p za0Q0zW@dh6jA}kUOD$vXmY_M6IjzDyt^00g_OUz?Qs+2t=}_hBv3vTpXA^~M^uv0} z-d#WVUx25MUF0vDFLQ%9^P$xWQM&ic8CoQt>n*DhWs}mFe)0QDK@EnJo4WFRtZf=* zK6JQ~KE_oykN7r++ob#^JeTWexKu zi=u$Tj8XO;($Q=$|A_s}lUd-)d?loJ&pyd*_n4%A{eC%D_iAJNN{uC=Ii*ReA{pCE`NBh0*2_5eCu{Zq1 zW!ubJIX0}$*j;t6LFLhb0&!p0YWX#`9KY}O&;2Z+A;-e9-}h=mq;A4iVS`;95jUCh zW8CZy{82ynGLYp(@`sa?AAGZD_`~w8S3WVmg6qIv!?b@cm0P3>?sndN!P(>aLA3dw z=9+7+n;l9euf=#pzswA%-gxilm*8KLHO0Ic^B2xvoNsUQo5yzbfqwyF%={}ixH)q3 z{B>&FE3l$UkNaB{7faiQJcY-}f39YRtiJeJD)-8SfEN~L-R3hKobB!6v_)3MTD|4q zH`Xe{SeKHA-bq)zJp2l#OM53D=y|&UYngT zNqxWgQty`K8+N!{`r3R&^{C?!$4^H$9ey)WR;_8e26LIAdfFrQ6&_PoPv~d4@_Kn* z%c0B7C7h2=WJwyDIhE9k!>dw2q(O^0BA92Vd{g>EsHOlxlijvp6lO^Nigl*`z%7sYH~h5Px!UEtw(rNoo*;LHp>c{{PMpgDJ7$u>Ry&U zS0N?{>(C(plIQ`&wYvG-)`%$c)EjltSF)wiKp z>+a2&(7I=PrY|*?)=k*>KhITS1g)scw_E&&K}1~2h$ggufJ=bsV*qG;r-tE zji2K4`;7CNPvzgKN891_VZYV4d?p446IKQWdF%t6KKbeJ-At{Mv-2+{h#ddVKRq-r zx#yw74Ygt|@mTXc-G+uL-X0$&ZAjQue*Vc7w}{Smi)gZ!zAc|nqW zrrwt?KQB7>zg+L-dwctO#*77$?mXL*b~N2K3<>C6+_uZ4e92Djv)hxMdeH5*Sk z^ti=*=P2gmS-I}?)^h!y7u5AU3Rj=sV7&Ue)g!;~IX$gQJJU*^J!*Zf>zt}|>qU#A zyvXqj99MF-Glwo#o^HKqcCJ#zN>Qz~4*AP2-cIT$f4gyUr>(Qt)EQs0qK~OebW-kQ znZ)%@wNRd4#Kz~5V3DWDhlp9~muyu160S>5JNIKfe}AhR=en3FLQi8RCeQfzzD{Om zl7slS8Hz;SKX|6CQ`2d{$Gqw-K|%};To6k z3ox6`x|e#>?^p1mEq;j;b7qxQb6jLTl5Kw=>*#xq+J&(k-YMdOYSp5GYbJTgN#;MV zoE#u};w^HD_z%jbneeT~I%=Un$1k)rG4)y`?)KVA0oKJq1--bCyjG6*^NL z=<_W4fa&}TpE+l8DjF)f+`ncwKY2>$Mo-0Pw6%{{zg=hDWvxH`b3UGU`ruX9@_=VMwF=8M zmIwyiRm=Mme|MVs?b+yQ)yr$WdM_gb!+RzM1`F(IH6XDh!?CzHH8&|IwMfM$KRGeS zC9}AsC^M6YMM zZm}gI+n1c){?Au;|2=EtX*r7GKNfs{SH0`~o#N@`&;R~@Ykr@pfb0LH0{d&$23)>} zLSrU-XnhQL<|v-{;TxO&Ji$b-2^-E?i+Ptx@SVS|+Ogx{?b$Xtoj-Q8G*^TjQvcw| zIX|g2&_?{&a#frD^ByN2Zq~Ug^3ighy?!Ex{)Z2XI`lh!BuT_iDiDe1d#t_YPpI6! zV-E4@FQt{@xE{~esnhPsZ~rLEdH?A58AsL{?2|vn|Ko$Mpgy;SLGqKSK~bA?f=^p+ zOMAieq-WjgXCa;&b*>5({5W*z=a(~IZhd<6<sIZ~&}yBuD`(|1*TrX~Y}0OHCyXn>%#i?D?gCg{3lO@Q8zJ4N<`qC<}|C-1qz2*o4y|LyKHcR_i}XSANI91 zFDGqNOwjvw?#*6)`{C(CT_U;Lzx~A}yCxHv1@104ybkU3T>XeVc*M3UY`8j%?{K2|j?Sr@E z{-l?odz2nr`K@1d?8nxQ`zLSi{noA7=Ozxn%%Phfv@E4AFF%zZ@hgwNAFi&9`}pfcXN&I9gdktk^UF$Bt=jm#Z=gzeew z^7hQ*7y34Q-?!N8-+lT1T_vx~uA-9x9lJ6#!>^{ z;r(CwhJ@3RjdW-&ZTS~JYRojv9=99{9F|8lhoHq7szbt=xi%-Iy ztLY0iZr+k7WxgYRm*DFq=8O~dSIjOBm)OKw{%QZ=q9SYG?*TfOZ|T)5{YeU(UpQsb z)t2X>-)~;@uI8>ez3J*E@gtR&7xdhW{w)x>(EOZ+?Y)LFo(Ihqyo~aTLZu&V`S)R6 z>7=?V#-4na<+cV$oxk|1ZWimT=8sY>*JG~sU(mS7!=v1wTrorCX7Hg(pG#*Lg6$3{ zFFCnOi1~@>8+S{loSlGJfggn3~#7j=D7ixNiCDBbNhScPi=K{rly==ys3piVM2I zzYbjLtzwY<&y2d%p{4%p>q9II3^kk#3JKJr^c(YWGlbr_dCUP&+io1KfgEo@9+Ee|5!dK z?c-h`9LOrnctt_#fW*B31IY$|sboftieSHclWt9E%49X>N@LD&xYBQFxli}#9+8g> zAvzvEtiMJa39{9D)FpDdXyT5h)pdKM4^&sgckwUTp>|RJTC&KjCeg-cKZdN|*tOR#EJ-#xl%$a++sj+0A^|nMcWhUOo(`J^MT#meQ$uf4qx28m?b2A#$uWZ(M zAiF&*)7B<+#xI||WY_KDTc&Q&n;glhyLZLSwmIUxaq6iltiP7)SWEidx|J2hpMNV# zZSn1gKfx1Sa2Qz>13NxQ%(MC1B~L&9!R{a-|uSswRjU%r*+ z@t?Mnxi4-?9HV2&b(mUmpJoF<+ZQ6YM0)mNsenjB^+mQ z<3#f`!<%<@ELOcH`S4lbgn$jJ8cvlq?%FZY#8f@qxcuy z>R8*p^EbnGF7>ul-n;5HQ*7YvZEwz7i*a%cqOhp@jy9p z!dfM6cSpaYyg93ObWZ8gt}bnWO*JX$|ojxPV(r@mw|6&H$`ndIsC<<%5VaOuc|ctM#pMrm$F*iuEOBCQbommt zGGP^$ZrL+&G2Qx^kmsz7yLz6r zHVXy`D_VL>H29v^-BdMuX8i%nKTcQHOB8j^c@lm~bBloK2`Qx)3h#8b-hOk+vZ|)1 zt~K6Pei+n=Hkd7NJyEw@;|SD*YZ;LGJEZ@4x5phPIE zUF&Hs*7ddx|0gy5>T6#ST<}re$0lLf^}EyJliCWycmI(`Ee}`OuH@gq#K2(Bj&lVg zbgQs)eqLH;dTCK2qKnx(IoDs7LJ$6poy;`kK%U4Bh-v{O&3ww;qxu>Mgo}p&E+4%GPzi;1tJ%2xbKjQ+X(^g9k zTzZhm^lVasgKM)pXNAv0o0+Ue{wHiyH?KH2iS4|E1bd%+h2LUfi`?l41KxJjKbw}# ze(l>ewrj?BznZ?g&9f=czk9du>ORx$n&0<6Kd9L3RKBY2g!X6dujdSz->xne@?E6y zs#o87o$wli7y47nC62^x;omTCo#nc}>x%De*vr4?BG>o4_n)uveV2*qPSLR_sbPB> zbvrxCw_kos&g<^u{(d>^sG4 zb4;|XqWd~-P`dSwIqvQVOC5vA@zFGXD?z_?|m&2~E zIE52*Cl$i&^TG1r=R@L7^O@Y9yIVJ{jy+@i#r*9217~vnYaTh1 z^G9ThfnQ7Er)mv(*XS0;EtMUKOCC?)N$WOfvv*ifAQ00o{X)`h`w=y>d7> z+3D!9h(Bs9{+KHv{z&xAyTy6MzeK)pi`bs=Tky=!VxdH7C#T58y%&xN-zXGSQET?m za(s8lR@B3XO>^C8N<=7SB@(p{ggCO+A9xZaSQu zEvLZBXe6AK_8)Cf(EZN}eo;;a1{o0s1`X^@0cg^9tw>ESMI5~z{=HuKx#a(Ox<8*h zjxb`&;SoI6Fd>JBg+qJEjTw)wId9GBP+V*2o&MY5n&xuh2O_gBUMg9lojYs7yOIkt zXNcuUnJ*N+Rq}rC@AM~^zg6C|UNYH2}zk>wELI=qg+9i$3(n&v^Fst!rvMPK%Uq_NFq({+<*o*HV;}bpL@Pe_^Gh zVP(Pn6Vq~UyH9f7D>O~^rC*N<`)~GLT7tGyxGYrDXIkFf#A~acxN>pTK{KPbosTS6 z?q6`E^uUP;|F~uR4(B|#ExfYi%$pg8dWp{cnZ4Wlqob@AZ=b(ui_G0@`!{FiHZ4!c z+?JU3GU)D%%!zXzSDm_-A7L`<+LV^H7mGgJRxF!)EtqweTSB9svF|M1H-?+4;uDKP zwzF*wyk>eQdD)gTif4@~-pg&eyDsLF=F5jnYFa#V=Wp41r87?V`MuRQlT&!B*SCBX zjrz4a@311LQ0(gODz4XGPMKG1E3iSGr>N%i+?hK}(qCGhz4TqW(re8{re>bVwiefm zynPL8^0xEymJ~aDN;~K^)hTq5hAZnQqh}{vysuwkx+Js7s6;V2>t&0&u9Mp|p~&lM z-DMLl-n|)fu&l5A^pCcl=_z}B-pOCjU0=0CdhQg~I47H5|5G~GaPv>lbYJYAucv3f zBu{Uf&#~#(5@(scT>iAmbes3)oV!AWTp8CaBBP%xH%~E;@!<5`F07Gwas~fC-BX9c zMY$#%F1s*CW%l!lzwaaj(<%uDf*4A)$*vC;SRJJU~u%HQbK5%$6Qwpsb^jaS}<@I0EWa%=srncsBf+Cpli?=L(SyQy-)gsr`vdn2-h zqGIPNseU*ip(z}>-)z6#B(u!frf)myX01wBp6biFx_*lF)UNlBs&(E! z;j&K3r9<9#7AY;Sk;$4l`Ore?o5A~o3VU+@nlk@q+aLPTI_|HW`^vzjr)OofiK@xZ zZ>?31ywm5oez|$MO>MvHAIGEeAJs+cpZQ1}50tDA{22bn_t^c=Kg|E9Y*>H3r^BUx zj?tUW6P&fDIxFm!FYhsQ_IK}DvtHA@sCiOiYxu$^A=j??RsRWketGig8VifWnak&V zUw!2Ed&h{7$TdROe(cLnz1vikRyyUl)Wj{78<*}{xA1vM(B{9@t=o57b-uk{_Uf;d z?8&GZ>uVCk+)u|_JX^K8qRry-imt``7QOmZcV?=4EkKle?yBwEWcV1pU z=xg?))NOzIv@fS_AKE2$zrTyidE=~qif0zrJ8jK45O>FY_w@_wxMpYUKfgKN?Kj`~ zI@Y(=w!cHS+<$-5{PMojo8!9;wr5AR?@aEVS!Hqdvu7E9Rp;jZvbif3ie4~a&#d4J zJ-o5ts}55!2jBTcX=0zYG`wDZF)^l{rFT^^XTHFNtr8kcea>QAuHE2qI-m4YCae9h z*8|lv((C$E*E`HT`e(I{PMF@}s1p$m#sbPc7PBrqn|SrrtcfCff`om)Bv?5u{r%FZ z!Eage<_L!a+?*8`Q+4{b#<^LD?eE!Tky$L~e=K@^g6A8dz_dX7=A}uJamOz6Ogq0Q zk*)XKO|Iu_Jm;(}ITvP`UOus1$vEPzjG1+NHcN7M@wRK0(dy@}#XBU=FF4G0oVT#d z`L<3{xc;Pnf;O$`0;RGS8eL8Dl(Mf~_g#*4y;TM0Fdi&d5`Iezf|;;l+O0{^2Z>L z!%9YTTXx9*;k@YcNx0ADSkYImM|w65=9A*q_;OrYoiXj?JM)Wr4$GB~)UH`^lKByj z63fF6%OrF5@u`a4GwIfCe_rr?Lqy)(quWlMd8I1)E5My=ic$8~)lIV_OXX&A)xX$b zG+#|AdQ%bK?e>$*-@hD-PT$a6XRYt>^6^ezmMzm#u8Kchx9G}~khMz#bQC-jCx!YK zE1&g!5_3&isU-Skn#FnxgPtuci#;lxL(VHc`fq^s&XmUs}#=td`%__IK~@oo=42fqpZ#t}X{&vZo3jXoN~E1VnT!Kut0oZ=&4Y2#5Y8gyH!owbXvLujtu zz8L0jJ8nvK9bL*GnzNMG(qF8tOUPNHL+_M_dh8PBmXNm!TYo9uoTqTF=g8{dEvtlP zERQ|&JVRV$tBa&h>*mVv1&Rf{ro4?WuVr&=^ILDze=A~+P*s$cYN2srqk)J|V0lGH zO<|jkMfcN+8IxWLF0z02G1)tC`m{xQy6g1z%u-_cmaKfwC!KL(3fHQ5*N9^?6Lbsb z>Q)&$W}ewT%SCy&`3tj64;ili>)jUjaQbmuxjd~`m!7;*dZ^p<|KWco)LzAv)NXFj zJgY49dNIhoTe{_tLx*(1cXk#emSpG^z(-lMHBO!P^3~Ja=Bw}JbJFvw@41stw6xA@ zpFHEc^(hnD-CoF-k!mt9Frb-VkXV$Mn_7}uq??zRn_7%y3o{;r_)*({$vK%A*OXqp zW53RZiGd-Q72{-3kaEW)&;_N*CC>S|xruoxKACx`&iQ#|si3P$p>sGPvH3!dBDT!j z-LG#cZQEMsY(J$xNXvhjfY7B36UDWHmyR4yjJ%$kFrCMFas8qDi}Xz{9+7|0Z*)&~ zLcp{wUlO0+v#frWs} zn8@UgwBqGFwbWbE?u6#IyZVt#cpqpRS%>0RG(^|%(F!9EYGhesO}JNh^2zmNmMI7GzKQa%U!tAwNxT3klQ)DgR}Ht#xLi@oB4MZ^v_)VYn%KP!^8(>&cAlOHVQnluTzN6 z=gwj8wnMyXEsB=wI}@up10Nb02bMnMsoti(+;?Hy1&+ER*{c)ozWHh_P?KqBYRt1< zj4wBDo=a+3+KLzF?zefhE}k~4PS?4!xl;@4Z}a-nu!0g@NG>7XyPeX3B*nMZ_>B?nJpZEIM2|ROFwZ z(a8e_PlY%=7K(1T(z4`;xA#`hK(Cd(ZQaY7Zb(`Cv>6DbJvzhk(ErD~sC~bq;un>y z-Sz9u|BsH-x7YgV2?%ALGK#c({-*ZZz30}y%Rarn?_baO!E9fkLC^Qpngt0QpKEq3 zPGBq-)p;bRZvSNUZ>~!}JPsZ|@g;%twC#+eT=HSNKXfw5+r4$##@<|Up!58X4Qk1A zU)Sp;J0BD0IXESA<~^Bad!g9m31V;d9K0~yP&aw!p6!_y{9Dc{6*@^qU+>DEm0Yyz z*pxlFms5Al+&I^1(SwYiizZ&^PD`r0{8h&HdzkF*6pMM8JGj5yo0s-<=S|kzx73$C zRFP!ka*KUYu=L5yZAT3xI@8Lw)anLaS>vSi+HzT_!o`VV7k9TUpXsW6-r!o=`Kz*< z(>6wLU#q)0pp4^F$P<$r!WY6!kE_Pq5j;34!q{hD#JnYMZw01%hEJThWlebOfr90% zy#_Pw7fxMcwfN1(aWy9m_;dAA96@(l936fotpMWihO8 zwmIJEYH-vvRXREOu}RayXHP72t_nYK+nljV)Jr?Pk0uz{Gs_pj?s!M}o-H-|Wbc@>QlKqFeOvSwDfO7QK9j0A_sTEIpCs=zakA@t!`Qf% zy=P_G^fn8AU8vStW%fbNdtH^(zReFktoNBX)=RY=v^SE_uawdknz~?SmT=?C`3LHj zwDK=DVPiG!G5oMglxauH=?0aL`xKsa-m*EFKB-bRJj=~n4wwJ3TeIIU{@%$evYMY>T?YT;KMg|6NCaiTQQt{+ilwO*fnpfglk(^pk zf>_Wp@vIlKp-8L$FXP2qw?*Y@zdH18ODOxDFfJvoLk|v3dv`f=hFhA@$uld~KV9|U z5c`LPM^tn}w{WBtPOdI`U+#b6_wm=#4aO$k8VRRca>YxnIyGKtDiU6E{W;VYn0xsvT)(je?^WL;vT)7Z8SsUXUena z!$%v^I@PXD4-=U3%P5#}_p@X9ul19cvjw;b_Hl|fowJW~-SFG=zn-vW|IJ52x=+69 z&zQ<=C|pufdhK$h-+RB^#;lbaziV4fTXXBmRO^VV%a5(`dYwJ((Sn}Rvj%HU%_&`+ z{^aYsZPv$wpVS@tR&se?tL&LmVg}`W#>=(L)*SV{9s8RFHC8s?fAmX|k%1wdiGcys zl0v#A71W|ZiWRh$OmCpC_hAQt+RN9DU0J_M{k)P1mz#~*QU%vQ4h@xx`^#Q$QoVbk zEcn-Ln;*=70w0;R3i)uwMwC4}yYGA3%=!1%=QDh_D8Eoz;8ifmM|X!=OP&6lO{|}T zm$m#)+L88n3cKo?Qk$opoO`rfUfAdf|I18S_KfcwN1jS+lG^n-1>x#88i^Ax26ZM# z94UNo)4Sqb>rAnI7jh;Q*?TVif9%T~f47@C$F8VM$$EZnUHODtKX#Q#$p?w##)o#O zdM#YY{C@5Q=_7wvecVvy6*%p6i~Y$fui~dXaeJ*=P`f)xP;a|+{yw2y|8+UcZ+G%X zJuzN)CM`IZX<_5l%xxULTXMFgZ=QDac0taS19gFg*;=yEmIo3A*H2WI5r2PD9yNS} z)3~lOu`w{DV=nbU3txYvq&_z+x?DI^;_l>Sb4_N(b!^F881qCa(@A>kmIZ6?MCl!k zyKqgci{o;N5Bsv21~Ur;-i|4{46wNQUtBf&T)+f{yAR3T$N_sc~R_c|X4=(&F2 z((87dx>3eaN>XxGPx;{t{kx~QGF?5YIKmIjS>M$vcxaWy$l|s;6&$Mpwq&dU9FH{EAbf*6h{aj|n|WQ&}u}Y1%srWyL6NeZ@7;FC9(2 z`pm0oi_8B_(<0C4zxvkIvM}qvkM`_SQCY_>1Z6t=_s(9uI!n)Z>GLg-$2%V9TP%L6 ztGo7Al$+D(8S^f`JuH+|IRB?-^yVw6&eCt=CkV)x8_)6(O-$=reR`Iu{<)UJX;G$P zA>QKI<(rR|9Ok^GZDqXY^-aIfXU8||F5G>I;qq&#mCv)AUu`*iGwau@Ewjqkozgh9 zLr-yMBFn_ag0Ri$*EKh5-ivphwfAZ@@0`vxD_WEFqE&c~XRo-le`1d8s-5aDwk%v- z{YW|}xTenNN!c;`hs$3*`&yxW@>Wu2>!O1vseJM=nyxvRnVu=yKG;1!<9| zL-s8E6tq?_uy2|W)9;E>*(G!N!YeF-x_52lag}^+c-SEShwufF_vx>5KlUw?TsqI( zWPaD8%Z)|xg_bX0$Fzp~Pn9m;F->1O>u$!Lt|}#2<&XaxpI)dsrxTU=c8Olps@-nc z0SmNl^h>MM-aM4Kb%T>O*S6&Lp4nV^iQAiElU^L>V}1W{Thn*L#SfTkG!|~&#baa_ z^uf?B@#3ik!Bvf0I!zUtn)X&W9#pXlo!~O(<(`1L!+Kg4t(UpJP31fFUX|bOa7)wq zkF7g?FKt@?VQZ81p)cpsm##SU@#a(Qs9FD9v+wtO^;PFezig)4ZovJT(drlXGQTjE z=jZ3!Dk?6i?z$`L6TzOaGS#TcZMWy@gL8~Szr~zXz33;pb$P0(vvcgns948`jk|I$ z?uc~W7I`b_x@44|_KDLs)KtG5J^uf768GJN^&d1pY%acI!m~?BLu1dw>Wx`(*FV_& z-?9DU>jkWV2emfX#jx&>7dZ0iTJ;ZQVVRjvpUH^M`Fu|JkLujNe(&BMXSm)R^S4or z#d5xpAlx)`^dHa4YuR-r2Q~+4jY;uwQ$b7k_!FveYwck4Nv^FNq?8dtCm{ZqffE zCdDL^Y0Y=xe&Q*v=aQg%0uDxZcDPwzCcItW2iLJ^HD{OnVRY^D> zu?P@PmJ!xFCM$OCVr^4d(Sqczov;7!dIg>7-;imjxFqAt&f_=Ubi`iF>pJP)F*SH| zYukbK*92ByKDg+M!SdabZxovKmS>$Xl~;22pCG;TSDV7$Ub&g_s~A7_?aaIT%*$Zg z_V;>8Cf7de9y)n$ZvTbdGh_tvY(MmrZ&pgMacpwqkXH5J*HqzA_fm-Kt%^PDG)rYQ zQ(wukg-;yYFEwTupN~I2<@E_cds(aOARFJu#=I{StU?8fW=W}T5Y9VjHS@UGw|nIi zn!gL=?0U}{{OHx}eTzzWb+F6cW4qPQbt00#=uqn$fe9D4y_Z(d`ypVhC3sur$dco$ z62$}kSmc$Wgg*+skNL3hq}=vbEy+QRju!c`P(JI8$Z$VdEtz5N0aGhhKz^YOS$L!Dym)EFg0oB-nZwHXQ&si zpXfBL-^=j)_8GsI2h>F#@jFx^niJmmS@ z&j0@ZbmvgUT-@K6aVMLVIRRQFy3$T)9t^t^cQ&F3TdGyPkJXxE8g| zIjcRX^ZODo;mT8c|+fHJ8fTZx}fmY(v=d%s{#$e%P+Zlu-)IYOw&zC zU!038ve%-BC*;sVi@h_GPjiI_%W*nSUblAEro?62d_%8W2=T1_cB{`;4Fv$-61Ma?*t$*PEI5?`;!goHYH+ zvqgJyYq|Q4^UQueiS^vZV{3d0_jYzEs_ zY~rumQe^Wnk!`a~vE7-+n$FWNUp45l6sla8oAqm5n4-MZ>XkJ+y0V*YD5-8*+}x`5 zLH4T1UsJUia~(pWmPR{fd-74>IZAC`Qvbqj#7UzAsuUKsEES7D8 zPiM|KsnP0`VB!^N72=%u-4() z8R1)2HG7xnU+|w;R=j2X;+x_;wJq8bT<;gZvwa(zv3z6XiyJzxZp!S*k&{(DRO}j_QXg z7OB76e*ciffz(;cPv#oiZnp9)YJN65kF%V$Wq+gJ5d)@=`??hQKHlgOEd6NIop|f* zA*QpQ+rO}*Hfonjx-4GA#K54&j&WfS_TmVfM=aB{NgYPHI%Ti15Ij_}`^bK~mL+WD+)S7hBS{>8RU+*tAbqmgsb$<;zO%XzeK zfAx*Fdpuh&(r?NhuJY3{R$lzQp7p0Q4N~6k{gV6hN$>j#r>=YIUSXktlB za)jL>+2hISO?DT`mR)s-HLsbX%X4hic3tgn&hB52I?O&lWtBlU&!(&$^IXk&WX&a> zCBMvmwDrTiPQGySiW`%<43|Gn6WeZZ-k@>~TgnWJb2p98$n7_M@wwMNwC&Tw@=eLndj8_>wTBa=PfW~R@UqXwkEwCVhMv0R z7v!CuXN1K?JxcDLsw91*o5MNV&}7pbK8a=vaU;3Qd|iEa%2lp+KTB5$X^WlD$Dqx) z;X+9+W3gg@=X1|X9{U=+6{`%_+zhz#D16D8+Eoj~Zirnv)43#T%L%v1bHZEW`6|Dk zbU*4p^Qi5HyUdd7T#bCTT4?gH9$&UZTY}F>inA1-ySUFvg>*?qEd{(N)EukXf% z6Q4CPJ-PgaJtK+fSUzf!@9Fjx{=>|`P|D4~V1qf_3axiQBmU58$tg80zbF+~y;O8N zMBrcF+9x|UU9wu{DXs8;ZMkPnXL0uI8=2md;yR_p@374&nk;&C#m1$5I+yn`^&g0y zA~{3E=)%9|hq4tS65l-XlCJwbF_`=C{LVKwH~)US+;;Wf&-3H!m<`(7jr@{7vhaEv zwQV>tak64!q?_3E!#kQ*%gA?~wA7#IyDl zzi#yY#2cl{Z)lk38fk3z4c>lzRo32hlW#53KT#3xERq>v9q`u4FI|U+d0J#i$ZbuV zGM>{F_z}liL3U z((c~jwwWtrte|GCgKK2P6$!ncZCmO* z91h>*Z;90s6aODw_=0u9^P;DnS+CzV@LA8W{+GSn{n`9=?rMF@k8HRX?E30!;K^0X zUVg2rDQuIwUc>*s^3><61wC19U5y$%xlF6FMDHeA+~>M{dP;bCv@1KuG?u21aRNDo z0`ni5mF<6h?3A>I?ON8|;eN{Nl@}KIt&9xroVHZYLSE+VmAaR5uZqHd{60K?%eVav z^*jIXU*Ro#Tr25{HE-bQqe_B-doo2`9Ig}@?D5?ly@mhM8kI@*iW5ARl?nc7{KmM) z`LSo=v1b>LiwUHOf6zPG_Oj=Tq1U#D>sCyg)bo&ePJYy1Zkbw>)zuTcPKw-iz3=wO zon_u)`>)0o4Elnnyp%7!pYipG`abs({vu7!+XYQurlwa*Xt?@&?SB-aIYZCRJw?1( z|C@mBQuig{QTevT%t8V8m8U0ovlw>=PCG0S?ba3R))%{UTfx=VeGeLMwXaKJn#>Zp zW?6=)!;HEh&Lt;~osG=zezhi= zJ-70!(E8nHx%EJL%yF5|deuo=8@S(H-}&O!j+XD4ajqTv5~dyER`C10L*>^Kok>^t z&T|A$6mXfphwUP3dLjFw`R5)bYc}X-{0o}&A8kx@i`MEWUnT~I_bdzyZp4%-&WXjT zo_WQodBvF}nPsWLB}JKe>CXANpczm20=1>V;DaS?%YSbS*dBDcy-TShG{aG>!f;v3 z3SXaDSNfNOtA`GkKl$NBGt*iKL{RBGvnS6sF`p^$MTNpX>n*y!}tSf_1*C8~*w zme%~Nxp04SmHV5@Ihxy>`aYDkhCQ33w)vvAsL(TMr76DlQ|eYIm8#sH7*@Al@C$#d zSl|Lqzsuj3=2a;1sEXTUvhI5RZOe4k+?rV36eZMxg(Z*%NY z(Q5}8&g;T;=Y1!9edx3Gr0K!LB~0~)Uq~{|iOt`%Y2B^+>H^nAZclu%r%sJ8_~P?j zdv91zn7hJm&Pub~JN*Zhm*!pR{d{e9sngT=3oReFz51ynCGj!3epZ@*=J~%*Z(Q5( zAa|M8mgdHHCzhY`m@S#LJ*`Vdq~PR1sTlUx1)NLouaxmgI>q+;*Ngi9H;dnNDjd4v z+Qrem_^y)AviB+K8Q=amcwe4xP z{onB3NTz%1RR0reKfP!9`FMYahT2=N1ome^liFq0USZsIqj4rnfmt@+c7yZZC3TMc zxE;~+ST?5f`Su&{w9025^<~!Y{fjnST=ORA+yyoU1~owj23ukhtao01c^;(Y2dNl? zOA<>`A#K335iiSyLq-1YH9NcHGNboIE!RSq7n4dFo2?x-$iEcf2w?xtxrMcCzWsyn=LJ*VvT)Rv&03aMzQpAC zMcKw_dZ+!ispRTTycGRNbMex{QZiT0xJ*xcWuCch@$GK)OYxcl-&8qttaw-YJKqrf zD6@f~Q(eihae~JwnegIL&hx@s*E{Jmv&`y#ZN)f0RQ6g>=C)0nvNxNp*}5cF`Rb)q z3%$Ejze{-r7hg6rjI()Kwech#=mX6QO$m7eyxrN2yWKX}g|QN+$u6#7-_vXo)%TD8jmpB{0E zsHvVmIqhbi`sLgor<;XmxORNhy?ZMl;ciY}YuawxUt!Zj z1S6vClV@(8ysNtT*%KYjwfQ>^8~16n2U%6GJmYP7D(1B8g|}NBS86!NSs2f+d^uVC zt%A179)-mA7ID_P-~XS-6uwD~i<_1cd+M}NTvTP`xzk+h4rdl9w11Dvv|bmVUy!i+ zbHOZ2S&^8|se9gt%$U9UUD@qx+X_2BU-jnFJDSS1C?o7b(^9T!3qubU#VmT@wnngH zZq!^Kp*6Rpt~amH<6k#BsAT=G*r2u1kFKob^ezv%Tof99d(rAyE7K-zpBWXbS9v|} z&YI(%o2P%|TD{%-;OZN?8#dhy`Fc>XJbIds>_wT<6WiK)Tjm9rM{bj{Z+(%*xnA=| zb0BN%i;xZC*&=)TOoi?V>{zm5`)=Oj3u8WgZuQ}dI=H8);98}8;oFVpTmo5te_YnG zU1U#hxyYXGyIgz^Y+2tItK8akS@Ze=sneUvt_a?}w*2fL1-pO`iqZcC?dClx-5(=T zbBOQ1$b8QqF%G(6He7rUHyl`+enXfwYI)?@$sNx`8cffw(B&{Ww`3V>*7EDCR`5Pw zwdV5G+_PJAq<$Rbj#`i>wf5|LnJWdEyuoYOO<(PwEB7ms3mz$Yu)|J%ckZpKkzH_ z`%mM{y`oVz>#WZ`O4Xhg#Q$!_OCQY_Cs(>XP35rNb|Q18&CcBokGs#W+LKtjYN98< zJE+NXG?z~+vLXCJ=uJTmi+8r!9YXCJDYTxQ)XE30*` zi`sBmxbx0pN5ze+bG2=^oO@t0+qkoO{W+G+YK$F6uCcFun6p&ZGWLeizgNv(`z@qi z9QTo2RdM9v%C6;ewqIWGxolSHJ-=ds5dRXrgQ6Uly&g|i2p3FI|NBF8@m@2nzC|ik zJWKz&3tBa8%6P+Z=;ATu35$H)1v<~EC;Uw8cb8tvt;m?9f9i-_NY`~k|2>UT8~MdM z{zfR>J94#U^P%TG*#)`}=0*fRoEvdn=>3sf)2h_3nY(B%-T+!`8wxMw$=2b#{xr}N{?i`Q+yQhDl*Xd zUPFcWBnyXG`}?B`1#%xbmvr^paIdO;8OdF>f$RF@ywxvdoKCWGzpP=lV$LjTTA2rlKAtM&*P2tn=eCp4=8V=(i!?JVkIiw-1Sc* z-GZ`r)ofz@GEeGo<-J7b7Y@f*pQkr%+E5U1?Bwegc{1v|J>S?^#b|u675jQ**0F+% z2kZZTx~U_#e9ozdb7vo1{Wh_cW9z#I`|B@lw0V9dY?I6*i)UT=0>bx$L+TIMK63Br zn7_gMPfy@S?!A-h4i|p3t`n+%5MFrbbNG~+P2Hci&3+Wq_4vt7xlp4-`>ES zT^Kxpm0O^1?i2OvXVnAs9!}dISS-|4Zn0k|Bx5_9-%egf86U-JI|YV=TeOskf#C}qkv&G=%;MtAymUxq z=~$GGR8!8Kn(Kc#K;-!Ua_PI*L?yL4`h8q(GH>FYsJMdjT)Tw&tOSFD;o{fM%z7u5 zZ@uVaZ~m9dKb(YGkBHYX*iZR*XWJP@Z?@Bu-p#pPT=#tA+xh$V*D=pHI;oGP$ZFCF zbx$pg(kOQ>^Q$*|CUq4~c|0Ta!W`Z6MJ_ViCEDVDeDc7T-&X%%i;Iy zx>JJMeK+{0S{dADzgzTP_E|#17G*0RxlXIA^Ukh4YIyv^vh|(cziIxC7W^3yG~MX% zlfoE@FQLm-&Ced6c5Bi_Wi#Vdl5A}g!lvJ5E16?t&~L~rsw1(Z!<+40f=!=m+s}W~ zIud`W{7s!F?2fpG z3pZapAd~Ldq_3&7_`YRN>xH8NtDbW2?x@ZFY_f}g+uef~Z^`gJFFg4C!nBq-=_2!u z4kY|zU$k37!{@!z?>dg}Cy%Ex9X87|IHKvPuIcnCwWs|-bz+kz>oS?g?F;8y7x`>k zc(SZdGKM!$G@d_j%H!An-(H?-{kw_f_p8S}2Mi7@bqwAi<|H>M)gLV!Q)bDE zmYL;T%HK0--Q%|Si&NguTso_f^GJq;s)XTJR_#QVSHD9KiC$6{QJDJ!ZHzF4#oKNb z69dB$HX>V!&~#s(Sd@y|-U3ZgSJ8zE ztgc%5(#KgYA6a*euhst{cf~_S6|N4i8V32+8NPQXX*ueN>ff6?bMy0_^t8A0_wW19 z5O8F#h0uwW7Yww@uZL9itUQq%)7r<$I$2A^M_Xe>M{<~B;K9tGoPV9%mTvj&6rcFMsGE=F zj&3czZFV%PdG|Wc9jym+iucZZ5gY9LZ(G(R)jhR5hXeL~>DvFSm2La$)=la794ZG{APLY#;-H-e~Sj4_Vbo_HcPd)$Zt|A|*q+ufubeWc;!}&@LPaTt!;+i4S~xDxx};uqu;iPlVr`N5L;rt~7b3eh z>x7s!shm#isLAZQC>eF0`R&{*9D>hTd48YiPVo*2ng65dM7#4ojiU!Y$=Y0Wo~NPr zNa(1fp7P_*Zx*cY)p|8wP;Hf6)J=1j`-*$nQ-h<7oMs-sr@XWM$FlrY^0)J~O+!x} zSSFBsS$5Y^=evv6S5N)M<+Qk8L@SQtX4xfW3-{H2nJOJ zW0j)CvE4V&Mh$1EJ~*?RiGg7i8v}zmG2JCdCP44dPYnSV|8?)KT@196S97l+Ueq-TlcCfg;~^u9tjrA zR=OrxW#)7Hi@D?^*3^DS4QqC`!)sS%N?Wgew(sG-i{EuW@r2FYmG}1Lx|45DX#RNb z)33O@`pN9R*s@vfyH@}1JFO6&bGzX5w0D(kiqls0-42jmSC=4}b=N5LkXh;PhUJxO zch7t17ytBv#F46mz(wJ$*&K&FbmbO?bX?~AB6K*U=74I=#B;YyDz7Ab4Y0o!>b+e$ z>ukC2?uD({EGNZpiIson@7=Y&v}#Hhhc3hMYfF}VoTqY6-@xZS%h?Oj#?sr~ZLhev z>i;d@)8@Kg{g;|Q;@;?aCFpGC&eF$APg>jGnpih4`1FY+hhuE>Iv320+M9U&u+z5l z=QF;uo|`ZHG3J=qw#*OfzBMIko25p6@9BCgxh?(IvyB&LRx7b7CiCb^s6AeFtne|< z(*JUQ_w$LB^(F3zex1?v_;^#)b%Q?!oVDG|tV?rwvf9?iR!^JInz+Pvf9l0~PPRe&Brn=V6`M^`Z;2Fo1 zqx>HgHk@B9;K91x(=YI>kw(xJiB{D4s(Or3P!OWIN7*#6Ctn|Cdf`fqY|+qD&pi$$(`<$Em&U|!m~pw&Zf z&uo>$R-0x{z1jJLt5#+`i~cW$+7*|KXE$`Tn(P+8KXdc_pR(uX{Qdjr`b!Te>lo)I~ky8i9W>r9?6nq82#bLuyjOM-4YKU=)K z?LAd++PQUWy}t4%8fD%Ji})AvYu#3pvo0w!Q%)Zb%X*|?wrKu|71LL&n90ZH^gVWd zVC-ATC5{murtQb(or?Z_^a$fNj?ezTWUhTzTRh+CaENiX#e`czey`SQ7b-7ud0~+i zy}0Dgj9EJbynp+q^oQ&_)4fM9JWl=6DTdoEcYmdPXm5*dF0b}j@wb2W?k$%WzC5}9 zRPK^bXNn&j(@tbN%3$5H_~my84g0u@k>9<(RUG<$IYuzC`jbY>E0xLdPyb(UukQMA z$47ysrYoYeH0{6KJy~|Sv|?fPm+H?f^m=(XJ~Dl%OpiEKDyhA5-J{fXqSN)QpWl-B z*LhX?@XPxjPMt7ldVbL=?s;l{(f{Z?{&ONfYTGOAk0@o&{5o~>^mOz^42zC^cU{fI zz_5*#$XP>31yvY_sEt$lP)LSr3Q;1OX=`#wlWcE)!;Uqekk{$_4eBaBK{2$An zclb1(-CHWR%PC*-YQ^bZiJs7z8#tX`ao;aL)OCFEiG{)0mK@n(x=%vSZ_GKrRm*Yj zxxcGCKF(^NDE!RtSTN(uS;F?ki`$Odum526bmt8H60I%uy3f9~hwo8*nX%*igs$di z8{61<^v{Qsm)E76RpVKr%--V1x@`;jChP85u6=*VYeR;9q{gs1yN7Yr9%QYS~Ie9qa(YA`1rTf`Z zKdp6>S5tfE+TGx~y1-`^yFpq1>TD%R^$C-Fm-MXo0F`MEW8B=!h-LxO;51gDmJ#LS~qRxw}>n9uTySYw7Z0*MjmG;kO zf8Y1LG;M!P^?$(wN?|2mlHJ<%#Ca=uH(Q0AIr(&ATjfsQYgaZbSa@VY%=TF8I|%`w zT4UL^K6)F?<7|3+>%47!-*o=(yiu%S9P?S~&`r^Ox{obZwy~ETm~hjQFZcY~_I+>7 zwC`Qq_`&tXPae0crMmkcbtr7$*dF%z&$63ej_ymI$$Q+4vs&d&?aencKQNy@9~-~& z%mULz9kb3o$!>q-GNZ3xb{c!+yK^F|N{)4}nyT_!@4(YM`r;_Aq6g@^r3H-fqDi&o?aHG-b~IxP%jbxo0ihpJyH`xNQmF>-wb2 zT)D^pXx^*({zf{)Ykjru{>0qdMO%ErcUM-PXlR_$sH&4)ac0BK$;qxPVOM!|%r`jy zKJsdxps=mLrpHSf15+d}?f;w>@%KD;$}9BT=Q}R2)^p|)u1im(Wf}4kT689VI?`_M zywGMts7?E#eT~cZwXVuJ^H*x^ikrbN7Qd1d+B4U7@;gnTtl!L6#NRM=DlMFxtF_3b z_xQ`rjPnjC_$!?@_4#b~WM`j{`~e|<*OqD4zR6CPcDLRZJgIv}v0r$nYR^Nxt7nwj ztDmfV(&(q&>-$ZV?e&_fs_tWw{l_C!_c)oEM*m^S?W=qKBKyDV|G-jbUV zxOt+pZvSz!R~1Q#$DNudR6q38ukt)$GV3_YX6c@oLs{o1zkB#I<-sccqzA>zE^)UV zvKKpb`J+ju+wwRMjXmO8C8730g6iGxmz0EF7f*cg>2#TXKyvz;BPYt|E_`o!H^E4- zXoDQ3T_JCO(`pnanMP+My_BaS#liq9m(bHnzgyiM&n*M#(XN8^aaqBBcE<6_X zl&5cl&W^AE@3u#aMY+CNn3Fhv z!}c>dHn;Y_mEk>iC(*?sqV8?Wtw#^nnRxd#rO)Wds93fC-YX@x&8PN<@%`GA@Ithq zpjo+HcjMW2{~A8_$}*eT7h8F%irmmwW7xL3;iOkUU#C`%@Fc!)$yvKXPu8^m{3>-e z%4hRL2ccOvcDI@e2(NqW=Tjp-dye{@-67YzlJ~9GzU`&CxMkB4&G-J?^EaC=OFk5&OnFSKnmi|XBca^uVg&7QqM%NFmDI%>+e^^NEGjz?_{zm;E3<1L@c%=C-- zw%Hw3?`fyqA`@zE7IyDkq|H5T;_2wLypx(!m&8Vx!Nh=-2-Wc`$L zlgbRYzuLJhcFE#ze;C6OUKngXAH3qRx<|^rEx!4V2Rvj`bNntueST@$_+e7;f@ixd zQkMqZGPrZR=&;cbH@~>3>`SJV@+^n%1y6Q)Ri9+PeeU#chkehNyxIT6#*zDYzZ$FOnF$PeQetH6Gi?@F0DQ1S^S3imNoB{+?j@I*)=YH zH)l^f=${kz;A;JhxX*7q>kpl}#xmQ}EI4NwYnwmQvh1~1qUQN`j4QvLRJMFv<1TCa zBDFZ!UHp;Nu89{GE(|^{z_w_{WyAjHzzehI{e8Y%(cr4cZH6m%6{h97ru@2UChnD> zQhF(o$E=!#Np@4fj@?gp7Wqbr-uiA@wVHQD-;^T?wXC;acuP7pu{*71=|Az^MD>%P zhK*f-=&h+4;%fJ&3;qr;TD+Shb<=K+^kZcU^DZ|wsoYcvaj0q0vU3!ke^GVcg7BK7 znpUSnI4|@YH|&4ZbSS>i^Jsjbv0~S}qdOOrOtVSXe0Y7Um0({-zTNsbTiy!RbIA%0 z&zP3Xy6ODntm237RvV(2;}ud@J&62ts_=olokgVYin|_1UH`i^%`bP@>|hzAH1DS3 z)-r)x&Qs=89av%5{iCz&=Woq5N2vjXlH5KnZn(8(|5-qp%d?f zZk*kec(d8Ifmbny;rQ{ozxQTeJo7@b^G`sze|czWXzA7KSN+$^R(}&{i2M{HXZ3Mo z*1vGm{ns~&&CmGJ%%^X6@uTLy6~(`ddH*ke=>PKvuif>9kGW+le*_&)lvV!tyhi5W z#hN$A4X?^vZxIRa{-NrAT;k-_Ba4&wRQ!Duyx;Wd4^h{7eX)yvtHz&|SS7hI&FxpJ zNa;kT{XN%~1RO7^IW#x5M=iN@$^+s3A{(a{u994Dp|(P80n3~vPv)(e)#>Hm{eDV~ zjsD~t4_2s6F1_NhQEmCF8QQ@nGgqB7k^P*yt1rsZH}KqrB`(L4svNyaudJLZrFnTt z%<&@C54kg+Oy{a{)w=36HGb7qJ>$^gU3#0J@R-h8dvZ?AnrTw|=d3&#V>)N~wl!Z* z*DU?cv|lG=zj)@R2zO)F(?T*&JziC?oLZA6x&CC(ex2Yl-Rm1e!&Y33th0=+d@d9F zDs`#e^HW;;O#+1HwVS-!6m{*#Q=6?j!X~RV2FiEN*qHh-!6xq*>*KI;Ri}#{!AfFX zTlBl%z2G|5)S2NkxwPrz>88TXc0%UL9|M$1I%oQ-Xl&Sd`R6%t9}D|UwnBF$H;P|p zNiHSaYAd}`L!InYPk9CozT5%GZutR{Mi&37=3f8 zt*7MTODm+CVwarE=GyAs)cH^&;JaAATk1EUB}eJkQ0wW76`P z)?M2+uUxa;yO$+>T8>mse&#lo?zN`Ae5y~j^ZZ$2sh8B@sa>{p#>opem1aawatl5G zIjD<2-}-RSjgM;*zD;oRlNNbC<&m-S+MZlFAKPYySv@9C1H`|os$9rfbor!o>9=#d zTAayueG)}VQnlX~Ddn-f5?egEG}L+a^voMO&NRGu>BZJM`*^HV4C@CDuI!uVL^ed2 z?hM$Fb>q!pt0O0wHJv&Nm^9+VB|T@~+EW7f=5Yq^e2-M91hS-C|=R1X%In}3}((Z%O) zT;g}vnG-C}&I*gU_^zePHJo>5!5)RO2#dLzkJdaf$#hH8@O`=`@mEXjyS@i{2e0JV z?{fRJMW@-%!R-E79`irtPv;qb+57O&@{_K!SWIua?RnSJCQ-ocb%{l{yX|mVwD(>@0d3!Sf>DZyN&Q7@An_aqmhqdX= zJdcPn389HA6l04F1yz54;OXQ%Y_NROr3o!1+*<8-4EKHrk&bn`v3AX!l81+MpSHc3 zvO{tAlH(J+pBg#qP1oj-RqcMfl<(-FRYuPH6vMR^>1<+`TJ?U7=< zHtJKd-v^wX+jd8_T)nqsgYl-jIi*bfk(NFWZ}v+#?^FMtG9yU5o3&cR$XG?Ii+4|R zy+O+M82tvt>L+o|`}DtO6wPlmf6Cr=Xa3Q7OxezI0S}+el$$SivcpQ6k@x4qn4A;M zp9H?o%#*#?FX8&}QPRZEQBTk0r_?d)k|3dBew>oY{E#2~CW6@{jwm&XQo!)LrP!aMKVULdPUYxnd z@srymV_h>}8^IpcuJSe^*`vl<=^>Io#MWJZvb@bkt3UIT@l?LRgUb@Vbo$&5*&fxs z=%FOh6>xD~UqZ{XBTl+Ub!BZfinT?|`MC1ird?}|SM4)OczgKzlS1cj+OBHTa^h-! zK3jMC)uAcXN0(h$QS^G+rXc5e6VzW`QB3Ys{XR*tv#rMNymZi!&bUj5JIlY9Z%ep# zy?!nadyD@ExB134InG-SX_H>$6hnr#a@M7>nw$rVeKDrf$ANsZK zVAW~ijdeY`nx)!N++{4)+g465(3w~>MRe+pDb3PXtN%TEmtZl^b%G72&=vlo3-NjT zj@{nA)Ol`l+@9*_JDpb-O{%&sRMx`v+(g)H*NhL-ZK7lMXZMtt1^aE=mw!g)n6=ew z;U=-JEwgrCX};w=`PJ3JYrAJJi++&2bYa5E+ZSu@?)^OXwnp8$s6^v%brG*WLWu+zlCj?$qu5%X{efLRr~w;Vq5r#lJZD8Be^7mr~oM`p!Y8^7f&- zAN!sho%qz|Uc+G?-{&`N-ep*LT3`y_3 zYKN9fW*QpTq--|7;qy6G#a!iSuh-;d|86>-$S{4paK_D9RkkZx-al))cId^HdHdrh zUe=rcxlqe$qgaEu`pQ+)7t4HgYkQ!U{U`U|$DKL7`JuH3S-x(1{9bU$L+M#>%YSAI z7Fj+jy2)5^>es_uJBItlCn^s;+S9!DoZJj%{%4Q;`gmr`l|Rh;7XEQ(Ex+cJHLY(% zbK@d^I%e$s@ZO*EW2BF^4&&n-CHDrYx5wnx-9Npt{qOgm$IWd-7hkwzo{?mCwBpyq z>v#C~?tGK^PNbYsKz4J*VSmn~i(|9H0O>y~4+Me@s;_gZ9{`zfU!@P0J8+Hm7F@ef}g zi3r8`HF=6Th8~FCut9usc8yu8pk|loTCodEf1d6<@yK?nPx|D{uA?QZTwi~*S|TF- z;a5<}%}Bd+cW>)GPfx77 zs^fKb;ht5S*l)Zzve6ZpS>&Y^d4%k&(sS&=$o}`)y3$^;Zv5! zg#A@`c1i1BP?_lUS2LAL-IvT0io9BpJMWG4zejdD%k(C#Teo>1|K;{e-hwZ$O+1@( zr#vSuRo`#@jeOURuI1{12fx|)BnbJq3cuRGr(I}w`zuEh^R~xKUlvG)Cn;Y}5KhiD zmOCb9)64zw`i-vm$4Yy=PkrCOJAX0j@rzo@38yNyoYc&io>bzYY4y}6XS!NTs&JWy z`l3s%dOMlKPy2S9j63o)@LwI9^;63)Cr)EKU#rJK}L%PB>Un&;Wy!h}d^XBp!r!D1QJ&Ant_{8c>nlo!<-X80pSt$HO*`{f} zf%)=Nzkg_&o;{j;vSj;;x?>j~&xvekO4hhOVfn)>-rxNReQkzEH>da5HwpFiEtp#3jXM5u-&c69ag85|5KJ0PgT6^X*Ptoab=?gEc4f(gt zMC6i;af|jkrIZOB)*6re#0A3*&E{=%?U=qTd(Q1OAD%I!nNBeelUw!i$+e2^J+AZE zXP$Ds7rAv`SBl@fiOqIr&K+#a-rMv0e(0CPz6BdEu_^YfFELo*rWow1f9_8CpXN=C z5%UfOGyQ$2{>=B$%Cjl)hj&=?Ki|{%eBZ?MWnbp{o>gCX!Sd|qAJ@HkzG&D6N9>Yh z*VXxIEd1+(j;`Olg|?T^OP00;KeRj-@Fnxbl@D{;p5NrU`;m2-Y~beK{K-l)`5v;~ zR^55N!1~aJKda=6XP@RzkSp9it-@yi%nv&sFo>CjEo<1x-~FPyPG{NsX;-SWbjz;@ z{gG+kv(By0y=L1CE7dg3qY(mLDkRe&VLMUYVThqQ63_#Ts$}IN$7#FS^-|uxMS@}nKEZCO-+hiyXQi?so3tV zKTnFbKChg2{MNIWtrt`>ULCcPGFh*bdH$!#(vFOs`wE(Kr*i)8=(GZuRu zbcXQ6ojMWpV#@IfPJ_$dlB%0G$Y;-(@Axde<>l7Nl11jlPwI}{RXlbxUo!rG&+`wA z^Nt^%V|gaQvTc5MU%Y^K{1m1=Dw{uU(mANTiD$i+b)Ba5$60sUu0K`XqqzQ&Z{^&I z^5s)+C~H<*zEwR{c-+0?(s?8H@}S-GAB*?g<2T^dFEZ_~p4&8CXP4=co2O+y6v`iV z^61?i(=olXc^22Tl(>Q~-g}M(oSQ8E(Y#`Aq`-vbAGJR{Pnve8+dRUma>d8b6_X>M zZ{6hfboSbl>w{$C$L<}kOSPXRX(TOe(|szwaecwlRh#T`yw=Y)xVv(8*52iB9OGCIXMS$u zDizNUTztp%ci;ZQKXw){99|`2aiPxs-lKW>!57Op^!Gp2{p){Zb9ra_{=-7|wt0Wl z-}3*j$h^=EQh(epRZfjtQhVKLJ^RzG4Cl}Sv-}~tYOGRJO zmbNrYrn5y%bGLo-ewBFN=)YIZ{qFkW54J}(2hXzzxff8F5t!Gz{6z7o<{OcLPiBU` z-s*7o^%Vccx}03SJtsVE^DjHH@Fz%j8?ox^p8R_4%~$nAt^BQD7uEJVU!Le&ytcjk zXz$+@nPFEyF}6r)>D=(%Z>1Kx%;8A#%cmB+~4ME`$6&8BjtN)@|y$x z+uDBT%wFJsvCaNb(Yn}Pd&ARq>3#K%Pkv6SthOlSO1_wQS1WE^--b^fceUDswx3db z9=3C8`pQqbmF*eFA8gm_Q(lqQ;AhvfGX6ldh-;XMvC49>A0OZU;D4UBE^R%Z-%g9~ z7uRe*IA@FX&C@y0&!znH|1+Jt*zeonLvw%bk1d~;_~y)s-}A2sXLfF7V&Sr`-Z)ph zFq6d_>r?AxUxo>5#(jMH7j0xos{5(kNp=PX1|h5?OW;xc6r@>w$jH*v@XT`QbkRDu z+b2F9>3*eJHaWyMIeOYtiAf9}gSPk0o?x*p_|dUrnz}w~I@g3;(^x!Fc>5H+fQ*|9 zb24w5GjmQ5nUqizvG3qR>-#^B&0jEQ&Wz`^=_=mWb{M)pKXd2%x!U>P?>*Nu{&jq> z{XfYAjDPf+%4TSN68w>HO|i3EYYj)!xkU7TqqM^9U8Uie2R z7GDG9j3er=KPu_1{1IXscj$EDo<)zYa%``)Y7(n)JDM$6a){^g=12c_8pMfzH*Y!m z{OCFTE04Q(s7#9&Vu%m>IMraEl%x2*u>SRreG@s!EYN?=>}ow(cE+X8hq+#*8NHm7C$8&!@Z_uI%IAz$KflDxci_X4%%$v? zrp-Gg#ms&4%z??fLJfi*Kf5$(p0{sW(J{@+M;|mMN`IObXi&6mu~vw@UGfc+$|X^c z3m33#>QODe=`poYs^|2!rTvjF9|?V2nX&4?%rhb^U-tQ~(%s;!JTvB)T<8V4^#&G+ z($nhnvU$Fr)8govt?68G{=>3v@8>+PH9z_|mRyRtuzB;Yse;+zkt6I8kc?1JAal~>2@K1Yg@Ob9l1K&gKpgvT%WO|(0I0~n9BX6^5kni{!*vi zw%wVTd+!*R&^-Qa9DDBn_IZEde4f%Ok-Oi1ud>X&;co6bC1YikR=QVtZ&5cx*^8A^ zT;^Qe?3&E|D{|ez)aRA5Z@pXWUnf@o|McJf(A7DGQC90$r!iG7xP1LYPvjP{o@B|3 z-*Qbq{BFH0#rK$t^RB|n9dQvU-PtdW|C_}w^Hb_xKeH6y!-ghyvw3}y)AlKGT>HRS z6L2`XBKmOoM_bB z!xbJHbJ?JOvs7L0{emswKJ4E-qRvm-V^vjou%PnB%O4`IUuu0hQnXCgTk^SNZPxx1 zqRD^Ow!Pl5>LJ^_iy!#bU7HkrSZ4R)GQFu&jB0~2XZIYFpPpK>+;VnHv*Tgh}tYy*)ivK`{S(-wo3W)J3aT! z_EbN%!2Ph@-+Kzu??r6qn-{I@ofvL=Xj|RN7V$H4)~36BSu1DA!4$nGO@rB=+iKTd zv&z<+0k$7@%-`%~G0&?vb+gEvOUDoYG0J`3Ud#}9@pXW~fvNoOAJ5W_i<5uy>%ubA z=skP>3B0Pb?{Pfk++f6KACh&?<4)V89nAOt-7?{4{gtn{tBqZRht;}iU96ACr46YXQ-0Vo$u8_W zAQkm@Ms)1|>#u%Uo2N`T{K>xQShjlj+CTg97hgGd{^MMppN{{8Z?sCjY3sGE5aoYp zdgOzZr_{qf=Rf?3iy8ZqL=>ZB4-8;s(ESf3zlPsfGM|f55h6bwa6Io(#@J9cj7&jx|lOC>~-B0B0Fj+b&OJC$!MJ6RyJ{8vnSZPE$LoribtX|J%a zWuKe!=itf%D<)4}*zh3aQ>}-G-u;7j^A#ucyxY}!xA^1>xysiZVQc;@Uj4e>|K1_z z>!&?$=)Ybno~RoCWUd6eMX-Ya-Pbqqq@R@v-Epu{ze# zZx`wdToIlAI*-Lbscb&hk_2R|RS_0K1kC089f zyH?FXBKS`GPU+c~W~H;otnpYKRu-NtvG`LDkKT?BOY^nWt0HEJ9i1`d@rLQ{U-Ez4 zR?rl9yz0cnZ93;YEjXSQe>)Ml%)^t%Kz{9P700Ca#XZ+7e|J@XONh_>Fm1I~TwrL) z(t2J_`K!E(X6=0Ox%1=i^U_ayELFeRsK4PZdG5N;DejT?5 zcFa7e>!Vs2z3=^s9oqLV7wugBXkQmQmr(SXoPu!2ZCg%mn{n?$%gX63ug_|DOl?gx zT>r3uRatj_dh_O!I?npleH(gaT55To;qtC1=33PHF_d+q>u#^3vZ{$6r}y5nDv#S- zI$3Gwq}aq~TE#6k?FVOUSM2aym}1?QXR#)$Xm1kNU(w>2xY#Y(rfH$7SG?{DZ*zS+ ziS2vXQU=z$tsgf}5!o`;Hmve{)78UrD;INRO|jO>6tk^TtLVGJvpP&%W^v%-Y0DQZ zFEI1Anvil_b7%cW?>qzZ5Qk}dLnr9n=3RbhKIh4@2Qn`w3wb*8SBaL)-4I(KvrShs z`-O|&VU1%VJabE8&3y9=ChL6sap^bL_l+9L3}?f0-#yd4eL8=G?!5GCW%u^WOx|>H zLsz?0n!)M?_CD&*enuVs;&S(i<1+7+{SKGUZ_~Q^LE^KNn99P7|E1q+PMP}EH%^iN zwts)yyB_T_ruWk_mp(idsAC@Ax?swhzDoz9*hK3l?^}H+cGcCrxiJgtHN5R5czt$x zyw|gGz0hm%_GD3%en1Le;4Q|RkL;#AQdG+R-BIWIX7l{oiD5tEo3xTbF1uUno;a@h z<@XQf9WqI8TOuwW&F^@>=&eBK);YWSpPYXreuQs=(n9$|1-~?8Y#r2^;xBw#Vf3o} zjcVV5IIXWQRQ@==3OEoD{Qc#I-wL*dE>=rtDL(03`DEc`&$Oee|L$#^&>}CB*VNf= z&}bU2Q8slSd(ylh7q06nCyp@QQmVL|bxOj>WzG5JZ=SbR6qz?=+wtz&tN7^igfP8= z#jywVuH10n^x*QwFAa}fgpbEezG9(tvywCDLuU{AhVPiNV@@@U3=G`N3=CFS@<>sB zR%&tyF^Bffo#gF*$U&s_JS&S!jeWr3MQ;wU%#tX5tQ^>*y+}pkyR-E)!`yZrX3qa! z;c9w+82@QJ7FlI@!{xEgyO(n;?|&&?bictXTC+g zxGB5V<;Bbw=lWl$UibNNM|FzuHMTyXkA1CuGFt8NohzR1wf}mp!%9P`-KOzYOH#ep z)!uKM;vk3`Nf{(S!b**QD+)bH8Hcz`iyx&nW%QjW;N z$d+bm%Hb4SFo$vs%T2k`{HgZXRQbmzawkr zY-l(TIG_9W7FJp7j3_>1_C$q?dxh_~D@wA(zT0(0+P%AMr+KhamoSs5bZQb;e(lK} z?h7tn``usS=G$Ji&2mCh<+e@R>LRK&G>Z#-63kQeHn4by>G1vtiKsZasO#(gDQPaO zAqyB>nU=UOkU0`?JY1ylF{4-Rx{SI_YmdpBSUSGVs#v@A;s)Nemx~W)d^^LNer4vO zJOPV8$4yt>j$NSV)+lmi(*EKN?3Q021fFKpRQ~P3QE|dX~JA zFI7~#xcQW};XbabEbmkbmhJnprNM0f!kPLdx~yk)7Fn;%&}BWVwr5P9m7~X=Vd|g8vbv^yu^aH$6wdHQKvvpvAteX;nYhz$o(x}P6 zzyR0g>*(j{<{BKL=j(=U0!S0gViphwj6oL9h9-G@I=I2AK?F$0ey9#esA4!4qzq;i zC;~uyh#y~}YJ^@VfMLK?)yvCvGBPkoGBYsfLv(}5C5^I-3=H{2>H1KcVFozm=a+y^ z?l167%*+ceNh~Ts9HEEqtxQIp)$*(i3@TiZ3)8?RGk}6pnVD$AA&nk%qcZ~k)h=RS zV7SD7i@Gd2Ueq9^2_sb@)J{F$NixjxRhDR)0%~W;RZV_?LbUj(pbQW-$0kt zw8YY!5|{-6MfvGPsl~-UnZ+fkd8tL%-G6bH-y0of28IJ{3=Ad^o519fMlL@5)&&&h zS5!KdmSm*nm1HI-!cQ_m_mB$1$-2k<3=9zp3=E23vmwNi#tuQeHeh#fWuwocg$xV~ zrx+L*3?U|g$t8`e6!98ITzDv2)^KcNV_<0EfoymHn+=Z7SxQ)qcdbayDJ{+{OLfc4 zNp(z4PAx9>P0UM7#}(ic-n8gcGBGf;v7o2Lg_>9`a4$+s$w>_@PA&4t&rS8o&(AI` zz#cR+lg-SRb22dSiZL)~!5j{Dd#Vl|qoEh3f$|Y{bALUOc(#gxf#Eg-1A`KZxglm) z&Gm$(LG0#P=7>BB<6vND6GAu7&K$dWdBr7(IXSqp%nhDNDm6?D43cc-s&&0rB!V0OPU_z znooW@&V=OUwO+lKk%8eo6J%c&!Z3{}tcC?7mSi{<7pLYX<)jv=_~a)i=D1`QmlS0t zl_CyoN6)=mo}*rSkFbS1hOjNzg8}_MUxb-$$ym)qZ;qkd?u+i?Rko09F};+6o{( z2rp^$Da9}lcS{DJv7mc*Kt@99_^G8Rz6UK(Mc0ge3lBsys0u!UsTp<=3hrh%y7AYW zw{AwhSp{Lde+7oyV8#>EWka{97VSm^ghdRs7#1P9AIJR&=*FWT?T;{iS{**)(RwB5 z#s;IEXpb=VTRlEwk%xQG&HRIQzA?f~n>KuAg2$BLryZl4s)BZuFv3*zb|Op#A6km_ z07rC-mZF`9im>Q(7qJ#Wf(Lp!D!K*er%xg**w;&h1$fS)M7Ih3L^^~`B9n-)$(fk* z>Co-0K|53oVdv2)MA(V%h%t1l&<|xnSoL8x5mw{vGDNooed{*D5|*VzTLQ_iNC(@X+k(DF7-0+Ja-wa4?3%>h zUO?Z7iLl`BN)jy~6k_O`;1E{5Swn1Ak2JthOn8CT!-DY=tH*%lUXldnoRsaF1ioU zhZ7O@+`Nck4|;1Bsg*#W8b=@ELRhorGQ4328lJ*-StGieL7HK$M9}CKh!4U`8vmgh YhOdRl$_CP6#$d+qN{E5M`3i^!03mLhYXATM diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3f203e9..68e8816 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 \ No newline at end of file +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..f5feea6 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,69 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# 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"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# 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" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +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=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd3..9d21a21 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @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 +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +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 @@ -56,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 @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt b/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt index b653fe4..e020576 100644 --- a/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt +++ b/src/commonMain/kotlin/app/revanced/library/ApkSigner.kt @@ -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, - ) = 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() } } } diff --git a/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt b/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt index b56c62c..a42cb80 100644 --- a/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt +++ b/src/commonMain/kotlin/app/revanced/library/ApkUtils.kt @@ -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. * diff --git a/src/commonMain/kotlin/app/revanced/library/Commands.kt b/src/commonMain/kotlin/app/revanced/library/Commands.kt deleted file mode 100644 index 3f25439..0000000 --- a/src/commonMain/kotlin/app/revanced/library/Commands.kt +++ /dev/null @@ -1,43 +0,0 @@ -@file:Suppress("DeprecatedCallableAddReplaceWith") - -package app.revanced.library - -import app.revanced.library.installation.command.AdbShellCommandRunner -import se.vidstige.jadb.JadbDevice -import se.vidstige.jadb.ShellProcessBuilder -import java.io.File - -@Deprecated("Do not use this anymore. Instead use AdbCommandRunner.") -internal fun JadbDevice.buildCommand( - command: String, - su: Boolean = true, -): ShellProcessBuilder { - if (su) return shellProcessBuilder("su -c \'$command\'") - - val args = command.split(" ") as ArrayList - val cmd = args.removeFirst() - - return shellProcessBuilder(cmd, *args.toTypedArray()) -} - -@Suppress("DEPRECATION") -@Deprecated("Use AdbShellCommandRunner instead.") -internal fun JadbDevice.run( - command: String, - su: Boolean = true, -) = buildCommand(command, su).start() - -@Deprecated("Use AdbShellCommandRunner instead.") -internal fun JadbDevice.hasSu() = AdbShellCommandRunner(this).hasRootPermission() - -@Deprecated("Use AdbShellCommandRunner instead.") -internal fun JadbDevice.push( - file: File, - targetFilePath: String, -) = AdbShellCommandRunner(this).move(file, targetFilePath) - -@Deprecated("Use AdbShellCommandRunner instead.") -internal fun JadbDevice.createFile( - targetFile: String, - content: String, -) = AdbShellCommandRunner(this).write(content.byteInputStream(), targetFile) diff --git a/src/commonMain/kotlin/app/revanced/library/Options.kt b/src/commonMain/kotlin/app/revanced/library/Options.kt index 3775f3c..32ada70 100644 --- a/src/commonMain/kotlin/app/revanced/library/Options.kt +++ b/src/commonMain/kotlin/app/revanced/library/Options.kt @@ -4,17 +4,15 @@ package app.revanced.library import app.revanced.patcher.patch.OptionException import app.revanced.patcher.patch.Patch -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import java.io.File import java.util.logging.Logger -private val logger = Logger.getLogger("Options") - typealias PatchName = String typealias OptionKey = String typealias OptionValue = Any? typealias PatchesOptions = Map> +private val logger = Logger.getLogger("Options") + /** * Set the options for a set of patches that have a name. * @@ -31,107 +29,3 @@ fun Set>.setOptions(options: PatchesOptions) = filter { it.name != null } } } - -@Suppress("unused") -@Deprecated("Functions have been moved to top level.") -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. - */ - @Deprecated("Functions have been moved to the Serialization class.") - fun serialize( - patches: Set>, - 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: OptionException) { - logger.warning("Using default option value for the \"${patch.name}\" patch: ${e.message}") - option.default - } - - Patch.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 - */ - @Deprecated("Functions have been moved to the Serialization class.") - fun deserialize(json: String): Array = mapper.readValue(json, Array::class.java) - - /** - * Sets the options for a set of patches. - * - * @param json The JSON string containing the options. - */ - @Deprecated("Function has been moved to top level.") - fun Set>.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 } - } - - setOptions(jsonPatches) - } - } - - /** - * Sets the options for a set of patches. - * - * @param file The file containing the JSON string containing the options. - * @see setOptions - */ - @Deprecated("Function has been moved to top level.") - fun Set>.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