Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Disable analytics and telemetry patches #3448

Draft
wants to merge 21 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Some of the features the patches provide are:
* 🚫 **Block ads**: Say goodbye to ads
* ⭐ **Customize your app**: Personalize the appearance of apps with various layouts and themes
* 🪄 **Add new features**: Extend the functionality of apps with lots of new features
* 📡 **Enhance your privacy**: Disable embedded trackers and privacy invasive components
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved
* ⚙️ **Miscellaneous and general purpose**: Rename packages, enable debugging, disable screen capture restrictions,
export activities, etc.
* ✨ **And much more!**
Expand Down
17 changes: 17 additions & 0 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ public final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePa
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}

public final class app/revanced/patches/all/privacy/UniversalPrivacyPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/all/privacy/UniversalPrivacyPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/all/privacy/UniversalResourcePrivacyPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/all/privacy/UniversalResourcePrivacyPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}

public final class app/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
public static final field INSTANCE Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch;
public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
Expand Down Expand Up @@ -1019,6 +1031,11 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}

public final class app/revanced/patches/shared/resource/AndroidManifest {
public static final field INSTANCE Lapp/revanced/patches/shared/resource/AndroidManifest;
public final fun addMetadata (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;)V
}

public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package app.revanced.patches.all.privacy

import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
import app.revanced.patches.all.privacy.fingerprints.*
import app.revanced.util.resultOrThrow
import java.util.logging.Logger

@Patch(
name = "Disable privacy invasive components",
description = "Disables multiple embedded analytics and telemetry SDKs",
)
@Suppress("unused")
object UniversalPrivacyPatch : BytecodePatch(
setOf(
StatsigClientFingerprint,
AnalyticsInitFingerprint,
AppsFlyerInitFingerprint,
ComScoreSetupFingerprint,
SettingsSpiCallFingerprint,
DoConfigFetchFingerprint,
SendFingerprint,
InitialiseSdkFingerprint,
SegmentBuilderFingerprint,
InitSDKFingerprint
)
) {

private val subPatchesOptions = mapOf(
::disableGoogleAnalytics to booleanPatchOption(
key = "disableGoogleAnalytics",
default = true,
values = mapOf(),
title = "Google Analytics",
description = "",
required = true
),
::disableStatsig to booleanPatchOption(
key = "disableStatsig",
default = true,
values = mapOf(),
title = "Statsig",
description = "",
required = true
),
::disableAppsFlyer to booleanPatchOption(
key = "disableAppsFlyer",
default = true,
values = mapOf(),
title = "Apps Flyer",
description = "",
required = true
),
::disableAppsFlyerPlugin to booleanPatchOption(
key = "disableAppsFlyerPlugin",
default = true,
values = mapOf(),
title = "Apps Flyer plugin",
description = "",
required = true
),
::disableComScore to booleanPatchOption(
key = "disableComScore",
default = true,
values = mapOf(),
title = "ComScore",
description = "",
required = true
),
::disableCrashlytics to booleanPatchOption(
key = "disableCrashlytics",
default = true,
values = mapOf(),
title = "Crashlytics",
description = "",
required = true
),
::disableFirebaseTransport to booleanPatchOption(
key = "disableFirebaseTransport",
default = true,
values = mapOf(),
title = "Firebase Transport",
description = "",
required = true
),
::disableMoEngage to booleanPatchOption(
key = "disableMoEngage",
default = true,
values = mapOf(),
title = "MoEngage",
description = "",
required = true
),
::disableSegment to booleanPatchOption(
key = "disableSegment",
default = true,
values = mapOf(),
title = "Segment",
description = "",
required = true
),
)

private fun disableStatsig(context: BytecodeContext) {
StatsigClientFingerprint.resultOrThrow().mutableMethod.addInstructions(0,"return-void")
}

private fun disableGoogleAnalytics(context: BytecodeContext) {
// Empties the "context" argument to force an exception
AnalyticsInitFingerprint.resultOrThrow().mutableMethod.addInstructions(0,"const/4 p0, 0x0")
}

private fun disableAppsFlyer(context: BytecodeContext) {
AppsFlyerInitFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
sget-object p0, Lkotlin/Unit;->INSTANCE:Lkotlin/Unit;
return-object p0
"""
)
}

// This plugin is used to interact with a non-Java app technology, like Flutter or React Native
private fun disableAppsFlyerPlugin(context: BytecodeContext) {
InitSDKFingerprint.resultOrThrow().mutableMethod.addInstructions(0,"return-void")
}

private fun disableComScore(context: BytecodeContext) {
ComScoreSetupFingerprint.resultOrThrow().mutableMethod.addInstructions(0, "return-void")
}

private fun disableCrashlytics(context: BytecodeContext) {
// Neutralize the two methods responsible for requesting Crashlytics' configuration
// which effectively disables the SDK

SettingsSpiCallFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
const/4 p1, 0x0
return-object p1
"""
)

DoConfigFetchFingerprint.resultOrThrow().mutableMethod.addInstructions(
0,
"""
sget-object p1, Lkotlin/Unit;->INSTANCE:Lkotlin/Unit;
return-object p1
"""
)
}

// Prevents the sending of Firebase Logging and Firebase Crashlytics logs to Google's servers.
private fun disableFirebaseTransport(context: BytecodeContext) {
// Neutralize the method sending data to the backend
SendFingerprint.resultOrThrow().mutableMethod.addInstructions(0,"return-void")
}

