refactor(Spotify): Improve protobuf array list mutability patch (#5053)

Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
Nuckyz 2025-05-28 06:55:25 -03:00 committed by GitHub
parent ef21efa0e8
commit 752a776101
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 21 additions and 22 deletions

View File

@ -65,8 +65,15 @@ internal val protobufListsFingerprint = fingerprint {
custom { method, _ -> method.name == "emptyProtobufList" } custom { method, _ -> method.name == "emptyProtobufList" }
} }
internal val protobufListRemoveFingerprint = fingerprint { internal val abstractProtobufListEnsureIsMutableFingerprint = fingerprint {
custom { method, _ -> method.name == "remove" } accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters()
returns("V")
custom { method, _ ->
method.indexOfFirstInstruction {
getReference<TypeReference>()?.type == "Ljava/lang/UnsupportedOperationException;"
} >= 0
}
} }
internal val homeSectionFingerprint = fingerprint { internal val homeSectionFingerprint = fingerprint {

View File

@ -3,7 +3,6 @@ package app.revanced.patches.spotify.misc
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
@ -12,9 +11,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference import app.revanced.util.*
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.toPublicAccessFlags import app.revanced.util.toPublicAccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
@ -133,29 +130,24 @@ val unlockPremiumPatch = bytecodePatch(
} }
val protobufListClassDef = with(protobufListsFingerprint.originalMethod) { val protobufArrayListClassDef = with(protobufListsFingerprint.originalMethod) {
val emptyProtobufListGetIndex = indexOfFirstInstructionOrThrow(Opcode.SGET_OBJECT) val emptyProtobufListGetIndex = indexOfFirstInstructionOrThrow(Opcode.SGET_OBJECT)
// Find the protobuffer list class using the definingClass which contains the empty list static value. // Find the protobuf array list class using the definingClass which contains the empty list static value.
val classType = getInstruction(emptyProtobufListGetIndex).getReference<FieldReference>()!!.definingClass val classType = getInstruction(emptyProtobufListGetIndex).getReference<FieldReference>()!!.definingClass
classes.find { it.type == classType } ?: throw PatchException("Could not find protobuffer list class.") classes.find { it.type == classType } ?: throw PatchException("Could not find protobuf array list class.")
} }
val abstractProtobufListClassDef = classes.find {
it.type == protobufArrayListClassDef.superclass
} ?: throw PatchException("Could not find abstract protobuf list class.")
// Need to allow mutation of the list so the home ads sections can be removed. // Need to allow mutation of the list so the home ads sections can be removed.
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability. // Protobuf array list has an 'isMutable' boolean parameter that sets the mutability.
// Forcing that always on breaks unrelated code in strange ways. // Forcing that always on breaks unrelated code in strange ways.
// Instead, remove the method call that checks if the list is unmodifiable. // Instead, return early in the method that throws an error if the list is unmutable.
protobufListRemoveFingerprint.match(protobufListClassDef).method.apply { abstractProtobufListEnsureIsMutableFingerprint.match(abstractProtobufListClassDef)
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow { .method.returnEarly()
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.returnType == "V" && reference.parameterTypes.isEmpty()
}
// Remove the method call that throws an exception if the list is not mutable.
removeInstruction(invokeThrowUnmodifiableIndex)
}
// Make featureTypeCase_ accessible so we can check the home section type in the extension. // Make featureTypeCase_ accessible so we can check the home section type in the extension.
homeSectionFingerprint.classDef.publicizeField("featureTypeCase_") homeSectionFingerprint.classDef.publicizeField("featureTypeCase_")