Compare commits

..

No commits in common. "main" and "v6.1.0-dev.3" have entirely different histories.

14 changed files with 96 additions and 611 deletions

View file

@ -1,33 +1,3 @@
# [6.1.0](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.1.0) (2026-03-18)
### Bug Fixes
* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757))
* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34))
### Features
* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c))
* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468))
* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24))
* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7))
* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be))
# [6.1.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.3...v6.1.0-dev.4) (2026-03-18)
### Bug Fixes
* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34))
### Features
* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c))
* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7))
# [6.1.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.2...v6.1.0-dev.3) (2026-03-18) # [6.1.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.2...v6.1.0-dev.3) (2026-03-18)

View file

@ -1,4 +1,4 @@
package app.revanced.extension.play; package app.revanced.extension.playintegrity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;

View file

@ -114,7 +114,7 @@ public class CustomBrandingPatch {
/** /**
* Injection point. * Injection point.
* <p> *
* The total number of app name aliases, including dummy aliases. * The total number of app name aliases, including dummy aliases.
*/ */
private static int numberOfPresetAppNames() { private static int numberOfPresetAppNames() {
@ -146,13 +146,13 @@ public class CustomBrandingPatch {
public static int getDefaultAppNameIndex() { public static int getDefaultAppNameIndex() {
return userProvidedCustomName() return userProvidedCustomName()
? numberOfPresetAppNames() ? numberOfPresetAppNames()
: 2; : 1;
} }
public static BrandingTheme getDefaultIconStyle() { public static BrandingTheme getDefaultIconStyle() {
return userProvidedCustomIcon() return userProvidedCustomIcon()
? BrandingTheme.CUSTOM ? BrandingTheme.CUSTOM
: BrandingTheme.ROUNDED; : BrandingTheme.ORIGINAL;
} }
/** /**

View file

@ -4,4 +4,4 @@ org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
android.uniquePackageNames = false android.uniquePackageNames = false
kotlin.code.style = official kotlin.code.style = official
version = 6.1.0 version = 6.1.0-dev.3

View file

@ -89,14 +89,10 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa
public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String; public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String;
} }
public final class app/revanced/patches/all/misc/play/DisablePlayIntegrityKt { public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt {
public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/Patch;
} }
public final class app/revanced/patches/all/misc/play/SpoofPlayAgeSignalsKt {
public static final fun getSpoofPlayAgeSignalsPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt { public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
@ -129,14 +125,6 @@ public final class app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofin
public static final fun getEnableROMSignatureSpoofingPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun getEnableROMSignatureSpoofingPatch ()Lapp/revanced/patcher/patch/Patch;
} }
public final class app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatchKt {
public static final fun getSpoofKeystoreSecurityLevelPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatchKt {
public static final fun getSpoofRootOfTrustPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt { public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
public static final fun getSetTargetSDKVersion34Patch ()Lapp/revanced/patcher/patch/Patch; public static final fun getSetTargetSDKVersion34Patch ()Lapp/revanced/patcher/patch/Patch;
} }
@ -377,10 +365,6 @@ public final class app/revanced/patches/instagram/story/flipping/DisableStoryAut
public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/Patch;
} }
public final class app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatchKt {
public static final fun getEnableLocationStickerRedesignPatch ()Lapp/revanced/patcher/patch/Patch;
}
public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt { public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch; public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch;
} }

View file

@ -6,8 +6,7 @@ import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption import app.revanced.patcher.patch.intOption
import app.revanced.patcher.patch.stringOption import app.revanced.patcher.patch.stringOption
import app.revanced.util.forEachInstructionAsSequence import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -34,8 +33,7 @@ val spoofSIMProviderPatch = bytecodePatch(
validator = { it: String? -> it == null || it.uppercase() in countries.values }, validator = { it: String? -> it == null || it.uppercase() in countries.values },
) )
fun isMccMncValid(it: Int?) = it == null || (it in 10000..999999) fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
fun isNumericValid(it: String?, length: Int) = it.isNullOrBlank() || it.equals("random", true) || it.length == length
val networkCountryIso by isoCountryPatchOption("Network ISO country code") val networkCountryIso by isoCountryPatchOption("Network ISO country code")
@ -63,119 +61,46 @@ val spoofSIMProviderPatch = bytecodePatch(
description = "The full name of the SIM operator.", description = "The full name of the SIM operator.",
) )
val imei by stringOption(
name = "IMEI value",
description = "15-digit IMEI to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 15) },
)
val meid by stringOption(
name = "MEID value",
description = "14-char hex MEID to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 14) },
)
val imsi by stringOption(
name = "IMSI (Subscriber ID)",
description = "15-digit IMSI to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 15) },
)
val iccid by stringOption(
name = "ICCID (SIM Serial)",
description = "19-digit ICCID to spoof, blank to skip, or 'random'.",
validator = { isNumericValid(it, 19) },
)
val phone by stringOption(
name = "Phone number",
description = "Phone number to spoof, blank to skip, or 'random'.",
validator = { it.isNullOrBlank() || it.equals("random", ignoreCase = true) || it.startsWith("+") },
)
dependsOn( dependsOn(
bytecodePatch { transformInstructionsPatch(
apply { filterMap = { _, _, instruction, instructionIndex ->
fun generateRandomNumeric(length: Int) = (1..length).map { ('0'..'9').random() }.joinToString("") if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
fun String?.computeSpoof(randomizer: () -> String): String? { val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null
if (this.isNullOrBlank()) return null
if (this.equals("random", ignoreCase = true)) return randomizer() val match = MethodCall.entries.firstOrNull { search ->
return this MethodUtil.methodSignaturesMatch(reference, search.reference)
} ?: return@transformInstructionsPatch null
val replacement = when (match) {
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
MethodCall.NetworkOperator -> networkOperator?.toString()
MethodCall.NetworkOperatorName -> networkOperatorName
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
MethodCall.SimOperator -> simOperator?.toString()
MethodCall.SimOperatorName -> simOperatorName
} }
replacement?.let { instructionIndex to it }
// Calculate the Luhn checksum (mod 10) to generate a valid 15th digit, standard for IMEI numbers. },
// Structure of an IMEI is as follows: transform = ::transformMethodCall,
// TAC (Type Allocation Code): First 8 digits (e.g., "86" + 6 digits) ),
// SNR (Serial Number): Next 6 digits
// CD (Check Digit): The 15th digit
val computedImei = imei.computeSpoof {
val prefix = "86" + generateRandomNumeric(12)
val sum = prefix.mapIndexed { i, c ->
var d = c.digitToInt()
// Double every second digit (index 1, 3, 5...).
if (i % 2 != 0) {
d *= 2
// If result is two digits (e.g. 14), sum them (1+4=5).
// This is mathematically equivalent to d - 9.
if (d > 9) d -= 9
}
d
}.sum()
// Append the calculated check digit to the 14-digit prefix.
prefix + ((10 - (sum % 10)) % 10)
}
val computedMeid = meid.computeSpoof { (1..14).map { "0123456789ABCDEF".random() }.joinToString("") }?.uppercase()
val computedImsi = imsi.computeSpoof { generateRandomNumeric(15) }
val computedIccid = iccid.computeSpoof { "89" + generateRandomNumeric(17) }
val computedPhone = phone.computeSpoof { "+" + generateRandomNumeric(11) }
forEachInstructionAsSequence(
match = { _, _, instruction, instructionIndex ->
if (instruction !is ReferenceInstruction) return@forEachInstructionAsSequence null
val reference = instruction.reference as? MethodReference ?: return@forEachInstructionAsSequence null
val match = MethodCall.entries.firstOrNull { search ->
MethodUtil.methodSignaturesMatch(reference, search.reference)
} ?: return@forEachInstructionAsSequence null
val replacement = when (match) {
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
MethodCall.NetworkOperator -> networkOperator?.toString()
MethodCall.NetworkOperatorName -> networkOperatorName
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
MethodCall.SimOperator -> simOperator?.toString()
MethodCall.SimOperatorName -> simOperatorName
MethodCall.Imei, MethodCall.ImeiWithSlot, MethodCall.DeviceId, MethodCall.DeviceIdWithSlot -> computedImei
MethodCall.Meid, MethodCall.MeidWithSlot -> computedMeid
MethodCall.SubscriberId, MethodCall.SubscriberIdWithSlot -> computedImsi
MethodCall.SimSerialNumber, MethodCall.SimSerialNumberWithSlot -> computedIccid
MethodCall.Line1Number, MethodCall.Line1NumberWithSlot -> computedPhone
}
replacement?.let { instructionIndex to it }
},
transform = ::transformMethodCall
)
}
},
) )
} }
private fun transformMethodCall(mutableMethod: MutableMethod, entry: Pair<Int, String>) { private fun transformMethodCall(
val (index, value) = entry mutableMethod: MutableMethod,
val nextInstr = mutableMethod.getInstruction<Instruction>(index + 1) entry: Pair<Int, String>,
) {
val (instructionIndex, methodCallValue) = entry
if (nextInstr.opcode.name != "move-result-object") { // Get the register which would have contained the return value
mutableMethod.replaceInstruction(index, "nop") val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
return
}
val register = (nextInstr as OneRegisterInstruction).registerA // Replace the move-result instruction with our fake value
mutableMethod.replaceInstruction(index, "const-string v$register, \"$value\"") mutableMethod.replaceInstruction(
mutableMethod.replaceInstruction(index + 1, "nop") instructionIndex + 1,
"const-string v$register, \"$methodCallValue\"",
)
} }
private enum class MethodCall( private enum class MethodCall(
@ -229,100 +154,4 @@ private enum class MethodCall(
"Ljava/lang/String;", "Ljava/lang/String;",
), ),
), ),
Imei(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getImei",
emptyList(),
"Ljava/lang/String;"
),
),
ImeiWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getImei",
listOf("I"),
"Ljava/lang/String;"
),
),
DeviceId(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getDeviceId",
emptyList(),
"Ljava/lang/String;"
),
),
DeviceIdWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getDeviceId",
listOf("I"),
"Ljava/lang/String;"
),
),
Meid(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getMeid",
emptyList(),
"Ljava/lang/String;"
),
),
MeidWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getMeid",
listOf("I"),
"Ljava/lang/String;"
),
),
SubscriberId(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSubscriberId",
emptyList(),
"Ljava/lang/String;"
)
),
SubscriberIdWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSubscriberId",
listOf("I"),
"Ljava/lang/String;"
)
),
SimSerialNumber(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimSerialNumber",
emptyList(),
"Ljava/lang/String;"
)
),
SimSerialNumberWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getSimSerialNumber",
listOf("I"),
"Ljava/lang/String;"
)
),
Line1Number(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getLine1Number",
emptyList(),
"Ljava/lang/String;"
)
),
Line1NumberWithSlot(
ImmutableMethodReference(
"Landroid/telephony/TelephonyManager;",
"getLine1Number",
listOf("I"),
"Ljava/lang/String;"
)
)
} }

View file

@ -3,7 +3,7 @@ package app.revanced.patches.all.misc.connectivity.wifi.spoof
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.IMethodCall import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.util.forEachInstructionAsSequence import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch" "Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
@ -19,32 +19,29 @@ val spoofWiFiConnectionPatch = bytecodePatch(
extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve") extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve")
dependsOn( dependsOn(
bytecodePatch { transformInstructionsPatch(
apply { filterMap = { classDef, _, instruction, instructionIndex ->
forEachInstructionAsSequence( filterMapInstruction35c<MethodCall>(
match = { classDef, _, instruction, instructionIndex -> EXTENSION_CLASS_DESCRIPTOR_PREFIX,
filterMapInstruction35c<MethodCall>( classDef,
EXTENSION_CLASS_DESCRIPTOR_PREFIX, instruction,
classDef, instructionIndex,
instruction, )
instructionIndex, },
) transform = { method, entry ->
}, val (methodType, instruction, instructionIndex) = entry
transform = { method, entry -> methodType.replaceInvokeVirtualWithExtension(
val (methodType, instruction, instructionIndex) = entry EXTENSION_CLASS_DESCRIPTOR,
methodType.replaceInvokeVirtualWithExtension( method,
EXTENSION_CLASS_DESCRIPTOR, instruction,
method, instructionIndex,
instruction, )
instructionIndex, },
) ),
})
}
},
) )
} }
// Information about method calls we want to replace. // Information about method calls we want to replace
@Suppress("unused") @Suppress("unused")
private enum class MethodCall( private enum class MethodCall(
override val definedClassName: String, override val definedClassName: String,
@ -92,13 +89,13 @@ private enum class MethodCall(
"Landroid/net/NetworkInfo;", "Landroid/net/NetworkInfo;",
"getState", "getState",
arrayOf(), arrayOf(),
$$"Landroid/net/NetworkInfo$State;", "Landroid/net/NetworkInfo\$State;",
), ),
GetDetailedState( GetDetailedState(
"Landroid/net/NetworkInfo;", "Landroid/net/NetworkInfo;",
"getDetailedState", "getDetailedState",
arrayOf(), arrayOf(),
$$"Landroid/net/NetworkInfo$DetailedState;", "Landroid/net/NetworkInfo\$DetailedState;",
), ),
IsActiveNetworkMetered( IsActiveNetworkMetered(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
@ -135,7 +132,7 @@ private enum class MethodCall(
"registerBestMatchingNetworkCallback", "registerBestMatchingNetworkCallback",
arrayOf( arrayOf(
"Landroid/net/NetworkRequest;", "Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;", "Landroid/os/Handler;",
), ),
"V", "V",
@ -143,19 +140,19 @@ private enum class MethodCall(
RegisterDefaultNetworkCallback1( RegisterDefaultNetworkCallback1(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
"registerDefaultNetworkCallback", "registerDefaultNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"), arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V", "V",
), ),
RegisterDefaultNetworkCallback2( RegisterDefaultNetworkCallback2(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
"registerDefaultNetworkCallback", "registerDefaultNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/os/Handler;"), arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
"V", "V",
), ),
RegisterNetworkCallback1( RegisterNetworkCallback1(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
"registerNetworkCallback", "registerNetworkCallback",
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"), arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V", "V",
), ),
RegisterNetworkCallback2( RegisterNetworkCallback2(
@ -169,7 +166,7 @@ private enum class MethodCall(
"registerNetworkCallback", "registerNetworkCallback",
arrayOf( arrayOf(
"Landroid/net/NetworkRequest;", "Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;", "Landroid/os/Handler;",
), ),
"V", "V",
@ -177,13 +174,13 @@ private enum class MethodCall(
RequestNetwork1( RequestNetwork1(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
"requestNetwork", "requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"), arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V", "V",
), ),
RequestNetwork2( RequestNetwork2(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
"requestNetwork", "requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;", "I"), arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
"V", "V",
), ),
RequestNetwork3( RequestNetwork3(
@ -191,7 +188,7 @@ private enum class MethodCall(
"requestNetwork", "requestNetwork",
arrayOf( arrayOf(
"Landroid/net/NetworkRequest;", "Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;", "Landroid/os/Handler;",
), ),
"V", "V",
@ -207,7 +204,7 @@ private enum class MethodCall(
"requestNetwork", "requestNetwork",
arrayOf( arrayOf(
"Landroid/net/NetworkRequest;", "Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;", "Landroid/os/Handler;",
"I", "I",
), ),
@ -216,7 +213,7 @@ private enum class MethodCall(
UnregisterNetworkCallback1( UnregisterNetworkCallback1(
"Landroid/net/ConnectivityManager;", "Landroid/net/ConnectivityManager;",
"unregisterNetworkCallback", "unregisterNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"), arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V", "V",
), ),
UnregisterNetworkCallback2( UnregisterNetworkCallback2(

View file

@ -1,138 +0,0 @@
package app.revanced.patches.all.misc.play
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.removeInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
import app.revanced.patcher.patch.option
import app.revanced.util.forEachInstructionAsSequence
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
@Suppress("unused")
val spoofPlayAgeSignalsPatch = bytecodePatch(
name = "Spoof Play Age Signals",
description = "Spoofs Google Play data about the user's age and verification status.",
use = false,
) {
val lowerAgeBound by intOption(
name = "Lower age bound",
description = "A positive integer.",
default = 18,
validator = { it == null || it > 0 },
)
val upperAgeBound by intOption(
name = "Upper age bound",
description = "A positive integer. Must be greater than the lower age bound.",
default = Int.MAX_VALUE,
validator = { it == null || it > lowerAgeBound!! },
)
val userStatus by intOption(
name = "User status",
description = "An integer representing the user status.",
default = UserStatus.VERIFIED.value,
values = UserStatus.entries.associate { it.name to it.value },
)
apply {
forEachInstructionAsSequence(match = { classDef, _, instruction, instructionIndex ->
// Avoid patching the library itself.
if (classDef.type.startsWith("Lcom/google/android/play/agesignals/")) return@forEachInstructionAsSequence null
// Keep method calls only.
val reference = instruction.getReference<MethodReference>()
?: return@forEachInstructionAsSequence null
val match = MethodCall.entries.firstOrNull {
reference == it.reference
} ?: return@forEachInstructionAsSequence null
val replacement = when (match) {
MethodCall.AgeLower -> lowerAgeBound!!
MethodCall.AgeUpper -> upperAgeBound!!
MethodCall.UserStatus -> userStatus!!
}
replacement.let { instructionIndex to it }
}, transform = { method, entry ->
val (instructionIndex, replacement) = entry
// Get the register which would have contained the return value.
val register = method.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
// Replace the call instructions with the spoofed value.
method.removeInstructions(instructionIndex, 2)
method.addInstructions(
instructionIndex,
"""
const v$register, $replacement
invoke-static { v$register }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
move-result-object v$register
""".trimIndent(),
)
})
}
}
/**
* See [AgeSignalsResult](https://developer.android.com/google/play/age-signals/reference/com/google/android/play/agesignals/AgeSignalsResult).
*/
private enum class MethodCall(
val reference: MethodReference,
) {
AgeLower(
ImmutableMethodReference(
"Lcom/google/android/play/agesignals/AgeSignalsResult;",
"ageLower",
emptyList(),
"Ljava/lang/Integer;",
),
),
AgeUpper(
ImmutableMethodReference(
"Lcom/google/android/play/agesignals/AgeSignalsResult;",
"ageUpper",
emptyList(),
"Ljava/lang/Integer;",
),
),
UserStatus(
ImmutableMethodReference(
"Lcom/google/android/play/agesignals/AgeSignalsResult;",
"userStatus",
emptyList(),
"Ljava/lang/Integer;",
),
),
}
/**
* All possible user verification statuses.
*
* See [AgeSignalsVerificationStatus](https://developer.android.com/google/play/age-signals/reference/com/google/android/play/agesignals/model/AgeSignalsVerificationStatus).
*/
private enum class UserStatus(val value: Int) {
/** The user provided their age, but it hasn't been verified yet. */
DECLARED(5),
/** The user is 18+. */
VERIFIED(0),
/** The user's guardian has set the age for him. */
SUPERVISED(1),
/** The user's guardian hasn't approved the significant changes yet. */
SUPERVISED_APPROVAL_PENDING(2),
/** The user's guardian has denied approval for one or more pending significant changes. */
SUPERVISED_APPROVAL_DENIED(3),
/** The user is not verified or supervised. */
UNKNOWN(4),
}

View file

@ -1,4 +1,4 @@
package app.revanced.patches.all.misc.play package app.revanced.patches.all.misc.playintegrity
import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
@ -9,7 +9,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/play/DisablePlayIntegrityPatch;" private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;"
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference( private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/content/Context;", "Landroid/content/Context;",

View file

@ -1,28 +0,0 @@
package app.revanced.patches.all.misc.spoof
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.forEachInstructionAsSequence
@Suppress("unused")
val spoofKeystoreSecurityLevelPatch = bytecodePatch(
name = "Spoof keystore security level",
description = "Forces apps to see Keymaster and Attestation security levels as 'StrongBox' (Level 2).",
use = false
) {
apply {
forEachInstructionAsSequence(
match = { _, method, _, _ ->
// Match methods by comparing the current method to a reference criteria.
val name = method.name.lowercase()
if (name.contains("securitylevel") && method.returnType == "I") method else null
},
transform = { mutableMethod, _ ->
// Ensure the method has an implementation before replacing.
if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) {
mutableMethod.replaceInstructions(0, "const/4 v0, 0x2\nreturn v0")
}
}
)
}
}

View file

@ -1,70 +0,0 @@
package app.revanced.patches.all.misc.spoof
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@Suppress("unused")
val spoofRootOfTrustPatch = bytecodePatch(
name = "Spoof root of trust",
description = "Spoofs device integrity states (Locked Bootloader, Verified OS) for apps that perform local certificate attestation.",
use = false
) {
apply {
forEachInstructionAsSequence(
match = { _, method, _, _ ->
MethodCall.entries.firstOrNull { MethodUtil.methodSignaturesMatch(method, it.reference) }
},
transform = { mutableMethod, methodCall ->
if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) {
mutableMethod.replaceInstructions(0, methodCall.replacementInstructions)
}
}
)
}
}
private enum class MethodCall(
val reference: MethodReference,
val replacementInstructions: String,
) {
IsDeviceLockedRootOfTrust(
ImmutableMethodReference(
"LRootOfTrust;",
"isDeviceLocked",
emptyList(),
"Z"
),
"const/4 v0, 0x1\nreturn v0",
),
GetVerifiedBootStateRootOfTrust(
ImmutableMethodReference(
"LRootOfTrust;",
"getVerifiedBootState",
emptyList(),
"I"
),
"const/4 v0, 0x0\nreturn v0",
),
IsDeviceLockedAttestation(
ImmutableMethodReference(
"LAttestation;",
"isDeviceLocked",
emptyList(),
"Z"
),
"const/4 v0, 0x1\nreturn v0",
),
GetVerifiedBootStateAttestation(
ImmutableMethodReference(
"LAttestation;",
"getVerifiedBootState",
emptyList(),
"I"
),
"const/4 v0, 0x0\nreturn v0",
),
}

View file

@ -1,20 +0,0 @@
package app.revanced.patches.instagram.story.locationsticker
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val enableLocationStickerRedesignPatch = bytecodePatch(
name = "Enable location sticker redesign",
description = "Unlocks the redesigned location sticker with additional style options.",
use = false,
) {
compatibleWith("com.instagram.android")
apply {
// The gate method reads a MobileConfig boolean flag and returns it directly.
// Returning early with true bypasses the flag check entirely,
// enabling the redesigned sticker styles regardless of server configuration.
locationStickerRedesignGateMethodMatch.method.returnEarly(true)
}
}

View file

@ -1,16 +0,0 @@
package app.revanced.patches.instagram.story.locationsticker
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
// MobileConfig boolean key that gates the redesigned location sticker styles.
// The method containing this constant reads the flag and returns it directly,
// making it the sole control point for the feature. The key is stable across
// app updates as MobileConfig keys are server-assigned constants.
private const val LOCATION_STICKER_REDESIGN_CONFIG_KEY = 0x8105a100041e0dL
internal val BytecodePatchContext.locationStickerRedesignGateMethodMatch by composingFirstMethod {
instructions(LOCATION_STICKER_REDESIGN_CONFIG_KEY())
}

View file

@ -3,6 +3,7 @@ package app.revanced.patches.shared.layout.branding
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.getInstruction import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.firstImmutableClassDef
import app.revanced.patcher.patch.* import app.revanced.patcher.patch.*
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
@ -125,14 +126,14 @@ internal fun baseCustomBrandingPatch(
val getBuilderIndex = if (isYouTubeMusic) { val getBuilderIndex = if (isYouTubeMusic) {
// YT Music the field is not a plain object type. // YT Music the field is not a plain object type.
indexOfFirstInstructionOrThrow { indexOfFirstInstructionOrThrow {
getReference<FieldReference>()?.type == $$"Landroid/app/Notification$Builder;" getReference<FieldReference>()?.type == "Landroid/app/Notification\$Builder;"
} }
} else { } else {
// Find the field name of the notification builder. Field is an Object type. // Find the field name of the notification builder. Field is an Object type.
val builderCastIndex = indexOfFirstInstructionOrThrow { val builderCastIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<TypeReference>() val reference = getReference<TypeReference>()
opcode == Opcode.CHECK_CAST && opcode == Opcode.CHECK_CAST &&
reference?.type == $$"Landroid/app/Notification$Builder;" reference?.type == "Landroid/app/Notification\$Builder;"
} }
indexOfFirstInstructionReversedOrThrow(builderCastIndex) { indexOfFirstInstructionReversedOrThrow(builderCastIndex) {
getReference<FieldReference>()?.type == "Ljava/lang/Object;" getReference<FieldReference>()?.type == "Ljava/lang/Object;"
@ -147,11 +148,11 @@ internal fun baseCustomBrandingPatch(
).forEach { index -> ).forEach { index ->
addInstructionsAtControlFlowLabel( addInstructionsAtControlFlowLabel(
index, index,
$$""" """
move-object/from16 v0, p0 move-object/from16 v0, p0
iget-object v0, v0, $$builderFieldName iget-object v0, v0, $builderFieldName
check-cast v0, Landroid/app/Notification$Builder; check-cast v0, Landroid/app/Notification${'$'}Builder;
invoke-static { v0 }, $$EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification$Builder;)V invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V
""", """,
) )
} }
@ -161,37 +162,16 @@ internal fun baseCustomBrandingPatch(
) )
afterDependents { afterDependents {
val useCustomName = customName != null
val useCustomIcon = customIcon != null
val isRootInstall = setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName
// Can only check if app is root installation by checking if change package name patch is in use. // Can only check if app is root installation by checking if change package name patch is in use.
// and can only do that in the afterDependents block here. // and can only do that in the afterDependents block here.
// The UI preferences cannot be selectively added here, because the settings afterDependents block // The UI preferences cannot be selectively added here, because the settings afterDependents block
// may have already run and the settings are already wrote to file. // may have already run and the settings are already wrote to file.
// Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes), // Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes),
// and the non-functional in-app settings are removed on app startup by extension code. // and the non-functional in-app settings are removed on app startup by extension code.
if (isRootInstall && (useCustomName || useCustomIcon)) { if (customName != null || customIcon != null) {
Logger.getLogger(this::class.java.name).warning( if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) {
"Custom branding does not work with root installation. No changes applied." Logger.getLogger(this::class.java.name).warning(
) "Custom branding does not work with root installation. No changes applied.",
}
if (!isRootInstall || useCustomName) {
document("AndroidManifest.xml").use { document ->
val application = document.getElementsByTagName("application").item(0) as Element
application.setAttribute(
"android:label",
if (useCustomName) {
// Use custom name everywhere.
customName
} else {
// The YT application name can appear in some places alongside the system
// YouTube app, such as the settings app list and in the "open with" file picker.
// Because the YouTube app cannot be completely uninstalled and only disabled,
// use a custom name for this situation to disambiguate which app is which.
"@string/revanced_custom_branding_name_entry_2"
}
) )
} }
} }
@ -332,19 +312,16 @@ internal fun baseCustomBrandingPatch(
activityAliasNameWithIntents, activityAliasNameWithIntents,
).childNodes ).childNodes
// If user provides a custom icon, then change the application icon ('static' icon) // The YT application name can appear in some places alongside the system
// which shows as the push notification for some devices, in the app settings, // YouTube app, such as the settings app list and in the "open with" file picker.
// and as the icon for the apk before installing. // Because the YouTube app cannot be completely uninstalled and only disabled,
// This icon cannot be dynamically selected and this change must only be done if the // use a custom name for this situation to disambiguate which app is which.
// user provides an icon otherwise there is no way to restore the original YouTube icon. application.setAttribute(
if (useCustomIcon) { "android:label",
application.setAttribute( "@string/revanced_custom_branding_name_entry_2",
"android:icon", )
"@mipmap/revanced_launcher_custom"
)
}
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 2 // 1 indexing. val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing.
val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing. val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing.
for (appNameIndex in 1..numberOfPresetAppNames) { for (appNameIndex in 1..numberOfPresetAppNames) {
@ -359,7 +336,7 @@ internal fun baseCustomBrandingPatch(
iconMipmapName = originalLauncherIconName, iconMipmapName = originalLauncherIconName,
appNameIndex = appNameIndex, appNameIndex = appNameIndex,
useCustomName = useCustomNameLabel, useCustomName = useCustomNameLabel,
enabled = false, enabled = (appNameIndex == 1),
intentFilters, intentFilters,
), ),
) )