private fun disableMoEngage(context: BytecodeContext) {
InitialiseSdkFingerprint.resultOrThrow().mutableMethod.addInstructions(0, "return-void")
}

private fun disableSegment(context: BytecodeContext) {
// Empties the writeKey parameter to abort initialization
SegmentBuilderFingerprint.resultOrThrow().mutableMethod.addInstructions(0,"const-string p2, \"\"")
}


override fun execute(context: BytecodeContext) {
subPatchesOptions.forEach {
if (it.value.value == true){
try {
it.key(context)
Logger.getLogger(this::class.java.name).info("Applied privacy patch to disable ${it.value.title}")
}catch (exception: PatchException){
Logger.getLogger(this::class.java.name).info("${it.value.title} not found, skipping")
}
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package app.revanced.patches.all.privacy

import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
import app.revanced.patches.shared.resource.AndroidManifest
import java.util.logging.Logger

@Patch(
name = "Disable more privacy invasive components",
description = "Disables multiple embedded analytics and telemetry SDKs by modifying the app's manifest. Please note this can break some apps.",
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
use = false
)
@Suppress("unused")
object UniversalResourcePrivacyPatch : ResourcePatch() {

private val subPatchesOptions = mapOf(
::disableFirebaseCollections to booleanPatchOption(
key = "disableFirebaseCollections",
default = true,
values = mapOf(),
title = "Firebase collections",
description = "Disables multiple Firebase data collection mechanisms.",
required = true
),
::disableFacebookAnalytics to booleanPatchOption(
key = "disableFacebookAnalytics",
default = true,
values = mapOf(),
title = "Facebook Analytics",
description = "Disables parts of the Facebook SDK responsible for data gathering.",
required = true
),
::disableFacebookSDK to booleanPatchOption(
key = "disableFacebookSDK",
default = false,
values = mapOf(),
title = "Facebook SDK",
description = "Disables the Facebook SDK. Will break Facebook login.",
required = true
),
::disableGoogleAnalyticsCollections to booleanPatchOption(
key = "Google Analytics collections",
default = true,
values = mapOf(),
title = "Apps Flyer",
description = "Disables multiple Google Analytics data collection mechanisms.",
required = true
),
)

private fun disableFacebookAnalytics(context: ResourceContext) {
mapOf(
"com.facebook.sdk.AutoLogAppEventsEnabled" to "false",
"com.facebook.sdk.AdvertiserIDCollectionEnabled" to "false",
"com.facebook.sdk.MonitorEnabled" to "false"
).forEach {
AndroidManifest.addMetadata(context, it.key, it.value)
}
}

private fun disableFacebookSDK(context: ResourceContext) {
AndroidManifest.addMetadata(context, "com.facebook.sdk.AutoInitEnabled", "false")
}

private fun disableFirebaseCollections(context: ResourceContext) {
mapOf(
"firebase_analytics_collection_enabled" to "false",
"firebase_analytics_collection_deactivated" to "true",
"firebase_crashlytics_collection_enabled" to "false",
"firebase_performance_collection_enabled" to "false",
"firebase_performance_collection_deactivated" to "true",
"firebase_data_collection_default_enabled" to "false"
).forEach {
AndroidManifest.addMetadata(context, it.key, it.value)
}
}

private fun disableGoogleAnalyticsCollections(context: ResourceContext) {
mapOf(
"google_analytics_adid_collection_enabled" to "false",
"google_analytics_default_allow_ad_personalization_signals" to "false",
"google_analytics_automatic_screen_reporting_enabled" to "false",
"google_analytics_default_allow_ad_storage" to "false",
"google_analytics_default_allow_ad_user_data" to "false",
"google_analytics_default_allow_analytics_storage" to "false",
"google_analytics_sgtm_upload_enabled" to "false",
"google_analytics_deferred_deep_link_enabled" to "false"
).forEach {
AndroidManifest.addMetadata(context, it.key, it.value)
}
}

override fun execute(context: ResourceContext) {
subPatchesOptions.forEach {
if (it.value.value == true){
try {
it.key(context)
Logger.getLogger(this::class.java.name).info("Applied privacy patch to disable ${it.value.title}")
}catch (exception: PatchException){
Logger.getLogger(this::class.java.name).info("${it.value.title} not found, skipping")
}
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package app.revanced.patches.all.privacy.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags

internal object AnalyticsInitFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("Landroid/content/Context;"),
strings = listOf("Slow initialization (ms)"),
customFingerprint = { _, classDef ->
classDef.sourceFile?.startsWith("com.google.android.gms:play-services-analytics-impl") == true
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package app.revanced.patches.all.privacy.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags

internal object AppsFlyerInitFingerprint : MethodFingerprint(
returnType = "Ljava/lang/Object;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC or AccessFlags.SYNTHETIC,
parameters = listOf("L", "L", "L"),
customFingerprint = { _, classDef ->
classDef.sourceFile == "CatchingAppsFlyerLibWrapper.kt"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.revanced.patches.all.privacy.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags

internal object ComScoreSetupFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
customFingerprint = { methodDef, classDef ->
classDef.type == "Lcom/comscore/util/setup/Setup;" && methodDef.name == "setUp"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.revanced.patches.all.privacy.fingerprints

import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags

internal object DoConfigFetchFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC.value,
customFingerprint = { methodDef, classDef ->
classDef.sourceFile == "RemoteSettingsFetcher.kt" && methodDef.name == "doConfigFetch"
}
)
Loading
Loading