diff --git a/api/revanced-patches.api b/api/revanced-patches.api index a30a71d22..7b5140448 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -279,6 +279,16 @@ public final class app/revanced/patches/googlenews/misc/integrations/Integration public static final field INSTANCE Lapp/revanced/patches/googlenews/misc/integrations/IntegrationsPatch; } +public final class app/revanced/patches/googlephotos/features/SpoofFeaturesPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/SpoofFeaturesPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + +public final class app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint { + public static final field INSTANCE Lapp/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint; +} + public final class app/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch { public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/gms/GmsCoreSupportPatch; } diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofBuildInfoPatch.kt b/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofBuildInfoPatch.kt new file mode 100644 index 000000000..ad4ed411f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofBuildInfoPatch.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.googlephotos.features + +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.all.misc.build.BaseSpoofBuildInfoPatch + +@Patch(description = "Spoof build info to Google Pixel XL.") +internal class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() { + override val brand = "google" + override val manufacturer = "Google" + override val device = "marlin" + override val product = "marlin" + override val model = "Pixel XL" + override val fingerprint = "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys" +} diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt b/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt new file mode 100644 index 000000000..040012d45 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/googlephotos/features/SpoofFeaturesPatch.kt @@ -0,0 +1,87 @@ +package app.revanced.patches.googlephotos.features + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringArrayPatchOption +import app.revanced.patches.googlephotos.features.fingerprints.InitializeFeaturesEnumFingerprint +import app.revanced.util.getReference +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.StringReference + +@Patch( + name = "Spoof features", + description = "Spoofs the device to enable Google Pixel exclusive features, including unlimited storage.", + dependencies = [SpoofBuildInfoPatch::class], + compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")], +) +@Suppress("unused") +object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprint)) { + private val featuresToEnable by stringArrayPatchOption( + "featuresToEnable", + arrayOf( + "com.google.android.apps.photos.NEXUS_PRELOAD", + "com.google.android.apps.photos.nexus_preload", + ), + title = "Features to enable", + description = "Google Pixel exclusive features to enable. Features up to Pixel XL enable the unlimited storage feature.", + required = true, + ) + + private val featuresToDisable by stringArrayPatchOption( + "featuresToDisable", + arrayOf( + "com.google.android.apps.photos.PIXEL_2017_PRELOAD", + "com.google.android.apps.photos.PIXEL_2018_PRELOAD", + "com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD", + "com.google.android.apps.photos.PIXEL_2019_PRELOAD", + "com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2020_EXPERIENCE", + "com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2021_EXPERIENCE", + "com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2022_EXPERIENCE", + "com.google.android.feature.PIXEL_2023_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2023_EXPERIENCE", + "com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2024_EXPERIENCE", + "com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE", + ), + title = "Features to disable", + description = "Google Pixel exclusive features to disable." + + "Features after Pixel XL may have to be disabled for unlimited storage depending on the device.", + required = true, + ) + + override fun execute(context: BytecodeContext) { + val featuresToEnable = featuresToEnable!!.toSet() + val featuresToDisable = featuresToDisable!!.toSet() + + InitializeFeaturesEnumFingerprint.resultOrThrow().let { result -> + result.mutableMethod.apply { + getInstructions().filter { it.opcode == Opcode.CONST_STRING }.forEach { + val feature = it.getReference()!!.string + + val spoofedFeature = when (feature) { + in featuresToEnable -> "android.hardware.wifi" + in featuresToDisable -> "dummy" + else -> return@forEach + } + + val constStringIndex = it.location.index + val constStringRegister = (it as OneRegisterInstruction).registerA + + replaceInstruction( + constStringIndex, + "const-string v$constStringRegister, \"$spoofedFeature\"", + ) + } + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint.kt b/src/main/kotlin/app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint.kt new file mode 100644 index 000000000..b3fde939a --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/googlephotos/features/fingerprints/InitializeFeaturesEnumFingerprint.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.googlephotos.features.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +object InitializeFeaturesEnumFingerprint : MethodFingerprint( + strings = listOf("com.google.android.apps.photos.NEXUS_PRELOAD"), +) diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch.kt b/src/main/kotlin/app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch.kt index 188f309b7..d45ab0fc6 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch.kt @@ -18,11 +18,13 @@ import app.revanced.patches.shared.misc.gms.fingerprints.GooglePlayUtilityFinger import app.revanced.patches.shared.misc.gms.fingerprints.ServiceCheckFingerprint import app.revanced.util.exception import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c +import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.StringReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference import com.android.tools.smali.dexlib2.util.MethodUtil @@ -112,11 +114,20 @@ abstract class BaseGmsCoreSupportPatch( } // Verify GmsCore is installed and whitelisted for power optimizations and background usage. - mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions( - 0, - "invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" + - "checkGmsCore(Landroid/app/Activity;)V", - ) ?: throw mainActivityOnCreateFingerprint.exception + mainActivityOnCreateFingerprint.result?.mutableMethod?.apply { + val setContextIndex = indexOfFirstInstructionOrThrow { + val reference = getReference() ?: return@indexOfFirstInstructionOrThrow false + + reference.toString() == "Lapp/revanced/integrations/shared/Utils;->setContext(Landroid/content/Context;)V" + } + + // Add after setContext call, because this patch needs the context. + addInstructions( + setContextIndex + 1, + "invoke-static/range { p0 .. p0 }, Lapp/revanced/integrations/shared/GmsCoreSupport;->" + + "checkGmsCore(Landroid/app/Activity;)V", + ) + } ?: throw mainActivityOnCreateFingerprint.exception // Change the vendor of GmsCore in ReVanced Integrations. GmsCoreSupportFingerprint.result?.mutableClass?.methods