Skip to content

Commit 418f594

Browse files
feat(YouTube Music): Add Enable debugging patch (#5939)
1 parent e26c971 commit 418f594

File tree

8 files changed

+257
-173
lines changed

8 files changed

+257
-173
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package app.revanced.extension.youtube.patches;
1+
package app.revanced.extension.shared.patches;
22

33
import java.util.concurrent.ConcurrentHashMap;
44
import java.util.concurrent.ConcurrentMap;

patches/api/patches.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ public final class app/revanced/patches/music/misc/backgroundplayback/Background
401401
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
402402
}
403403

404+
public final class app/revanced/patches/music/misc/debugging/EnableDebuggingPatchKt {
405+
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
406+
}
407+
404408
public final class app/revanced/patches/music/misc/extension/SharedExtensionPatchKt {
405409
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
406410
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package app.revanced.patches.music.misc.debugging
2+
3+
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
4+
import app.revanced.patches.music.misc.settings.PreferenceScreen
5+
import app.revanced.patches.music.misc.settings.settingsPatch
6+
import app.revanced.patches.shared.misc.debugging.enableDebuggingPatch
7+
8+
@Suppress("unused")
9+
val enableDebuggingPatch = enableDebuggingPatch(
10+
block = {
11+
dependsOn(
12+
sharedExtensionPatch,
13+
settingsPatch,
14+
)
15+
16+
compatibleWith(
17+
"com.google.android.apps.youtube.music"(
18+
"7.29.52"
19+
)
20+
)
21+
},
22+
// String feature flag does not appear to be present with YT Music.
23+
hookStringFeatureFlag = false,
24+
preferenceScreen = PreferenceScreen.MISC
25+
)

patches/src/main/kotlin/app/revanced/patches/music/misc/settings/SettingsPatch.kt

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ import app.revanced.patches.all.misc.resources.addResources
88
import app.revanced.patches.all.misc.resources.addResourcesPatch
99
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
1010
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
11-
import app.revanced.patches.shared.misc.settings.preference.*
11+
import app.revanced.patches.shared.misc.settings.preference.BasePreference
12+
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
13+
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
14+
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
1215
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
13-
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
14-
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
1516
import app.revanced.patches.shared.misc.settings.settingsPatch
16-
import app.revanced.util.*
17+
import app.revanced.util.ResourceGroup
18+
import app.revanced.util.copyResources
19+
import app.revanced.util.copyXmlNode
20+
import app.revanced.util.inputStreamFromBundledResource
1721
import com.android.tools.smali.dexlib2.util.MethodUtil
1822

1923
private const val BASE_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
@@ -23,7 +27,6 @@ private const val GOOGLE_API_ACTIVITY_HOOK_CLASS_DESCRIPTOR =
2327

2428
private val preferences = mutableSetOf<BasePreference>()
2529

26-
2730
private val settingsResourcePatch = resourcePatch {
2831
dependsOn(
2932
resourceMappingPatch,
@@ -87,27 +90,6 @@ val settingsPatch = bytecodePatch(
8790
addResources("music", "misc.settings.settingsPatch")
8891
addResources("shared", "misc.debugging.enableDebuggingPatch")
8992

90-
// Should make a separate debugging patch, but for now include it with all installations.
91-
PreferenceScreen.MISC.addPreferences(
92-
PreferenceScreenPreference(
93-
key = "revanced_debug_screen",
94-
sorting = Sorting.UNSORTED,
95-
preferences = setOf(
96-
SwitchPreference("revanced_debug"),
97-
NonInteractivePreference(
98-
"revanced_debug_export_logs_to_clipboard",
99-
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
100-
selectable = true
101-
),
102-
NonInteractivePreference(
103-
"revanced_debug_logs_clear_buffer",
104-
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
105-
selectable = true
106-
)
107-
)
108-
)
109-
)
110-
11193
// Add an "About" preference to the top.
11294
preferences += NonInteractivePreference(
11395
key = "revanced_settings_music_screen_0_about",
@@ -154,19 +136,19 @@ fun newIntent(settingsName: String) = IntentPreference.Intent(
154136

155137
object PreferenceScreen : BasePreferenceScreen() {
156138
val ADS = Screen(
157-
"revanced_settings_music_screen_1_ads",
139+
key = "revanced_settings_music_screen_1_ads",
158140
summaryKey = null
159141
)
160142
val GENERAL = Screen(
161-
"revanced_settings_music_screen_2_general",
143+
key = "revanced_settings_music_screen_2_general",
162144
summaryKey = null
163145
)
164146
val PLAYER = Screen(
165-
"revanced_settings_music_screen_3_player",
147+
key = "revanced_settings_music_screen_3_player",
166148
summaryKey = null
167149
)
168150
val MISC = Screen(
169-
"revanced_settings_music_screen_4_misc",
151+
key = "revanced_settings_music_screen_4_misc",
170152
summaryKey = null
171153
)
172154

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package app.revanced.patches.shared.misc.debugging
2+
3+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
4+
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
5+
import app.revanced.patcher.patch.BytecodePatchBuilder
6+
import app.revanced.patcher.patch.BytecodePatchContext
7+
import app.revanced.patcher.patch.bytecodePatch
8+
import app.revanced.patches.all.misc.resources.addResources
9+
import app.revanced.patches.all.misc.resources.addResourcesPatch
10+
import app.revanced.patches.shared.misc.settings.preference.BasePreference
11+
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
12+
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
13+
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
14+
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
15+
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
16+
import app.revanced.util.findInstructionIndicesReversedOrThrow
17+
import app.revanced.util.indexOfFirstInstructionOrThrow
18+
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
19+
import com.android.tools.smali.dexlib2.Opcode
20+
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
21+
22+
private const val EXTENSION_CLASS_DESCRIPTOR =
23+
"Lapp/revanced/extension/shared/patches/EnableDebuggingPatch;"
24+
25+
/**
26+
* Patch shared with YouTube and YT Music.
27+
*/
28+
internal fun enableDebuggingPatch(
29+
block: BytecodePatchBuilder.() -> Unit = {},
30+
executeBlock: BytecodePatchContext.() -> Unit = {},
31+
hookStringFeatureFlag: Boolean,
32+
preferenceScreen: BasePreferenceScreen.Screen,
33+
additionalDebugPreferences: List<BasePreference> = emptyList()
34+
) = bytecodePatch(
35+
name = "Enable debugging",
36+
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
37+
) {
38+
39+
dependsOn(addResourcesPatch)
40+
41+
block()
42+
43+
execute {
44+
executeBlock()
45+
46+
addResources("shared", "misc.debugging.enableDebuggingPatch")
47+
48+
val preferences = mutableSetOf<BasePreference>(
49+
SwitchPreference("revanced_debug"),
50+
)
51+
52+
preferences.addAll(additionalDebugPreferences)
53+
54+
preferences.addAll(
55+
listOf(
56+
SwitchPreference("revanced_debug_stacktrace"),
57+
SwitchPreference("revanced_debug_toast_on_error"),
58+
NonInteractivePreference(
59+
"revanced_debug_export_logs_to_clipboard",
60+
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
61+
selectable = true
62+
),
63+
NonInteractivePreference(
64+
"revanced_debug_logs_clear_buffer",
65+
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
66+
selectable = true
67+
)
68+
)
69+
)
70+
71+
preferenceScreen.addPreferences(
72+
PreferenceScreenPreference(
73+
key = "revanced_debug_screen",
74+
sorting = Sorting.UNSORTED,
75+
preferences = preferences,
76+
)
77+
)
78+
79+
// Hook the methods that look up if a feature flag is active.
80+
experimentalBooleanFeatureFlagFingerprint.match(
81+
experimentalFeatureFlagParentFingerprint.originalClassDef
82+
).method.apply {
83+
findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
84+
val register = getInstruction<OneRegisterInstruction>(index).registerA
85+
86+
addInstructions(
87+
index,
88+
"""
89+
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
90+
move-result v$register
91+
"""
92+
)
93+
}
94+
}
95+
96+
experimentalDoubleFeatureFlagFingerprint.match(
97+
experimentalFeatureFlagParentFingerprint.originalClassDef
98+
).method.apply {
99+
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
100+
101+
addInstructions(
102+
insertIndex,
103+
"""
104+
move-result-wide v0 # Also clobbers v1 (p0) since result is wide.
105+
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
106+
move-result-wide v0
107+
return-wide v0
108+
"""
109+
)
110+
}
111+
112+
experimentalLongFeatureFlagFingerprint.match(
113+
experimentalFeatureFlagParentFingerprint.originalClassDef
114+
).method.apply {
115+
val insertIndex = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_WIDE)
116+
117+
addInstructions(
118+
insertIndex,
119+
"""
120+
move-result-wide v0
121+
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
122+
move-result-wide v0
123+
return-wide v0
124+
"""
125+
)
126+
}
127+
128+
if (hookStringFeatureFlag) experimentalStringFeatureFlagFingerprint.match(
129+
experimentalFeatureFlagParentFingerprint.originalClassDef
130+
).method.apply {
131+
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
132+
133+
addInstructions(
134+
insertIndex,
135+
"""
136+
move-result-object v0
137+
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
138+
move-result-object v0
139+
return-object v0
140+
"""
141+
)
142+
}
143+
144+
// There exists other experimental accessor methods for byte[]
145+
// and wrappers for obfuscated classes, but currently none of those are hooked.
146+
}
147+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package app.revanced.patches.shared.misc.debugging
2+
3+
import app.revanced.patcher.fingerprint
4+
import com.android.tools.smali.dexlib2.AccessFlags
5+
6+
internal val experimentalFeatureFlagParentFingerprint = fingerprint {
7+
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
8+
returns("L")
9+
parameters("L", "J", "[B")
10+
strings("Unable to parse proto typed experiment flag: ")
11+
}
12+
13+
internal val experimentalBooleanFeatureFlagFingerprint = fingerprint {
14+
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
15+
returns("Z")
16+
parameters("L", "J", "Z")
17+
}
18+
19+
internal val experimentalDoubleFeatureFlagFingerprint = fingerprint {
20+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
21+
returns("D")
22+
parameters("J", "D")
23+
}
24+
25+
internal val experimentalLongFeatureFlagFingerprint = fingerprint {
26+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
27+
returns("J")
28+
parameters("J", "J")
29+
}
30+
31+
internal val experimentalStringFeatureFlagFingerprint = fingerprint {
32+
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
33+
returns("Ljava/lang/String;")
34+
parameters("J", "Ljava/lang/String;")
35+
}

0 commit comments

Comments
 (0)