From b57dbaf1b1950e334f50f0f32d9e4f88437b3ebc Mon Sep 17 00:00:00 2001 From: rospino74 <34315725+rospino74@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:30:56 +0100 Subject: [PATCH] use shared litho patch Co-authored-by: oSumAtrIX Co-authored-by: semantic-release-bot --- .../shared/patches/litho/CustomFilter.java | 4 +- .../patches/litho/LithoFilterPatch.java | 30 +-- .../misc/litho/filter/LithoFilterPatch.kt | 9 + .../shared/misc/litho/filter/Fingerprints.kt | 45 +++-- .../misc/litho/filter/LithoFilterPatch.kt | 53 ++--- .../youtube/ad/general/HideAdsPatch.kt | 2 +- .../layout/buttons/action/HideButtonsPatch.kt | 2 +- .../hide/general/HideLayoutComponentsPatch.kt | 2 +- .../hide/infocards/HideInfoCardsPatch.kt | 2 +- .../HidePlayerFlyoutMenuPatch.kt | 2 +- .../hide/shorts/HideShortsComponentsPatch.kt | 2 +- .../ReturnYouTubeDislikePatch.kt | 2 +- .../youtube/misc/litho/filter/Fingerprints.kt | 60 ------ .../misc/litho/filter/LithoFilterPatch.kt | 191 +++--------------- .../quality/AdvancedVideoQualityMenuPatch.kt | 2 +- .../speed/custom/CustomPlaybackSpeedPatch.kt | 2 +- 16 files changed, 114 insertions(+), 296 deletions(-) rename extensions/{youtube/src/main/java/app/revanced/extension/youtube => shared/library/src/main/java/app/revanced/extension/shared}/patches/litho/LithoFilterPatch.java (95%) diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java index f3d142e882..c82c28353a 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/CustomFilter.java @@ -15,8 +15,8 @@ import java.util.regex.Pattern; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.ByteTrieSearch; -import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; +import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; /** * Allows custom filtering using a path and optionally a proto buffer string. @@ -147,7 +147,7 @@ public final class CustomFilter extends Filter { @Override public boolean isFiltered(String identifier, String path, byte[] buffer, - StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { + StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { // All callbacks are custom filter groups. CustomFilterGroup custom = (CustomFilterGroup) matchedGroup; if (custom.startsWith && contentIndex != 0) { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LithoFilterPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java similarity index 95% rename from extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LithoFilterPatch.java rename to extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java index 73169e3123..ddcfe41cc0 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/litho/LithoFilterPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/litho/LithoFilterPatch.java @@ -1,4 +1,4 @@ -package app.revanced.extension.youtube.patches.litho; +package app.revanced.extension.shared.patches.litho; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -11,12 +11,10 @@ import java.util.Map; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; -import app.revanced.extension.shared.patches.litho.Filter; import app.revanced.extension.shared.patches.litho.FilterGroup.StringFilterGroup; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.StringTrieSearch; -import app.revanced.extension.youtube.patches.VersionCheckPatch; -import app.revanced.extension.youtube.settings.Settings; +import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; @SuppressWarnings("unused") public final class LithoFilterPatch { @@ -39,11 +37,11 @@ public final class LithoFilterPatch { public String toString() { // Estimate the percentage of the buffer that are Strings. StringBuilder builder = new StringBuilder(Math.max(100, buffer.length / 2)); - builder.append( "ID: "); + builder.append("ID: "); builder.append(identifier); builder.append(" Path: "); builder.append(path); - if (Settings.DEBUG_PROTOBUFFER.get()) { + if (YouTubeAndMusicSettings.DEBUG_PROTOBUFFER.get()) { builder.append(" BufferStrings: "); findAsciiStrings(builder, buffer); } @@ -83,9 +81,10 @@ public final class LithoFilterPatch { /** * Placeholder for actual filters. */ - private static final class DummyFilter extends Filter { } + private static final class DummyFilter extends Filter { + } - private static final Filter[] filters = new Filter[] { + private static final Filter[] filters = new Filter[]{ new DummyFilter() // Replaced during patching, do not touch. }; @@ -106,12 +105,15 @@ public final class LithoFilterPatch { private static final int LITHO_LAYOUT_THREAD_POOL_SIZE = 1; /** - * 20.22+ cannot use the thread buffer, because frequently the buffer is not correct, - * especially for components that are recreated such as dragging off screen then back on screen. + * For YouTube 20.22+, this is set to true by a patch, + * because it cannot use the thread buffer due to the buffer frequently not being correct, + * especially for components that are recreated such as dragging off-screen then back on screen. * Instead, parse the identifier found near the start of the buffer and use that to * identify the correct buffer to use when filtering. + *

+ * This is set during patching, do not change manually. */ - private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = VersionCheckPatch.IS_20_22_OR_GREATER; + private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = false; /** * Turns on additional logging, used for development purposes only. @@ -122,9 +124,11 @@ public final class LithoFilterPatch { * String suffix for components. * Can be any of: ".eml", ".e-b", ".eml-js", "e-js-b" */ - private static final String LITHO_COMPONENT_EXTENSION = ".e"; - private static final byte[] LITHO_COMPONENT_EXTENSION_BYTES = LITHO_COMPONENT_EXTENSION.getBytes(StandardCharsets.US_ASCII); + private static final byte[] LITHO_COMPONENT_EXTENSION_BYTES = ".e".getBytes(StandardCharsets.US_ASCII); + /** + * Used as placeholder for litho id/path filters that do not use a buffer + */ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /** diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt index 0ed3e3e77e..ebb0edc8a6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/litho/filter/LithoFilterPatch.kt @@ -1,9 +1,12 @@ package app.revanced.patches.music.misc.litho.filter +import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.shared.conversionContextToStringMethod +import app.revanced.patches.shared.misc.litho.filter.EXTENSION_CLASS_DESCRIPTOR import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceLegacyMethod import app.revanced.util.indexOfFirstInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode @@ -13,6 +16,12 @@ val lithoFilterPatch = lithoFilterPatch( indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) }, getConversionContextToStringMethod = BytecodePatchContext::conversionContextToStringMethod::get, + insertProtobufHook = { + protobufBufferReferenceLegacyMethod.addInstruction( + 0, + "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", + ) + }, ) { dependsOn(sharedExtensionPatch) } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt index 3131edc413..59c52e34f5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/Fingerprints.kt @@ -1,21 +1,7 @@ package app.revanced.patches.shared.misc.litho.filter -import app.revanced.patcher.accessFlags -import app.revanced.patcher.composingFirstMethod -import app.revanced.patcher.custom -import app.revanced.patcher.definingClass -import app.revanced.patcher.gettingFirstMethod -import app.revanced.patcher.gettingFirstMethodDeclaratively -import app.revanced.patcher.gettingFirstMutableMethod -import app.revanced.patcher.gettingFirstMutableMethodDeclaratively -import app.revanced.patcher.immutableClassDef -import app.revanced.patcher.instructions -import app.revanced.patcher.invoke -import app.revanced.patcher.opcodes -import app.revanced.patcher.parameterTypes +import app.revanced.patcher.* import app.revanced.patcher.patch.BytecodePatchContext -import app.revanced.patcher.returnType -import app.revanced.patcher.strings import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -24,10 +10,30 @@ internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMutableMethod accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) } +internal val BytecodePatchContext.protobufBufferReferenceMethodMatch by composingFirstMethod { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returnType("V") + parameterTypes("[B") + + var methodDefiningClass = "" + custom { + methodDefiningClass = definingClass + true + } + + instructions( + allOf( + Opcode.IGET_OBJECT(), + field { definingClass == methodDefiningClass && type == "Lcom/google/android/libraries/elements/adl/UpbMessage;" }, + ), + method { definingClass == "Lcom/google/android/libraries/elements/adl/UpbMessage;" && name == "jniDecode" }, + ) +} + /** * Matches a method that use the protobuf of our component. */ -internal val BytecodePatchContext.protobufBufferReferenceMethod by gettingFirstMutableMethodDeclaratively { +internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returnType("V") parameterTypes("I", "Ljava/nio/ByteBuffer;") @@ -38,12 +44,11 @@ internal val BytecodePatchContext.componentContextParserMethodMatch by composing instructions("Number of bits must be positive"()) } -internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively("EmptyComponent") { +internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively { accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) parameterTypes() - custom { - immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 - } + instructions("EmptyComponent"()) + custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 } } internal val BytecodePatchContext.componentCreateMethod by gettingFirstMutableMethod( diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt index d759ab4c01..3d97331776 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/litho/filter/LithoFilterPatch.kt @@ -2,9 +2,9 @@ package app.revanced.patches.shared.misc.litho.filter -import app.revanced.patcher.extensions.addInstruction +import app.revanced.com.android.tools.smali.dexlib2.iface.value.MutableEncodedValue.Companion.toMutable +import app.revanced.patcher.classDef import app.revanced.patcher.extensions.addInstructions -import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.extensions.removeInstructions import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.firstClassDef @@ -13,15 +13,12 @@ import app.revanced.patcher.patch.BytecodePatchBuilder import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.shared.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.shared.conversionContextToStringMethod import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.findFreeRegister -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.findFieldFromToString import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.iface.Method -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue /** * Used to add a hook point to the extension stub. @@ -34,20 +31,24 @@ lateinit var addLithoFilter: (String) -> Unit */ private var filterCount = 0 -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;" +internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/litho/LithoFilterPatch;" /** * A patch that allows to filter Litho components based on their identifier or path. * * @param componentCreateInsertionIndex The index to insert the filtering code in the component create method. - * @param getConversionContextToStringMethod The Method of the conversion context to string method. + * @param insertProtobufHook This method injects a setProtoBuffer call in the protobuf decoding logic. + * @param getConversionContextToStringMethod The getter of the conversion context toString method. + * @param getExtractIdentifierFromBuffer Whether to extract the identifier from the protobuf buffer. * @param executeBlock The additional execution block of the patch. * @param block The additional block to build the patch. */ internal fun lithoFilterPatch( componentCreateInsertionIndex: Method.() -> Int, - getConversionContextToStringMethod: BytecodePatchContext.() -> Method, + insertProtobufHook: BytecodePatchContext.() -> Unit, executeBlock: BytecodePatchContext.() -> Unit = {}, + getConversionContextToStringMethod: BytecodePatchContext.() -> Method, + getExtractIdentifierFromBuffer: () -> Boolean = { false }, block: BytecodePatchBuilder.() -> Unit = {}, ) = bytecodePatch( description = "Hooks the method which parses the bytes into a ComponentContext to filter components.", @@ -111,11 +112,14 @@ internal fun lithoFilterPatch( } } + // Tell the extension whether to extract the identifier from the buffer. + if (getExtractIdentifierFromBuffer()) { + lithoFilterMethod.classDef.fields.first { it.name == "EXTRACT_IDENTIFIER_FROM_BUFFER" } + .initialValue = ImmutableBooleanEncodedValue.forBoolean(true).toMutable() + } + // Add an interceptor to steal the protobuf of our component. - protobufBufferReferenceMethod.addInstruction( - 0, - "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", - ) + insertProtobufHook() // Hook the method that parses bytes into a ComponentContext. // Allow the method to run to completion, and override the @@ -123,18 +127,11 @@ internal fun lithoFilterPatch( // It is important to allow the original code to always run to completion, // otherwise high memory usage and poor app performance can occur. - // Find the identifier/path fields of the conversion context. - val conversionContextIdentifierField = componentContextParserMethodMatch.let { - // Identifier field is loaded just before the string declaration. - val index = it.method.indexOfFirstInstructionReversedOrThrow(it[0]) { - // Our instruction reads a String from a field of the ConversionContext class. - val reference = getReference() - reference?.definingClass == conversionContextToStringMethod.immutableClassDef.type && - reference.type == "Ljava/lang/String;" - } + val conversionContextToStringMethod = getConversionContextToStringMethod() - it.method.getInstruction(index).getReference()!! - } + // Find the identifier/path fields of the conversion context. + val conversionContextIdentifierField = conversionContextToStringMethod + .findFieldFromToString("identifierProperty=") val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef .fields.single { field -> field.type == "Ljava/lang/StringBuilder;" } @@ -162,7 +159,11 @@ internal fun lithoFilterPatch( insertIndex, """ move-object/from16 v$freeRegister, p2 # ConversionContext parameter - check-cast v$freeRegister, ${getConversionContextToStringMethod().immutableClassDef.type} # Check we got the actual ConversionContext + + # In YT 20.41 the field is the abstract superclass. + # Check it's the actual ConversionContext just in case. + instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type} + if-eqz v$identifierRegister, :unfiltered # Get identifier and path from ConversionContext iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt index a61663dbb5..bc8f231755 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/ad/general/HideAdsPatch.kt @@ -13,9 +13,9 @@ import app.revanced.patches.shared.misc.fix.verticalscroll.verticalScrollPatch import app.revanced.patches.shared.misc.mapping.ResourceType import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.ad.getpremium.hideGetPremiumPatch import app.revanced.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt index e493fefdf2..c614b2ebb9 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt @@ -6,7 +6,7 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.playservice.is_20_22_or_greater import app.revanced.patches.youtube.misc.playservice.versionCheckPatch diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index f446e45912..ea9e4dd25b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -9,7 +9,7 @@ import app.revanced.patches.shared.layout.hide.general.hideLayoutComponentsPatch import app.revanced.patches.shared.misc.mapping.ResourceType import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.* -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch import app.revanced.patches.youtube.misc.playservice.is_20_26_or_greater diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt index 91d388f7fc..d42b037990 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/infocards/HideInfoCardsPatch.kt @@ -12,8 +12,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.mapping.ResourceType import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt index ae7f24c87f..9fae036084 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt @@ -5,7 +5,7 @@ import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt index 3328f6306e..fb1a8e82a6 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt @@ -16,8 +16,8 @@ import app.revanced.patches.shared.misc.mapping.ResourceType import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch import app.revanced.patches.youtube.misc.playservice.* diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt index 7fdd49c3b1..bb81a31c0c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/returnyoutubedislike/ReturnYouTubeDislikePatch.kt @@ -14,8 +14,8 @@ import app.revanced.patches.shared.misc.settings.preference.NonInteractivePrefer import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch import app.revanced.patches.youtube.misc.playservice.* diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt index 756d6eb6d9..a0cda67c8d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/Fingerprints.kt @@ -5,66 +5,6 @@ import app.revanced.patcher.patch.BytecodePatchContext import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal val BytecodePatchContext.componentCreateMethod by gettingFirstMutableMethodDeclaratively { - instructions( - "Element missing correct type extension"(), - "Element missing type"(), - ) -} - -internal val BytecodePatchContext.lithoFilterMethod by gettingFirstMutableMethodDeclaratively { - definingClass { endsWith("/LithoFilterPatch;") } - accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) -} - -internal val BytecodePatchContext.protobufBufferReferenceMethodMatch by composingFirstMethod { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returnType("V") - parameterTypes("[B") - - var methodDefiningClass = "" - custom { - methodDefiningClass = definingClass - true - } - - instructions( - allOf( - Opcode.IGET_OBJECT(), - field { definingClass == methodDefiningClass && type == "Lcom/google/android/libraries/elements/adl/UpbMessage;" }, - ), - method { definingClass == "Lcom/google/android/libraries/elements/adl/UpbMessage;" && name == "jniDecode" }, - ) -} - -internal val BytecodePatchContext.protobufBufferReferenceLegacyMethod by gettingFirstMutableMethodDeclaratively { - accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) - returnType("V") - parameterTypes("I", "Ljava/nio/ByteBuffer;") - opcodes( - Opcode.IPUT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.SUB_INT_2ADDR, - ) -} - -internal val BytecodePatchContext.emptyComponentMethod by gettingFirstMethodDeclaratively { - accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) - parameterTypes() - instructions("EmptyComponent"()) - custom { immutableClassDef.methods.filter { AccessFlags.STATIC.isSet(it.accessFlags) }.size == 1 } -} - -internal val BytecodePatchContext.lithoThreadExecutorMethod by gettingFirstMutableMethodDeclaratively { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - parameterTypes("I", "I", "I") - instructions(1L()) // 1L = default thread timeout. - custom { - immutableClassDef.superclass == "Ljava/util/concurrent/ThreadPoolExecutor;" - } -} - internal val BytecodePatchContext.lithoComponentNameUpbFeatureFlagMethod by gettingFirstMutableMethodDeclaratively { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returnType("Z") diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt index b25df92928..be87c62780 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/litho/filter/LithoFilterPatch.kt @@ -3,91 +3,29 @@ package app.revanced.patches.youtube.misc.litho.filter import app.revanced.patcher.extensions.addInstruction -import app.revanced.patcher.extensions.addInstructions -import app.revanced.patcher.extensions.removeInstructions -import app.revanced.patcher.extensions.replaceInstruction -import app.revanced.patcher.firstClassDef -import app.revanced.patcher.immutableClassDef -import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patches.youtube.shared.conversionContextToStringMethod +import app.revanced.patches.shared.misc.litho.filter.EXTENSION_CLASS_DESCRIPTOR +import app.revanced.patches.shared.misc.litho.filter.lithoFilterPatch +import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceLegacyMethod +import app.revanced.patches.shared.misc.litho.filter.protobufBufferReferenceMethodMatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.playservice.* -import app.revanced.patches.youtube.shared.conversionContextToStringMethod -import app.revanced.util.* -import com.android.tools.smali.dexlib2.AccessFlags +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.insertLiteralOverride +import app.revanced.util.returnLate import com.android.tools.smali.dexlib2.Opcode -lateinit var addLithoFilter: (String) -> Unit - private set - -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/litho/LithoFilterPatch;" - -val lithoFilterPatch = bytecodePatch( - description = "Hooks the method which parses the bytes into a ComponentContext to filter components.", -) { - dependsOn( - sharedExtensionPatch, - versionCheckPatch, - ) - - var filterCount = 0 - - /** - * The following patch inserts a hook into the method that parses the bytes into a ComponentContext. - * This method contains a StringBuilder object that represents the pathBuilder of the component. - * The pathBuilder is used to filter components by their path. - * - * Additionally, the method contains a reference to the component's identifier. - * The identifier is used to filter components by their identifier. - * - * The protobuf buffer is passed along from a different injection point before the filtering occurs. - * The buffer is a large byte array that represents the component tree. - * This byte array is searched for strings that indicate the current component. - * - * All modifications done here must allow all the original code to still execute - * even when filtering, otherwise memory leaks or poor app performance may occur. - * - * The following pseudocode shows how this patch works: - * - * class SomeOtherClass { - * // Called before ComponentContextParser.parseComponent() method. - * public void someOtherMethod(ByteBuffer byteBuffer) { - * ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch. - * ... - * } - * } - * - * class ComponentContextParser { - * public Component parseComponent() { - * ... - * - * if (extensionClass.shouldFilter()) { // Inserted by this patch. - * return emptyComponent; - * } - * return originalUnpatchedComponent; // Original code. - * } - * } - */ - apply { - // Remove dummy filter from extenion static field - // and add the filters included during patching. - lithoFilterMethod.apply { - removeInstructions(2, 4) // Remove dummy filter. - - addLithoFilter = { classDescriptor -> - addInstructions( - 2, - """ - new-instance v1, $classDescriptor - invoke-direct { v1 }, $classDescriptor->()V - const/16 v2, ${filterCount++} - aput-object v1, v0, v2 - """, - ) - } +val lithoFilterPatch = lithoFilterPatch( + componentCreateInsertionIndex = { + if (is_19_17_or_greater) { + indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) + } else { + // 19.16 clobbers p2 so must check at start of the method and not at the return index. + 0 } - - // region Pass the buffer into extension. - + }, + insertProtobufHook = { if (is_20_22_or_greater) { // Hook method that bridges between UPB buffer native code and FB Litho. // Method is found in 19.25+, but is forcefully turned off for 20.21 and lower. @@ -100,91 +38,15 @@ val lithoFilterPatch = bytecodePatch( } } - // Legacy Non native buffer. + // Legacy non-native buffer. protobufBufferReferenceLegacyMethod.addInstruction( 0, "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", ) - - // endregion - - // region Modify the create component method and - // if the component is filtered then return an empty component. - - // Find the identifier/path fields of the conversion context. - - val conversionContextIdentifierField = conversionContextToStringMethod - .findFieldFromToString("identifierProperty=") - - val conversionContextPathBuilderField = conversionContextToStringMethod.immutableClassDef - .fields.single { field -> field.type == "Ljava/lang/StringBuilder;" } - - // Find class and methods to create an empty component. - val builderMethodDescriptor = emptyComponentMethod.immutableClassDef.methods.single { - // The only static method in the class. - method -> - AccessFlags.STATIC.isSet(method.accessFlags) - } - - val emptyComponentField = firstClassDef(builderMethodDescriptor.returnType).fields.single() - - componentCreateMethod.apply { - val insertIndex = if (is_19_17_or_greater) { - indexOfFirstInstructionOrThrow(Opcode.RETURN_OBJECT) - } else { - // 19.16 clobbers p2 so must check at start of the method and not at the return index. - 0 - } - - val freeRegister = findFreeRegister(insertIndex) - val identifierRegister = findFreeRegister(insertIndex, freeRegister) - val pathRegister = findFreeRegister(insertIndex, freeRegister, identifierRegister) - - addInstructionsAtControlFlowLabel( - insertIndex, - """ - move-object/from16 v$freeRegister, p2 - - # 20.41 field is the abstract superclass. - # Verify it's the expected subclass just in case. - instance-of v$identifierRegister, v$freeRegister, ${conversionContextToStringMethod.immutableClassDef.type} - if-eqz v$identifierRegister, :unfiltered - - iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField - iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField - invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z - move-result v$freeRegister - if-eqz v$freeRegister, :unfiltered - - # Return an empty component - move-object/from16 v$freeRegister, p1 - invoke-static { v$freeRegister }, $builderMethodDescriptor - move-result-object v$freeRegister - iget-object v$freeRegister, v$freeRegister, $emptyComponentField - return-object v$freeRegister - - :unfiltered - nop - """, - ) - } - - // endregion - - // region Change Litho thread executor to 1 thread to fix layout issue in unpatched YouTube. - - lithoThreadExecutorMethod.addInstructions( - 0, - """ - invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorCorePoolSize(I)I - move-result p1 - invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->getExecutorMaxThreads(I)I - move-result p2 - """, - ) - - // endregion - + }, + getConversionContextToStringMethod = BytecodePatchContext::conversionContextToStringMethod::get, + getExtractIdentifierFromBuffer = { is_20_21_or_greater }, + executeBlock = { // region A/B test of new Litho native code. // Turn off native code that handles litho component names. If this feature is on then nearly @@ -205,11 +67,8 @@ val lithoFilterPatch = bytecodePatch( false, ) } - // endregion } - - afterDependents { - lithoFilterMethod.replaceInstruction(0, "const/16 v0, $filterCount") - } +) { + dependsOn(sharedExtensionPatch, versionCheckPatch) } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt index d1c376b7d4..77ac486946 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/AdvancedVideoQualityMenuPatch.kt @@ -10,8 +10,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.shared.misc.mapping.ResourceType import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt index a283b07d8f..5f3273718f 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatch.kt @@ -16,9 +16,9 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.InputType import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.shared.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.interaction.seekbar.customTapAndHoldMethodMatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch -import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.playservice.is_19_47_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_34_or_greater