use shared litho patch
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de> Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
This commit is contained in:
parent
58e141f735
commit
b57dbaf1b1
16 changed files with 114 additions and 296 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* <p>
|
||||
* <b>This is set during patching, do not change manually.</b>
|
||||
*/
|
||||
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];
|
||||
|
||||
/**
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<FieldReference>()
|
||||
reference?.definingClass == conversionContextToStringMethod.immutableClassDef.type &&
|
||||
reference.type == "Ljava/lang/String;"
|
||||
}
|
||||
val conversionContextToStringMethod = getConversionContextToStringMethod()
|
||||
|
||||
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()!!
|
||||
}
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.*
|
||||
|
|
|
|||
|
|
@ -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.*
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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-><init>()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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue