From 752a776101291f337691e970136b7267da951299 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 28 May 2025 06:55:25 -0300 Subject: [PATCH] refactor(Spotify): Improve protobuf array list mutability patch (#5053) Co-authored-by: oSumAtrIX --- .../patches/spotify/misc/Fingerprints.kt | 11 +++++-- .../spotify/misc/UnlockPremiumPatch.kt | 32 +++++++------------ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/Fingerprints.kt index daddd2841..708ec7e77 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/Fingerprints.kt @@ -65,8 +65,15 @@ internal val protobufListsFingerprint = fingerprint { custom { method, _ -> method.name == "emptyProtobufList" } } -internal val protobufListRemoveFingerprint = fingerprint { - custom { method, _ -> method.name == "remove" } +internal val abstractProtobufListEnsureIsMutableFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + returns("V") + custom { method, _ -> + method.indexOfFirstInstruction { + getReference()?.type == "Ljava/lang/UnsupportedOperationException;" + } >= 0 + } } internal val homeSectionFingerprint = fingerprint { diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt index 90bfc1694..b34df2aac 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt @@ -3,7 +3,6 @@ package app.revanced.patches.spotify.misc import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions 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.replaceInstruction 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.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch -import app.revanced.util.getReference -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import app.revanced.util.* import app.revanced.util.toPublicAccessFlags import com.android.tools.smali.dexlib2.Opcode 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) - // 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()!!.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. - // 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. - // Instead, remove the method call that checks if the list is unmodifiable. - protobufListRemoveFingerprint.match(protobufListClassDef).method.apply { - val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() - 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) - } - + // Instead, return early in the method that throws an error if the list is unmutable. + abstractProtobufListEnsureIsMutableFingerprint.match(abstractProtobufListClassDef) + .method.returnEarly() // Make featureTypeCase_ accessible so we can check the home section type in the extension. homeSectionFingerprint.classDef.publicizeField("featureTypeCase_")