feat(YouTube Music): Add Enable debugging patch (#5939)

This commit is contained in:
LisoUseInAIKyrios
2025-09-20 16:33:03 +04:00
committed by GitHub
parent e26c971067
commit 418f5945c2
8 changed files with 257 additions and 173 deletions

View File

@@ -1,4 +1,4 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.shared.patches;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;

View File

@@ -401,6 +401,10 @@ public final class app/revanced/patches/music/misc/backgroundplayback/Background
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/music/misc/debugging/EnableDebuggingPatchKt {
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt { public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }

View File

@@ -0,0 +1,25 @@
package app.revanced.patches.music.misc.debugging
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.music.misc.settings.settingsPatch
import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
@Suppress("unused")
val enableDebuggingPatch = enableDebuggingPatch(
block = {
dependsOn(
sharedExtensionPatch,
settingsPatch,
)
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52"
)
)
},
// String feature flag does not appear to be present with YT Music.
hookStringFeatureFlag = false,
preferenceScreen = PreferenceScreen.MISC
)

View File

@@ -8,12 +8,16 @@ import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.* import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.settingsPatch import app.revanced.patches.shared.misc.settings.settingsPatch
import app.revanced.util.* import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode
import app.revanced.util.inputStreamFromBundledResource
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
private const val BASE_ACTIVITY_HOOK_CLASS_DESCRIPTOR = private const val BASE_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
@@ -23,7 +27,6 @@ private const val GOOGLE_API_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
private val preferences = mutableSetOf<BasePreference>() private val preferences = mutableSetOf<BasePreference>()
private val settingsResourcePatch = resourcePatch { private val settingsResourcePatch = resourcePatch {
dependsOn( dependsOn(
resourceMappingPatch, resourceMappingPatch,
@@ -87,27 +90,6 @@ val settingsPatch = bytecodePatch(
addResources("music", "misc.settings.settingsPatch") addResources("music", "misc.settings.settingsPatch")
addResources("shared", "misc.debugging.enableDebuggingPatch") addResources("shared", "misc.debugging.enableDebuggingPatch")
// Should make a separate debugging patch, but for now include it with all installations.
PreferenceScreen.MISC.addPreferences(
PreferenceScreenPreference(
key = "revanced_debug_screen",
sorting = Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_debug"),
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
)
)
)
)
// Add an "About" preference to the top. // Add an "About" preference to the top.
preferences += NonInteractivePreference( preferences += NonInteractivePreference(
key = "revanced_settings_music_screen_0_about", key = "revanced_settings_music_screen_0_about",
@@ -154,19 +136,19 @@ fun newIntent(settingsName: String) = IntentPreference.Intent(
object PreferenceScreen : BasePreferenceScreen() { object PreferenceScreen : BasePreferenceScreen() {
val ADS = Screen( val ADS = Screen(
"revanced_settings_music_screen_1_ads", key = "revanced_settings_music_screen_1_ads",
summaryKey = null summaryKey = null
) )
val GENERAL = Screen( val GENERAL = Screen(
"revanced_settings_music_screen_2_general", key = "revanced_settings_music_screen_2_general",
summaryKey = null summaryKey = null
) )
val PLAYER = Screen( val PLAYER = Screen(
"revanced_settings_music_screen_3_player", key = "revanced_settings_music_screen_3_player",
summaryKey = null summaryKey = null
) )
val MISC = Screen( val MISC = Screen(
"revanced_settings_music_screen_4_misc", key = "revanced_settings_music_screen_4_misc",
summaryKey = null summaryKey = null
) )

View File

@@ -0,0 +1,147 @@
package app.revanced.patches.shared.misc.debugging
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/shared/patches/EnableDebuggingPatch;"
/**
* Patch shared with YouTube and YT Music.
*/
internal fun enableDebuggingPatch(
block: BytecodePatchBuilder.() -> Unit = {},
executeBlock: BytecodePatchContext.() -> Unit = {},
hookStringFeatureFlag: Boolean,
preferenceScreen: BasePreferenceScreen.Screen,
additionalDebugPreferences: List<BasePreference> = emptyList()
) = bytecodePatch(
name = "Enable debugging",
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
) {
dependsOn(addResourcesPatch)
block()
execute {
executeBlock()
addResources("shared", "misc.debugging.enableDebuggingPatch")
val preferences = mutableSetOf<BasePreference>(
SwitchPreference("revanced_debug"),
)
preferences.addAll(additionalDebugPreferences)
preferences.addAll(
listOf(
SwitchPreference("revanced_debug_stacktrace"),
SwitchPreference("revanced_debug_toast_on_error"),
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
)
)
)
preferenceScreen.addPreferences(
PreferenceScreenPreference(
key = "revanced_debug_screen",
sorting = Sorting.UNSORTED,
preferences = preferences,
)
)
// Hook the methods that look up if a feature flag is active.
experimentalBooleanFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
move-result v$register
"""
)
}
}
experimentalDoubleFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
move-result-wide v0
return-wide v0
"""
)
}
experimentalLongFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
move-result-wide v0
return-wide v0
"""
)
}
if (hookStringFeatureFlag) experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
}
// There exists other experimental accessor methods for byte[]
// and wrappers for obfuscated classes, but currently none of those are hooked.
}
}

View File

@@ -0,0 +1,35 @@
package app.revanced.patches.shared.misc.debugging
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val experimentalFeatureFlagParentFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("L")
parameters("L", "J", "[B")
strings("Unable to parse proto typed experiment flag: ")
}
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Z")
parameters("L", "J", "Z")
}
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("D")
parameters("J", "D")
}
internal val experimentalLongFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("J")
parameters("J", "J")
}
internal val experimentalStringFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Ljava/lang/String;")
parameters("J", "Ljava/lang/String;")
}

View File

@@ -1,144 +1,35 @@
package app.revanced.patches.youtube.misc.debugging package app.revanced.patches.youtube.misc.debugging
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = @Suppress("unused")
"Lapp/revanced/extension/youtube/patches/EnableDebuggingPatch;" val enableDebuggingPatch = enableDebuggingPatch(
block = {
// TODO: Refactor this into a shared patch that can be used by both YT and YT Music. dependsOn(
// Almost all of the feature flag hooks are the same between both apps. sharedExtensionPatch,
val enableDebuggingPatch = bytecodePatch( settingsPatch,
name = "Enable debugging",
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
versionCheckPatch
)
compatibleWith(
"com.google.android.youtube"(
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
"20.13.41",
) )
)
execute { compatibleWith(
addResources("shared", "misc.debugging.enableDebuggingPatch") "com.google.android.youtube"(
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
"20.13.41",
)
)
},
executeBlock = {
addResources("youtube", "misc.debugging.enableDebuggingPatch") addResources("youtube", "misc.debugging.enableDebuggingPatch")
},
PreferenceScreen.MISC.addPreferences( hookStringFeatureFlag = true,
PreferenceScreenPreference( preferenceScreen = PreferenceScreen.MISC,
key = "revanced_debug_screen", additionalDebugPreferences = listOf(SwitchPreference("revanced_debug_protobuffer"))
sorting = Sorting.UNSORTED, )
preferences = setOf(
SwitchPreference("revanced_debug"),
SwitchPreference("revanced_debug_protobuffer"),
SwitchPreference("revanced_debug_stacktrace"),
SwitchPreference("revanced_debug_toast_on_error"),
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
),
),
),
)
// Hook the methods that look up if a feature flag is active.
experimentalBooleanFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
move-result v$register
"""
)
}
}
experimentalDoubleFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
move-result-wide v0
return-wide v0
"""
)
}
experimentalLongFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
addInstructions(
insertIndex,
"""
move-result-wide v0
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
move-result-wide v0
return-wide v0
"""
)
}
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
}
// There exists other experimental accessor methods for byte[]
// and wrappers for obfuscated classes, but currently none of those are hooked.
}
}

View File

@@ -163,6 +163,15 @@ Playback may not work"</string>
<string name="revanced_debug_title">Debug logging</string> <string name="revanced_debug_title">Debug logging</string>
<string name="revanced_debug_summary_on">Debug logs are enabled</string> <string name="revanced_debug_summary_on">Debug logs are enabled</string>
<string name="revanced_debug_summary_off">Debug logs are disabled</string> <string name="revanced_debug_summary_off">Debug logs are disabled</string>
<string name="revanced_debug_stacktrace_title">Log stack traces</string>
<string name="revanced_debug_stacktrace_summary_on">Debug logs include stack trace</string>
<string name="revanced_debug_stacktrace_summary_off">Debug logs do not include stack trace</string>
<string name="revanced_debug_toast_on_error_title">Show toast on ReVanced error</string>
<string name="revanced_debug_toast_on_error_summary_on">Toast is shown if error occurs</string>
<string name="revanced_debug_toast_on_error_summary_off">Toast is not shown if error occurs</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"Turning off error toasts hides all ReVanced error notifications.
You will not be notified of any unexpected events."</string>
<string name="revanced_debug_export_logs_to_clipboard_title">Export debug logs</string> <string name="revanced_debug_export_logs_to_clipboard_title">Export debug logs</string>
<string name="revanced_debug_export_logs_to_clipboard_summary">Copies ReVanced debug logs to the clipboard</string> <string name="revanced_debug_export_logs_to_clipboard_summary">Copies ReVanced debug logs to the clipboard</string>
<string name="revanced_debug_logs_disabled">Debug logging is disabled</string> <string name="revanced_debug_logs_disabled">Debug logging is disabled</string>
@@ -209,15 +218,6 @@ Playback may not work"</string>
This can help identify components when creating custom filters. This can help identify components when creating custom filters.
However, enabling this will also log some user data such as your IP address."</string> However, enabling this will also log some user data such as your IP address."</string>
<string name="revanced_debug_stacktrace_title">Log stack traces</string>
<string name="revanced_debug_stacktrace_summary_on">Debug logs include stack trace</string>
<string name="revanced_debug_stacktrace_summary_off">Debug logs do not include stack trace</string>
<string name="revanced_debug_toast_on_error_title">Show toast on ReVanced error</string>
<string name="revanced_debug_toast_on_error_summary_on">Toast is shown if error occurs</string>
<string name="revanced_debug_toast_on_error_summary_off">Toast is not shown if error occurs</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"Turning off error toasts hides all ReVanced error notifications.
You will not be notified of any unexpected events."</string>
</patch> </patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch"> <patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_hide_album_cards_title">Hide album cards</string> <string name="revanced_hide_album_cards_title">Hide album cards</string>