diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..2384738b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dobby"] + path = native/jni/external/dobby + url = https://github.com/jmpews/Dobby diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 18df6c9f..254fed4d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -42,4 +42,5 @@ dependencies { implementation(libs.androidx.documentfile) implementation(project(":mapper")) + implementation(project(":native")) } \ No newline at end of file diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json index cf8fc79d..575efaee 100644 --- a/core/src/main/assets/lang/en_US.json +++ b/core/src/main/assets/lang/en_US.json @@ -249,6 +249,10 @@ "name": "Disable Metrics", "description": "Disables some analytics data sent to Snapchat" }, + "disable_bitmoji": { + "name": "Disable Bitmoji", + "description": "Disables friends profile bitmoji for the whole app" + }, "block_ads": { "name": "Block Ads", "description": "Prevent ads from being displayed" diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index 14579d82..7aba2762 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -21,6 +21,8 @@ import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.manager.impl.ActionManager import me.rhunk.snapenhance.manager.impl.FeatureManager +import me.rhunk.snapenhance.nativelib.NativeConfig +import me.rhunk.snapenhance.nativelib.NativeLib import me.rhunk.snapenhance.util.download.HttpServer import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -44,6 +46,7 @@ class ModContext { val config by modConfig val event = EventBus(this) val eventDispatcher = EventDispatcher(this) + val native = NativeLib() val translation = LocaleWrapper() val features = FeatureManager(this) @@ -121,6 +124,16 @@ class ModContext { fun reloadConfig() { modConfig.loadFromBridge(bridgeClient) + runCatching { + native.loadConfig( + NativeConfig( + disableBitmoji = config.global.disableBitmoji.get(), + disableMetrics = config.global.disableMetrics.get() + ) + ) + }.onFailure { + Logger.xposedLog("Failed to load native config", it) + } } fun getConfigLocale(): String { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt index bccc6bc8..466d28b0 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -97,6 +97,12 @@ class SnapEnhance { private suspend fun init() { measureTime { with(appContext) { + runCatching { + native.init() + }.onFailure { + Logger.xposedLog("Failed to init native", it) + return + } reloadConfig() withContext(appContext.coroutineDispatcher) { translation.userLocale = getConfigLocale() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt index 09daf353..d4c8c03b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt @@ -8,6 +8,7 @@ class Global : ConfigContainer() { val snapchatPlus = boolean("snapchat_plus") { addNotices(FeatureNotice.MAY_BAN) } val autoUpdater = unique("auto_updater", "EVERY_LAUNCH", "DAILY", "WEEKLY").apply { set("DAILY") } val disableMetrics = boolean("disable_metrics") + val disableBitmoji = boolean("disable_bitmoji") { addNotices(FeatureNotice.UNSTABLE) } val blockAds = boolean("block_ads") val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") { addNotices(FeatureNotice.MAY_BAN) } val disableGooglePlayDialogs = boolean("disable_google_play_dialogs") diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt index bf8c2bfa..111a8bb5 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt @@ -56,7 +56,7 @@ class EventBus( event.context = context - subscribers[event::class]?.forEach { listener -> + subscribers[event::class]?.toTypedArray()?.forEach { listener -> @Suppress("UNCHECKED_CAST") runCatching { (listener as IListener).handle(event) diff --git a/native/.gitignore b/native/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/native/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/native/build.gradle.kts b/native/build.gradle.kts new file mode 100644 index 00000000..dbd9d248 --- /dev/null +++ b/native/build.gradle.kts @@ -0,0 +1,28 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + alias(libs.plugins.androidLibrary) + alias(libs.plugins.kotlinAndroid) +} + +android { + namespace = "me.rhunk.snapenhance.nativelib" + compileSdk = 34 + + defaultConfig { + externalNativeBuild { + cmake { + cppFlags("") + } + } + } + + externalNativeBuild { + cmake { + path("jni/CMakeLists.txt") + version = "3.22.1" + } + } + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/native/jni/CMakeLists.txt b/native/jni/CMakeLists.txt new file mode 100644 index 00000000..5b67f6b5 --- /dev/null +++ b/native/jni/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.22.1) + +project("nativelib") + +set(DOBBY_GENERATE_SHARED OFF) +add_subdirectory(external/dobby) + +add_library(${CMAKE_PROJECT_NAME} SHARED + src/library.cpp + ) + +target_link_libraries(${CMAKE_PROJECT_NAME} + android + log + dobby_static + ) diff --git a/native/jni/external/dobby b/native/jni/external/dobby new file mode 160000 index 00000000..b0176de5 --- /dev/null +++ b/native/jni/external/dobby @@ -0,0 +1 @@ +Subproject commit b0176de574104726bb68dff3b77ee666300fc338 diff --git a/native/jni/src/config.h b/native/jni/src/config.h new file mode 100644 index 00000000..1544d3ae --- /dev/null +++ b/native/jni/src/config.h @@ -0,0 +1,6 @@ +#pragma once + +typedef struct { + bool disable_bitmoji; + bool disable_metrics; +} native_config_t; \ No newline at end of file diff --git a/native/jni/src/library.cpp b/native/jni/src/library.cpp new file mode 100644 index 00000000..90825697 --- /dev/null +++ b/native/jni/src/library.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "config.h" + +static native_config_t *native_config; + +static auto fstat_original = fstat; +static int fstat_hook(int fd, struct stat *buf) { + char name[256]; + memset(name, 0, 256); + snprintf(name, sizeof(name), "/proc/self/fd/%d", fd); + readlink(name, name, sizeof(name)); + + auto fileName = std::string(name); + + //prevent blizzardv2 metrics + if (native_config->disable_metrics && fileName.find("files/blizzardv2/queues") != std::string::npos) { + unlink(name); + return -1; + } + + //prevent bitmoji to load + if (native_config->disable_bitmoji && fileName.find("com.snap.file_manager_4_SCContent") != std::string::npos) { + return -1; + } + + return fstat_original(fd, buf); +} + + +#define GET_BOOL_FIELD(env, clazz, field) env->GetBooleanField(clazz, env->GetFieldID(clazz, field, "Z")) + +extern "C" JNIEXPORT void JNICALL +loadConfig(JNIEnv *env, jobject clazz, jobject config_object) { + auto native_config_class = env->GetObjectClass(config_object); + + native_config->disable_bitmoji = GET_BOOL_FIELD(env, native_config_class, "disableBitmoji"); + native_config->disable_metrics = GET_BOOL_FIELD(env, native_config_class, "disableMetrics"); + + LOGD("config loaded"); +} + +//jni onload +extern "C" JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved) { + LOGD("initializing native"); + // config + native_config = new native_config_t; + + // hooks + DobbyHook((void *) fstat_original,(void *) fstat_hook,(void **) &fstat_original); + + // register native methods + JNIEnv *env = nullptr; + vm->GetEnv((void **) &env, JNI_VERSION_1_6); + + auto methods = std::vector(); + methods.push_back({"loadConfig", "(Lme/rhunk/snapenhance/nativelib/NativeConfig;)V", (void *) loadConfig}); + + env->RegisterNatives( + env->FindClass("me/rhunk/snapenhance/nativelib/NativeLib"), + methods.data(), + methods.size() + ); + + LOGD("native initialized"); + + return JNI_VERSION_1_6; +} diff --git a/native/jni/src/logger.h b/native/jni/src/logger.h new file mode 100644 index 00000000..14595886 --- /dev/null +++ b/native/jni/src/logger.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#define LOG_TAG "SnapEnhanceNative" + +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) + diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt new file mode 100644 index 00000000..bc4c3019 --- /dev/null +++ b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeConfig.kt @@ -0,0 +1,6 @@ +package me.rhunk.snapenhance.nativelib + +data class NativeConfig( + val disableBitmoji: Boolean = false, + val disableMetrics: Boolean = false +) diff --git a/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt new file mode 100644 index 00000000..faa6e218 --- /dev/null +++ b/native/src/main/kotlin/me/rhunk/snapenhance/nativelib/NativeLib.kt @@ -0,0 +1,10 @@ +package me.rhunk.snapenhance.nativelib + +class NativeLib { + fun init() { + System.loadLibrary("nativelib") + } + + + external fun loadConfig(config: NativeConfig) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index c62f1ff7..132f3f5d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,4 +19,5 @@ dependencyResolutionManagement { rootProject.name = "SnapEnhance" include(":core") include(":app") -include(":mapper") \ No newline at end of file +include(":mapper") +include(":native")