mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 05:07:41 +02:00
feat(GmsCore support): switch to ReVanced GmsCore vendor
This commit is contained in:
@ -4,7 +4,6 @@ import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.music.utils.fix.fileprovider.fingerprints.FileProviderResolverFingerprint
|
||||
@ -17,11 +16,8 @@ object FileProviderPatch : BytecodePatch(
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
val youtubePackageName = PackageNamePatch.PackageNameYouTube
|
||||
?: throw PatchException("Invalid package name.")
|
||||
|
||||
val musicPackageName = PackageNamePatch.PackageNameYouTubeMusic
|
||||
?: throw PatchException("Invalid package name.")
|
||||
val youtubePackageName = PackageNamePatch.packageNameYouTube
|
||||
val musicPackageName = PackageNamePatch.packageNameYouTubeMusic
|
||||
|
||||
/**
|
||||
* For some reason, if the app gets "android.support.FILE_PROVIDER_PATHS",
|
||||
|
@ -0,0 +1,35 @@
|
||||
package app.revanced.patches.music.utils.gms
|
||||
|
||||
import app.revanced.patches.music.utils.fix.clientspoof.ClientSpoofPatch
|
||||
import app.revanced.patches.music.utils.fix.fileprovider.FileProviderPatch
|
||||
import app.revanced.patches.music.utils.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.music.utils.mainactivity.fingerprints.MainActivityFingerprint
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch.ORIGINAL_PACKAGE_NAME_YOUTUBE
|
||||
|
||||
@Suppress("unused")
|
||||
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
fromPackageName = ORIGINAL_PACKAGE_NAME_YOUTUBE,
|
||||
mainActivityOnCreateFingerprint = MainActivityFingerprint,
|
||||
integrationsPatchDependency = IntegrationsPatch::class,
|
||||
dependencies = setOf(ClientSpoofPatch::class, PackageNamePatch::class, FileProviderPatch::class),
|
||||
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
||||
compatiblePackages = setOf(
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
setOf(
|
||||
"6.21.52",
|
||||
"6.22.52",
|
||||
"6.23.56",
|
||||
"6.25.53",
|
||||
"6.26.51",
|
||||
"6.27.54",
|
||||
"6.28.53",
|
||||
"6.29.58",
|
||||
"6.31.55",
|
||||
"6.33.52"
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
@ -0,0 +1,32 @@
|
||||
package app.revanced.patches.music.utils.gms
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patches.music.utils.settings.CategoryType
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.addMicroGPreference
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePackageName
|
||||
import app.revanced.patches.music.utils.settings.SettingsPatch
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportResourcePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch.ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC
|
||||
|
||||
object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
|
||||
fromPackageName = ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC,
|
||||
spoofedPackageSignature = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875",
|
||||
dependencies = setOf(PackageNamePatch::class, SettingsPatch::class),
|
||||
) {
|
||||
private const val GMS_CORE_PACKAGE_NAME = "app.revanced.android.gms"
|
||||
private const val GMS_CORE_SETTINGS_ACTIVITY = "org.microg.gms.ui.SettingsActivity"
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
super.execute(context)
|
||||
|
||||
context.updatePackageName(PackageNamePatch.packageNameYouTubeMusic)
|
||||
|
||||
context.addMicroGPreference(
|
||||
CategoryType.MISC.value,
|
||||
"gms_core_settings",
|
||||
GMS_CORE_PACKAGE_NAME,
|
||||
GMS_CORE_SETTINGS_ACTIVITY
|
||||
)
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ object MainActivityResolvePatch : BytecodePatch(
|
||||
onCreateMethod.apply {
|
||||
addInstruction(
|
||||
2,
|
||||
"invoke-static/range {p0 .. p0}, $UTILS_PATH/$methods;->$descriptor(Landroid/content/Context;)V"
|
||||
"invoke-static/range {p0 .. p0}, $UTILS_PATH/$methods;->$descriptor(Landroid/app/Activity;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
package app.revanced.patches.music.utils.microg
|
||||
|
||||
object Constants {
|
||||
internal const val YOUTUBE_PACKAGE_NAME = "com.google.android.youtube"
|
||||
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
|
||||
internal const val SPOOFED_PACKAGE_NAME = MUSIC_PACKAGE_NAME
|
||||
internal const val SPOOFED_PACKAGE_SIGNATURE = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
package app.revanced.patches.music.utils.microg
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.utils.fix.clientspoof.ClientSpoofPatch
|
||||
import app.revanced.patches.music.utils.fix.fileprovider.FileProviderPatch
|
||||
import app.revanced.patches.music.utils.mainactivity.MainActivityResolvePatch
|
||||
import app.revanced.patches.music.utils.mainactivity.MainActivityResolvePatch.injectInit
|
||||
import app.revanced.patches.music.utils.microg.Constants.MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.utils.microg.Constants.YOUTUBE_PACKAGE_NAME
|
||||
import app.revanced.patches.music.utils.microg.fingerprints.CastContextFetchFingerprint
|
||||
import app.revanced.patches.music.utils.microg.fingerprints.CastDynamiteModuleFingerprint
|
||||
import app.revanced.patches.music.utils.microg.fingerprints.CastDynamiteModuleV2Fingerprint
|
||||
import app.revanced.patches.music.utils.microg.fingerprints.GooglePlayUtilityFingerprint
|
||||
import app.revanced.patches.music.utils.microg.fingerprints.PrimeFingerprint
|
||||
import app.revanced.patches.music.utils.microg.fingerprints.ServiceCheckFingerprint
|
||||
import app.revanced.patches.shared.microg.MicroGBytecodeHelper
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
|
||||
@Patch(
|
||||
name = "MicroG support",
|
||||
description = "Allows YouTube Music to run without root and under a different package name with MicroG.",
|
||||
dependencies = [
|
||||
ClientSpoofPatch::class,
|
||||
MainActivityResolvePatch::class,
|
||||
MicroGResourcePatch::class,
|
||||
PackageNamePatch::class,
|
||||
FileProviderPatch::class
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.apps.youtube.music",
|
||||
[
|
||||
"6.21.52",
|
||||
"6.22.52",
|
||||
"6.23.56",
|
||||
"6.25.53",
|
||||
"6.26.51",
|
||||
"6.27.54",
|
||||
"6.28.53",
|
||||
"6.29.58",
|
||||
"6.31.55",
|
||||
"6.33.52"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object MicroGPatch : BytecodePatch(
|
||||
setOf(
|
||||
CastContextFetchFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
PrimeFingerprint,
|
||||
ServiceCheckFingerprint
|
||||
)
|
||||
) {
|
||||
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
|
||||
// - "com.google.android.gms.chimera.GmsIntentOperationService",
|
||||
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
|
||||
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
|
||||
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
|
||||
// - "com.google.android.gms.phenotype.UPDATE",
|
||||
// - "com.google.android.gms.phenotype",
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val youtubePackageName = PackageNamePatch.PackageNameYouTube
|
||||
?: throw PatchException("Invalid package name.")
|
||||
|
||||
val musicPackageName = PackageNamePatch.PackageNameYouTubeMusic
|
||||
?: throw PatchException("Invalid package name.")
|
||||
|
||||
if (youtubePackageName == YOUTUBE_PACKAGE_NAME || musicPackageName == MUSIC_PACKAGE_NAME)
|
||||
throw PatchException("Original package name is not available as package name for MicroG build.")
|
||||
|
||||
// apply common microG patch
|
||||
MicroGBytecodeHelper.patchBytecode(
|
||||
context,
|
||||
arrayOf(
|
||||
MicroGBytecodeHelper.packageNameTransform(
|
||||
YOUTUBE_PACKAGE_NAME,
|
||||
youtubePackageName
|
||||
)
|
||||
),
|
||||
MicroGBytecodeHelper.PrimeMethodTransformationData(
|
||||
PrimeFingerprint,
|
||||
MUSIC_PACKAGE_NAME,
|
||||
musicPackageName
|
||||
),
|
||||
listOf(
|
||||
ServiceCheckFingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
CastContextFetchFingerprint
|
||||
)
|
||||
)
|
||||
|
||||
injectInit("MicroGPatch", "checkAvailability")
|
||||
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package app.revanced.patches.music.utils.microg
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.music.utils.microg.Constants.MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.utils.microg.Constants.SPOOFED_PACKAGE_NAME
|
||||
import app.revanced.patches.music.utils.microg.Constants.SPOOFED_PACKAGE_SIGNATURE
|
||||
import app.revanced.patches.music.utils.settings.CategoryType
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.addMicroGPreference
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.setMicroG
|
||||
import app.revanced.patches.music.utils.settings.SettingsPatch
|
||||
import app.revanced.patches.shared.microg.Constants.MICROG_PACKAGE_NAME
|
||||
import app.revanced.patches.shared.microg.MicroGManifestHelper.addSpoofingMetadata
|
||||
import app.revanced.patches.shared.microg.MicroGResourceHelper.patchManifest
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
|
||||
@Patch(
|
||||
dependencies = [
|
||||
PackageNamePatch::class,
|
||||
SettingsPatch::class
|
||||
]
|
||||
)
|
||||
object MicroGResourcePatch : ResourcePatch() {
|
||||
private const val MICROG_TARGET_CLASS = "org.microg.gms.ui.SettingsActivity"
|
||||
override fun execute(context: ResourceContext) {
|
||||
val packageName = PackageNamePatch.PackageNameYouTubeMusic
|
||||
?: throw PatchException("Invalid package name.")
|
||||
|
||||
if (packageName == MUSIC_PACKAGE_NAME)
|
||||
throw PatchException("Original package name is not available as package name for MicroG build.")
|
||||
|
||||
// update manifest
|
||||
context.patchManifest(
|
||||
MUSIC_PACKAGE_NAME,
|
||||
packageName
|
||||
)
|
||||
|
||||
// add metadata to the manifest
|
||||
context.addSpoofingMetadata(
|
||||
SPOOFED_PACKAGE_NAME,
|
||||
SPOOFED_PACKAGE_SIGNATURE
|
||||
)
|
||||
|
||||
context.setMicroG(packageName)
|
||||
|
||||
context.addMicroGPreference(
|
||||
CategoryType.MISC.value,
|
||||
"microg_settings",
|
||||
MICROG_PACKAGE_NAME,
|
||||
MICROG_TARGET_CLASS
|
||||
)
|
||||
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package app.revanced.patches.music.utils.microg.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object GooglePlayUtilityFingerprint : MethodFingerprint(
|
||||
returnType = "I",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
parameters = listOf("L", "I"),
|
||||
strings = listOf(
|
||||
"This should never happen.",
|
||||
"MetadataValueReader",
|
||||
"GooglePlayServicesUtil",
|
||||
"com.android.vending",
|
||||
"android.hardware.type.embedded"
|
||||
)
|
||||
)
|
@ -7,7 +7,7 @@ import app.revanced.util.doRecursively
|
||||
import app.revanced.util.insertNode
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@Suppress("DEPRECATION", "MemberVisibilityCanBePrivate")
|
||||
object ResourceUtils {
|
||||
|
||||
const val YOUTUBE_MUSIC_SETTINGS_PATH = "res/xml/settings_headers.xml"
|
||||
@ -25,10 +25,10 @@ object ResourceUtils {
|
||||
const val YOUTUBE_MUSIC_PREFERENCE_TARGET_CLASS =
|
||||
"com.google.android.gms.common.api.GoogleApiActivity"
|
||||
|
||||
var targetPackage = "com.google.android.apps.youtube.music"
|
||||
var musicPackageName = "com.google.android.apps.youtube.music"
|
||||
|
||||
fun ResourceContext.setMicroG(newPackage: String) {
|
||||
targetPackage = newPackage
|
||||
fun ResourceContext.updatePackageName(newPackage: String) {
|
||||
musicPackageName = newPackage
|
||||
replacePackageName()
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ object ResourceUtils {
|
||||
fun ResourceContext.replacePackageName() {
|
||||
this[YOUTUBE_MUSIC_SETTINGS_PATH].writeText(
|
||||
this[YOUTUBE_MUSIC_SETTINGS_PATH].readText()
|
||||
.replace("\"com.google.android.apps.youtube.music\"", "\"" + targetPackage + "\"")
|
||||
.replace("\"com.google.android.apps.youtube.music\"", "\"" + musicPackageName + "\"")
|
||||
)
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ object ResourceUtils {
|
||||
setAttribute("android:dependency", dependencyKey)
|
||||
}
|
||||
this.adoptChild("intent") {
|
||||
setAttribute("android:targetPackage", targetPackage)
|
||||
setAttribute("android:targetPackage", musicPackageName)
|
||||
setAttribute("android:data", key)
|
||||
setAttribute(
|
||||
"android:targetClass",
|
||||
|
@ -73,7 +73,7 @@ object SponsorBlockPatch : ResourcePatch() {
|
||||
context["res/xml/sponsorblock_prefs.xml"].readText()
|
||||
.replace(
|
||||
"\"com.google.android.apps.youtube.music\"",
|
||||
"\"" + ResourceUtils.targetPackage + "\""
|
||||
"\"" + ResourceUtils.musicPackageName + "\""
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,341 @@
|
||||
package app.revanced.patches.shared.gms
|
||||
|
||||
import app.revanced.patcher.PatchClass
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patches.shared.gms.fingerprints.*
|
||||
import app.revanced.patches.shared.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch.Constants.ACTIONS
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch.Constants.AUTHORITIES
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch.Constants.PERMISSIONS
|
||||
import app.revanced.patches.shared.integrations.Constants.INTEGRATIONS_PATH
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.util.getReference
|
||||
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.StringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
/**
|
||||
* A patch that allows patched Google apps to run without root and under a different package name
|
||||
* by using GmsCore instead of Google Play Services.
|
||||
*
|
||||
* @param fromPackageName The package name of the original app.
|
||||
* @param mainActivityOnCreateFingerprint The fingerprint of the main activity onCreate method.
|
||||
* @param integrationsPatchDependency The patch responsible for the integrations.
|
||||
* @param gmsCoreSupportResourcePatch The corresponding resource patch that is used to patch the resources.
|
||||
* @param dependencies Additional dependencies of this patch.
|
||||
* @param compatiblePackages The compatible packages of this patch.
|
||||
*/
|
||||
abstract class BaseGmsCoreSupportPatch(
|
||||
private val fromPackageName: String,
|
||||
private val mainActivityOnCreateFingerprint: MethodFingerprint,
|
||||
private val integrationsPatchDependency: PatchClass,
|
||||
gmsCoreSupportResourcePatch: BaseGmsCoreSupportResourcePatch,
|
||||
dependencies: Set<PatchClass> = setOf(),
|
||||
compatiblePackages: Set<CompatiblePackage>? = null,
|
||||
) : BytecodePatch(
|
||||
name = "GmsCore support",
|
||||
description = "Allows patched Google apps to run without root and under a different package name " +
|
||||
"by using GmsCore instead of Google Play Services.",
|
||||
dependencies = setOf(
|
||||
PackageNamePatch::class,
|
||||
gmsCoreSupportResourcePatch::class,
|
||||
integrationsPatchDependency,
|
||||
) + dependencies,
|
||||
compatiblePackages = compatiblePackages,
|
||||
fingerprints = setOf(
|
||||
CastContextFetchFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
GmsCoreSupportFingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
PrimeMethodFingerprint,
|
||||
ServiceCheckFingerprint,
|
||||
mainActivityOnCreateFingerprint
|
||||
),
|
||||
requiresIntegrations = true,
|
||||
) {
|
||||
private val gmsCoreVendorGroupId = "app.revanced"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
val packageName = PackageNamePatch.getPackageName(fromPackageName)
|
||||
|
||||
// Transform all strings using all provided transforms, first match wins.
|
||||
val transformations = arrayOf(
|
||||
::commonTransform,
|
||||
::contentUrisTransform,
|
||||
packageNameTransform(fromPackageName, packageName),
|
||||
)
|
||||
|
||||
context.transformStringReferences transform@{ string ->
|
||||
transformations.forEach { transform ->
|
||||
transform(string)?.let { transformedString -> return@transform transformedString }
|
||||
}
|
||||
|
||||
return@transform null
|
||||
}
|
||||
|
||||
// Specific method that needs to be patched.
|
||||
transformPrimeMethod(packageName)
|
||||
|
||||
// Return these methods early to prevent the app from crashing.
|
||||
listOf(
|
||||
CastContextFetchFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
ServiceCheckFingerprint
|
||||
).returnEarly()
|
||||
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.result?.mutableMethod?.addInstructions(
|
||||
1, // Hack to not disturb other patches (such as the YTMusic integrations patch).
|
||||
"invoke-static/range { p0 .. p0 }, $INTEGRATIONS_PATH/patches/GmsCoreSupport;->" +
|
||||
"checkGmsCore(Landroid/content/Context;)V",
|
||||
) ?: throw mainActivityOnCreateFingerprint.exception
|
||||
|
||||
// Change the vendor of GmsCore in ReVanced Integrations.
|
||||
GmsCoreSupportFingerprint.result?.mutableClass?.methods
|
||||
?.single { it.name == GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME }
|
||||
?.replaceInstruction(0, "const-string v0, \"$gmsCoreVendorGroupId\"")
|
||||
?: throw GmsCoreSupportFingerprint.exception
|
||||
}
|
||||
|
||||
private fun BytecodeContext.transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
|
||||
val mutableClass by lazy {
|
||||
proxy(it).mutableClass
|
||||
}
|
||||
|
||||
it.methods.forEach classLoop@{ methodDef ->
|
||||
val implementation = methodDef.implementation ?: return@classLoop
|
||||
|
||||
val mutableMethod by lazy {
|
||||
mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, methodDef) }
|
||||
}
|
||||
|
||||
implementation.instructions.forEachIndexed insnLoop@{ index, instruction ->
|
||||
val string = ((instruction as? Instruction21c)?.reference as? StringReference)?.string
|
||||
?: return@insnLoop
|
||||
|
||||
// Apply transformation.
|
||||
val transformedString = transform(string) ?: return@insnLoop
|
||||
|
||||
mutableMethod.replaceInstruction(
|
||||
index,
|
||||
BuilderInstruction21c(
|
||||
Opcode.CONST_STRING,
|
||||
instruction.registerA,
|
||||
ImmutableStringReference(transformedString),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Collection of transformations that are applied to all strings.
|
||||
|
||||
private fun commonTransform(referencedString: String): String? =
|
||||
when (referencedString) {
|
||||
"com.google",
|
||||
"com.google.android.gms",
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES,
|
||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId)
|
||||
|
||||
// No vendor prefix for whatever reason...
|
||||
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun contentUrisTransform(str: String): String? {
|
||||
// only when content:// uri
|
||||
if (str.startsWith("content://")) {
|
||||
// check if matches any authority
|
||||
for (authority in AUTHORITIES) {
|
||||
val uriPrefix = "content://$authority"
|
||||
if (str.startsWith(uriPrefix)) {
|
||||
return str.replace(
|
||||
uriPrefix,
|
||||
"content://${authority.replace("com.google", gmsCoreVendorGroupId)}",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// gms also has a 'subscribedfeeds' authority, check for that one too
|
||||
val subFeedsUriPrefix = "content://subscribedfeeds"
|
||||
if (str.startsWith(subFeedsUriPrefix)) {
|
||||
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendorGroupId.subscribedfeeds")
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? = { string ->
|
||||
when (string) {
|
||||
"$fromPackageName.SuggestionsProvider",
|
||||
"$fromPackageName.fileprovider",
|
||||
-> string.replace(fromPackageName, toPackageName)
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformPrimeMethod(packageName: String) {
|
||||
PrimeMethodFingerprint.result?.mutableMethod?.apply {
|
||||
var register = 2
|
||||
|
||||
val index = getInstructions().indexOfFirst {
|
||||
if (it.getReference<StringReference>()?.string != fromPackageName) return@indexOfFirst false
|
||||
|
||||
register = (it as OneRegisterInstruction).registerA
|
||||
return@indexOfFirst true
|
||||
}
|
||||
|
||||
replaceInstruction(index, "const-string v$register, \"$packageName\"")
|
||||
} ?: throw PrimeMethodFingerprint.exception
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of permissions, intents and content provider authorities
|
||||
* that are present in GmsCore which need to be transformed.
|
||||
*
|
||||
* NOTE: The following were present, but it seems like they are not needed to be transformed:
|
||||
* - com.google.android.gms.chimera.GmsIntentOperationService
|
||||
* - com.google.android.gms.phenotype.internal.IPhenotypeCallbacks
|
||||
* - com.google.android.gms.phenotype.internal.IPhenotypeService
|
||||
* - com.google.android.gms.phenotype.PACKAGE_NAME
|
||||
* - com.google.android.gms.phenotype.UPDATE
|
||||
* - com.google.android.gms.phenotype
|
||||
*/
|
||||
private object Constants {
|
||||
/**
|
||||
* A list of all permissions.
|
||||
*/
|
||||
val PERMISSIONS = listOf(
|
||||
// C2DM / GCM
|
||||
"com.google.android.c2dm.permission.RECEIVE",
|
||||
"com.google.android.c2dm.permission.SEND",
|
||||
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
|
||||
|
||||
// GAuth
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
|
||||
)
|
||||
|
||||
/**
|
||||
* All intent actions.
|
||||
*/
|
||||
val ACTIONS = listOf(
|
||||
// location
|
||||
"com.google.android.gms.location.places.ui.PICK_PLACE",
|
||||
"com.google.android.gms.location.places.GeoDataApi",
|
||||
"com.google.android.gms.location.places.PlacesApi",
|
||||
"com.google.android.gms.location.places.PlaceDetectionApi",
|
||||
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
|
||||
|
||||
// C2DM / GCM
|
||||
"com.google.android.c2dm.intent.REGISTER",
|
||||
"com.google.android.c2dm.intent.REGISTRATION",
|
||||
"com.google.android.c2dm.intent.UNREGISTER",
|
||||
"com.google.android.c2dm.intent.RECEIVE",
|
||||
"com.google.iid.TOKEN_REQUEST",
|
||||
"com.google.android.gcm.intent.SEND",
|
||||
|
||||
// car
|
||||
"com.google.android.gms.car.service.START",
|
||||
|
||||
// people
|
||||
"com.google.android.gms.people.service.START",
|
||||
|
||||
// wearable
|
||||
"com.google.android.gms.wearable.BIND",
|
||||
|
||||
// auth
|
||||
"com.google.android.gsf.login",
|
||||
"com.google.android.gsf.action.GET_GLS",
|
||||
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
|
||||
"com.google.android.gms.auth.login.LOGIN",
|
||||
"com.google.android.gms.auth.api.credentials.PICKER",
|
||||
"com.google.android.gms.auth.api.credentials.service.START",
|
||||
"com.google.android.gms.auth.service.START",
|
||||
"com.google.firebase.auth.api.gms.service.START",
|
||||
"com.google.android.gms.auth.be.appcert.AppCertService",
|
||||
|
||||
// fido
|
||||
"com.google.android.gms.fido.fido2.privileged.START",
|
||||
|
||||
// gass
|
||||
"com.google.android.gms.gass.START",
|
||||
|
||||
// games
|
||||
"com.google.android.gms.games.service.START",
|
||||
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
|
||||
|
||||
// chimera
|
||||
"com.google.android.gms.chimera",
|
||||
|
||||
// fonts
|
||||
"com.google.android.gms.fonts",
|
||||
|
||||
// phenotype
|
||||
"com.google.android.gms.phenotype.service.START",
|
||||
|
||||
// location
|
||||
"com.google.android.gms.location.reporting.service.START",
|
||||
|
||||
// misc
|
||||
"com.google.android.gms.gmscompliance.service.START",
|
||||
"com.google.android.gms.oss.licenses.service.START",
|
||||
"com.google.android.gms.tapandpay.service.BIND",
|
||||
"com.google.android.gms.measurement.START",
|
||||
"com.google.android.gms.languageprofile.service.START",
|
||||
"com.google.android.gms.clearcut.service.START",
|
||||
"com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE",
|
||||
|
||||
// potoken
|
||||
"com.google.android.gms.potokens.service.START",
|
||||
|
||||
// droidguard/ safetynet
|
||||
"com.google.android.gms.droidguard.service.START",
|
||||
"com.google.android.gms.safetynet.service.START",
|
||||
)
|
||||
|
||||
/**
|
||||
* All content provider authorities.
|
||||
*/
|
||||
val AUTHORITIES = listOf(
|
||||
// gsf
|
||||
"com.google.android.gsf.gservices",
|
||||
"com.google.settings",
|
||||
|
||||
// auth
|
||||
"com.google.android.gms.auth.accounts",
|
||||
|
||||
// chimera
|
||||
"com.google.android.gms.chimera",
|
||||
|
||||
// fonts
|
||||
"com.google.android.gms.fonts",
|
||||
|
||||
// phenotype
|
||||
"com.google.android.gms.phenotype",
|
||||
)
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package app.revanced.patches.shared.gms
|
||||
|
||||
import app.revanced.patcher.PatchClass
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
|
||||
/**
|
||||
* Abstract resource patch that allows Google apps to run without root and under a different package name
|
||||
* by using GmsCore instead of Google Play Services.
|
||||
*
|
||||
* @param fromPackageName The package name of the original app.
|
||||
* @param spoofedPackageSignature The signature of the package to spoof to.
|
||||
* @param dependencies Additional dependencies of this patch.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
abstract class BaseGmsCoreSupportResourcePatch(
|
||||
private val fromPackageName: String,
|
||||
private val spoofedPackageSignature: String,
|
||||
dependencies: Set<PatchClass> = setOf(),
|
||||
) : ResourcePatch(
|
||||
dependencies = setOf(PackageNamePatch::class) + dependencies
|
||||
) {
|
||||
private val gmsCoreVendorGroupId = "app.revanced"
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
context.patchManifest()
|
||||
context.addSpoofingMetadata()
|
||||
}
|
||||
|
||||
/**
|
||||
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
||||
*/
|
||||
private fun ResourceContext.addSpoofingMetadata() {
|
||||
fun Node.adoptChild(
|
||||
tagName: String,
|
||||
block: Element.() -> Unit,
|
||||
) {
|
||||
val child = ownerDocument.createElement(tagName)
|
||||
child.block()
|
||||
appendChild(child)
|
||||
}
|
||||
|
||||
xmlEditor["AndroidManifest.xml"].use { editor ->
|
||||
val document = editor.file
|
||||
|
||||
val applicationNode =
|
||||
document
|
||||
.getElementsByTagName("application")
|
||||
.item(0)
|
||||
|
||||
// Spoof package name and signature.
|
||||
applicationNode.adoptChild("meta-data") {
|
||||
setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_NAME")
|
||||
setAttribute("android:value", fromPackageName)
|
||||
}
|
||||
|
||||
applicationNode.adoptChild("meta-data") {
|
||||
setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_SIGNATURE")
|
||||
setAttribute("android:value", spoofedPackageSignature)
|
||||
}
|
||||
|
||||
// GmsCore presence detection in ReVanced Integrations.
|
||||
applicationNode.adoptChild("meta-data") {
|
||||
// TODO: The name of this metadata should be dynamic.
|
||||
setAttribute("android:name", "app.revanced.MICROG_PACKAGE_NAME")
|
||||
setAttribute("android:value", "$gmsCoreVendorGroupId.android.gms")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the manifest to support GmsCore.
|
||||
*/
|
||||
private fun ResourceContext.patchManifest() {
|
||||
val packageName = PackageNamePatch.getPackageName(fromPackageName)
|
||||
|
||||
val manifest = this["AndroidManifest.xml"].readText()
|
||||
this["AndroidManifest.xml"].writeText(
|
||||
manifest.replace(
|
||||
"package=\"$fromPackageName",
|
||||
"package=\"$packageName",
|
||||
).replace(
|
||||
"android:authorities=\"$fromPackageName",
|
||||
"android:authorities=\"$packageName",
|
||||
).replace(
|
||||
"$fromPackageName.permission.C2D_MESSAGE",
|
||||
"$packageName.permission.C2D_MESSAGE",
|
||||
).replace(
|
||||
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||
"$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||
).replace(
|
||||
"com.google.android.c2dm",
|
||||
"$gmsCoreVendorGroupId.android.c2dm",
|
||||
).replace(
|
||||
"</queries>",
|
||||
"<package android:name=\"$gmsCoreVendorGroupId.android.gms\"/></queries>",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.music.utils.microg.fingerprints
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.music.utils.microg.fingerprints
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.music.utils.microg.fingerprints
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
@ -0,0 +1,11 @@
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object GmsCoreSupportFingerprint : MethodFingerprint(
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type.endsWith("GmsCoreSupport;")
|
||||
},
|
||||
) {
|
||||
const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.youtube.utils.microg.fingerprints
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
@ -10,7 +10,6 @@ object GooglePlayUtilityFingerprint : MethodFingerprint(
|
||||
parameters = listOf("L", "I"),
|
||||
strings = listOf(
|
||||
"This should never happen.",
|
||||
"MetadataValueReader",
|
||||
"com.google.android.gms"
|
||||
"MetadataValueReader"
|
||||
)
|
||||
)
|
@ -1,7 +1,7 @@
|
||||
package app.revanced.patches.music.utils.microg.fingerprints
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
object PrimeFingerprint : MethodFingerprint(
|
||||
object PrimeMethodFingerprint : MethodFingerprint(
|
||||
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patches.music.utils.microg.fingerprints
|
||||
package app.revanced.patches.shared.gms.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
@ -1,136 +0,0 @@
|
||||
package app.revanced.patches.shared.microg
|
||||
|
||||
/**
|
||||
* constants for microG builds with signature spoofing
|
||||
*/
|
||||
object Constants {
|
||||
/**
|
||||
* microG vendor name
|
||||
* aka. package prefix / package base
|
||||
*/
|
||||
const val MICROG_VENDOR = "com.mgoogle"
|
||||
|
||||
/**
|
||||
* microG package name
|
||||
*/
|
||||
const val MICROG_PACKAGE_NAME = "$MICROG_VENDOR.android.gms"
|
||||
|
||||
/**
|
||||
* meta-data for microG package name spoofing on patched builds
|
||||
*/
|
||||
const val META_SPOOFED_PACKAGE_NAME = "$MICROG_PACKAGE_NAME.SPOOFED_PACKAGE_NAME"
|
||||
|
||||
/**
|
||||
* meta-data for microG package signature spoofing on patched builds
|
||||
*/
|
||||
const val META_SPOOFED_PACKAGE_SIGNATURE =
|
||||
"$MICROG_PACKAGE_NAME.SPOOFED_PACKAGE_SIGNATURE"
|
||||
|
||||
/**
|
||||
* meta-data for microG package detection
|
||||
*/
|
||||
const val META_GMS_PACKAGE_NAME = "app.revanced.MICROG_PACKAGE_NAME"
|
||||
|
||||
/**
|
||||
* a list of all permissions in microG
|
||||
*/
|
||||
val PERMISSIONS = listOf(
|
||||
// C2DM / GCM
|
||||
"com.google.android.c2dm.permission.RECEIVE",
|
||||
"com.google.android.c2dm.permission.SEND",
|
||||
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
|
||||
|
||||
// GAuth
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
|
||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
|
||||
)
|
||||
|
||||
/**
|
||||
* a list of all (intent) actions in microG
|
||||
*/
|
||||
val ACTIONS = listOf(
|
||||
// location
|
||||
"com.google.android.gms.location.places.ui.PICK_PLACE",
|
||||
"com.google.android.gms.location.places.GeoDataApi",
|
||||
"com.google.android.gms.location.places.PlacesApi",
|
||||
"com.google.android.gms.location.places.PlaceDetectionApi",
|
||||
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
|
||||
|
||||
// C2DM / GCM
|
||||
"com.google.android.c2dm.intent.REGISTER",
|
||||
"com.google.android.c2dm.intent.REGISTRATION",
|
||||
"com.google.android.c2dm.intent.UNREGISTER",
|
||||
"com.google.android.c2dm.intent.RECEIVE",
|
||||
"com.google.iid.TOKEN_REQUEST",
|
||||
"com.google.android.gcm.intent.SEND",
|
||||
|
||||
// car
|
||||
"com.google.android.gms.car.service.START",
|
||||
|
||||
// people
|
||||
"com.google.android.gms.people.service.START",
|
||||
|
||||
// wearable
|
||||
"com.google.android.gms.wearable.BIND",
|
||||
|
||||
// auth
|
||||
"com.google.android.gsf.login",
|
||||
"com.google.android.gsf.action.GET_GLS",
|
||||
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
|
||||
"com.google.android.gms.auth.login.LOGIN",
|
||||
"com.google.android.gms.auth.api.credentials.PICKER",
|
||||
"com.google.android.gms.auth.api.credentials.service.START",
|
||||
"com.google.android.gms.auth.service.START",
|
||||
"com.google.firebase.auth.api.gms.service.START",
|
||||
"com.google.android.gms.auth.be.appcert.AppCertService",
|
||||
|
||||
// fido
|
||||
"com.google.android.gms.fido.fido2.privileged.START",
|
||||
|
||||
// games
|
||||
"com.google.android.gms.games.service.START",
|
||||
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
|
||||
|
||||
// chimera
|
||||
"com.google.android.gms.chimera",
|
||||
|
||||
// fonts
|
||||
"com.google.android.gms.fonts",
|
||||
|
||||
// phenotype
|
||||
"com.google.android.gms.phenotype.service.START",
|
||||
|
||||
// location
|
||||
"com.google.android.gms.location.reporting.service.START",
|
||||
|
||||
// misc
|
||||
"com.google.android.gms.gmscompliance.service.START",
|
||||
"com.google.android.gms.oss.licenses.service.START",
|
||||
"com.google.android.gms.safetynet.service.START",
|
||||
"com.google.android.gms.tapandpay.service.BIND"
|
||||
)
|
||||
|
||||
/**
|
||||
* a list of all content provider authorities in microG
|
||||
*/
|
||||
val AUTHORITIES = listOf(
|
||||
// gsf
|
||||
"com.google.android.gsf.gservices",
|
||||
"com.google.settings",
|
||||
|
||||
// auth
|
||||
"com.google.android.gms.auth.accounts",
|
||||
|
||||
// chimera
|
||||
"com.google.android.gms.chimera",
|
||||
|
||||
// fonts
|
||||
"com.google.android.gms.fonts",
|
||||
|
||||
// phenotype
|
||||
"com.google.android.gms.phenotype"
|
||||
)
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
package app.revanced.patches.shared.microg
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.shared.microg.Constants.ACTIONS
|
||||
import app.revanced.patches.shared.microg.Constants.AUTHORITIES
|
||||
import app.revanced.patches.shared.microg.Constants.MICROG_VENDOR
|
||||
import app.revanced.patches.shared.microg.Constants.PERMISSIONS
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||
|
||||
/**
|
||||
* Helper class for applying bytecode patches needed for the microg-support patches.
|
||||
*/
|
||||
object MicroGBytecodeHelper {
|
||||
|
||||
/**
|
||||
* Transform strings with package name out of [fromPackageName] and [toPackageName].
|
||||
*
|
||||
* @param fromPackageName Original package name.
|
||||
* @param toPackageName The package name to accept.
|
||||
**/
|
||||
fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? {
|
||||
return { referencedString ->
|
||||
when (referencedString) {
|
||||
"$fromPackageName.SuggestionsProvider",
|
||||
"$fromPackageName.fileprovider" -> referencedString.replace(
|
||||
fromPackageName,
|
||||
toPackageName
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prime method data class for the [MicroGBytecodeHelper] class.
|
||||
*
|
||||
* @param primeMethodFingerprint The prime methods [MethodFingerprint].
|
||||
* @param fromPackageName Original package name.
|
||||
* @param toPackageName The package name to accept.
|
||||
**/
|
||||
data class PrimeMethodTransformationData(
|
||||
val primeMethodFingerprint: MethodFingerprint,
|
||||
val fromPackageName: String,
|
||||
val toPackageName: String
|
||||
) {
|
||||
/**
|
||||
* Patch the prime method to accept the new package name.
|
||||
*/
|
||||
fun transformPrimeMethodPackageName() {
|
||||
primeMethodFingerprint.result?.let {
|
||||
var targetRegister = 2
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val targetIndex = implementation!!.instructions.indexOfFirst { instructions ->
|
||||
if (instructions.opcode != Opcode.CONST_STRING) return@indexOfFirst false
|
||||
|
||||
val instructionString =
|
||||
((instructions as Instruction21c).reference as StringReference).string
|
||||
if (instructionString != fromPackageName) return@indexOfFirst false
|
||||
|
||||
targetRegister = instructions.registerA
|
||||
return@indexOfFirst true
|
||||
}
|
||||
|
||||
replaceInstruction(
|
||||
targetIndex,
|
||||
"const-string v$targetRegister, \"$toPackageName\""
|
||||
)
|
||||
}
|
||||
} ?: throw primeMethodFingerprint.exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the bytecode to work with MicroG.
|
||||
* Note: this only handles string constants to gms (intent actions, authorities, ...).
|
||||
* If the app employs additional checks to validate the installed gms package, you'll have to handle those in the app- specific patch
|
||||
*
|
||||
* @param context The context.
|
||||
* @param additionalStringTransforms Additional transformations applied to all const-string references.
|
||||
* @param primeMethodTransformationData Data to patch the prime method.
|
||||
* @param earlyReturns List of [MethodFingerprint] to return the resolved methods early.
|
||||
*/
|
||||
fun patchBytecode(
|
||||
context: BytecodeContext,
|
||||
additionalStringTransforms: Array<(str: String) -> String?>,
|
||||
primeMethodTransformationData: PrimeMethodTransformationData,
|
||||
earlyReturns: List<MethodFingerprint>
|
||||
) {
|
||||
earlyReturns.returnEarly()
|
||||
primeMethodTransformationData.transformPrimeMethodPackageName()
|
||||
|
||||
val allTransforms = arrayOf(
|
||||
MicroGBytecodeHelper::commonTransform,
|
||||
MicroGBytecodeHelper::contentUrisTransform,
|
||||
*additionalStringTransforms
|
||||
)
|
||||
|
||||
// transform all strings using all provided transforms, first match wins
|
||||
context.transformStringReferences transform@{
|
||||
for (transformFn in allTransforms) {
|
||||
val s = transformFn(it)
|
||||
if (s != null) return@transform s
|
||||
}
|
||||
|
||||
return@transform null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* const-string transform function for common gms string references.
|
||||
*
|
||||
* @param referencedString The string to transform.
|
||||
*/
|
||||
private fun commonTransform(referencedString: String): String? =
|
||||
when (referencedString) {
|
||||
"com.google",
|
||||
"com.google.android.gms",
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES -> referencedString.replace("com.google", MICROG_VENDOR)
|
||||
|
||||
// subscribedfeeds has no vendor prefix for whatever reason...
|
||||
"subscribedfeeds" -> "${MICROG_VENDOR}.subscribedfeeds"
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* const-string transform function for strings containing gms content uris / authorities.
|
||||
*/
|
||||
private fun contentUrisTransform(str: String): String? {
|
||||
// only when content:// uri
|
||||
if (str.startsWith("content://")) {
|
||||
// check if matches any authority
|
||||
for (authority in AUTHORITIES) {
|
||||
val uriPrefix = "content://$authority"
|
||||
if (str.startsWith(uriPrefix)) {
|
||||
return str.replace(
|
||||
uriPrefix,
|
||||
"content://${authority.replace("com.google", MICROG_VENDOR)}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// gms also has a 'subscribedfeeds' authority, check for that one too
|
||||
val subFeedsUriPrefix = "content://subscribedfeeds"
|
||||
if (str.startsWith(subFeedsUriPrefix)) {
|
||||
return str.replace(subFeedsUriPrefix, "content://${MICROG_VENDOR}.subscribedfeeds")
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform all constant string references using a transformation function.
|
||||
*
|
||||
* @param transformFn string transformation function. if null, string is not changed.
|
||||
*/
|
||||
private fun BytecodeContext.transformStringReferences(transformFn: (str: String) -> String?) {
|
||||
classes.forEach { classDef ->
|
||||
var mutableClass: MutableClass? = null
|
||||
|
||||
// enumerate all methods
|
||||
classDef.methods.forEach classLoop@{ methodDef ->
|
||||
var mutableMethod: MutableMethod? = null
|
||||
val implementation = methodDef.implementation ?: return@classLoop
|
||||
|
||||
// enumerate all instructions and find const-string
|
||||
implementation.instructions.forEachIndexed implLoop@{ index, instruction ->
|
||||
// skip all that are not const-string
|
||||
if (instruction.opcode != Opcode.CONST_STRING) return@implLoop
|
||||
val str = ((instruction as Instruction21c).reference as StringReference).string
|
||||
|
||||
// call transform function
|
||||
val transformedStr = transformFn(str)
|
||||
if (transformedStr != null) {
|
||||
// make class and method mutable, if not already
|
||||
mutableClass = mutableClass ?: proxy(classDef).mutableClass
|
||||
mutableMethod = mutableMethod ?: mutableClass!!.methods.first {
|
||||
it.name == methodDef.name && it.parameterTypes.containsAll(methodDef.parameterTypes)
|
||||
}
|
||||
|
||||
// replace instruction with updated string
|
||||
mutableMethod!!.implementation!!.replaceInstruction(
|
||||
index,
|
||||
BuilderInstruction21c(
|
||||
Opcode.CONST_STRING,
|
||||
instruction.registerA,
|
||||
ImmutableStringReference(
|
||||
transformedStr
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resolved methods of a list of [MethodFingerprint] early.
|
||||
*/
|
||||
private fun List<MethodFingerprint>.returnEarly() {
|
||||
this.forEach { fingerprint ->
|
||||
fingerprint.result?.let {
|
||||
it.mutableMethod.apply {
|
||||
val stringInstructions = when (it.method.returnType.first()) {
|
||||
'L' -> """
|
||||
const/4 v0, 0x0
|
||||
return-object v0
|
||||
"""
|
||||
|
||||
'V' -> "return-void"
|
||||
'I' -> """
|
||||
const/4 v0, 0x0
|
||||
return v0
|
||||
"""
|
||||
|
||||
else -> throw Exception("This case should never happen.")
|
||||
}
|
||||
addInstructions(
|
||||
0, stringInstructions
|
||||
)
|
||||
}
|
||||
} ?: throw fingerprint.exception
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package app.revanced.patches.shared.microg
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patches.shared.microg.Constants.META_GMS_PACKAGE_NAME
|
||||
import app.revanced.patches.shared.microg.Constants.META_SPOOFED_PACKAGE_NAME
|
||||
import app.revanced.patches.shared.microg.Constants.META_SPOOFED_PACKAGE_SIGNATURE
|
||||
import app.revanced.patches.shared.microg.Constants.MICROG_VENDOR
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
|
||||
/**
|
||||
* helper class for adding manifest metadata needed for microG builds with signature spoofing
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
object MicroGManifestHelper {
|
||||
|
||||
/**
|
||||
* Add manifest entries needed for package and signature spoofing when using MicroG.
|
||||
* Note: this only adds metadata entries for signature spoofing, other changes may still be required to make a microG patch work.
|
||||
*
|
||||
* @param spoofedPackage The package to spoof.
|
||||
* @param spoofedSignature The signature to spoof.
|
||||
*/
|
||||
fun ResourceContext.addSpoofingMetadata(
|
||||
spoofedPackage: String,
|
||||
spoofedSignature: String
|
||||
) {
|
||||
this.xmlEditor["AndroidManifest.xml"].use {
|
||||
val applicationNode = it
|
||||
.file
|
||||
.getElementsByTagName("application")
|
||||
.item(0)
|
||||
|
||||
// package spoofing
|
||||
applicationNode.adoptChild("meta-data") {
|
||||
setAttribute("android:name", META_SPOOFED_PACKAGE_NAME)
|
||||
setAttribute("android:value", spoofedPackage)
|
||||
}
|
||||
applicationNode.adoptChild("meta-data") {
|
||||
setAttribute("android:name", META_SPOOFED_PACKAGE_SIGNATURE)
|
||||
setAttribute("android:value", spoofedSignature)
|
||||
}
|
||||
|
||||
// microG presence detection in integrations
|
||||
applicationNode.adoptChild("meta-data") {
|
||||
setAttribute("android:name", META_GMS_PACKAGE_NAME)
|
||||
setAttribute("android:value", "${MICROG_VENDOR}.android.gms")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
|
||||
val child = ownerDocument.createElement(tagName)
|
||||
child.block()
|
||||
appendChild(child)
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package app.revanced.patches.shared.microg
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
|
||||
/**
|
||||
* Helper class for applying resource patches needed for the microg-support patches.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
object MicroGResourceHelper {
|
||||
/**
|
||||
* Patch the manifest to work with MicroG.
|
||||
*
|
||||
* @param fromPackageName Original package name.
|
||||
* @param toPackageName The package name to accept.
|
||||
*/
|
||||
fun ResourceContext.patchManifest(
|
||||
fromPackageName: String,
|
||||
toPackageName: String
|
||||
) {
|
||||
val manifest = this["AndroidManifest.xml"]
|
||||
|
||||
manifest.writeText(
|
||||
manifest.readText()
|
||||
.replace(
|
||||
"package=\"$fromPackageName",
|
||||
"package=\"$toPackageName"
|
||||
).replace(
|
||||
"android:authorities=\"$fromPackageName",
|
||||
"android:authorities=\"$toPackageName"
|
||||
).replace(
|
||||
"$fromPackageName.permission.C2D_MESSAGE",
|
||||
"$toPackageName.permission.C2D_MESSAGE"
|
||||
).replace(
|
||||
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||
"$toPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
|
||||
).replace(
|
||||
"com.google.android.c2dm",
|
||||
"${Constants.MICROG_VENDOR}.android.c2dm"
|
||||
).replace(
|
||||
"</queries>",
|
||||
"<package android:name=\"${Constants.MICROG_VENDOR}.android.gms\"/></queries>"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the settings fragment to work with MicroG.
|
||||
*
|
||||
* @param fromPackageName Original package name.
|
||||
* @param toPackageName The package name to accept.
|
||||
*/
|
||||
fun ResourceContext.patchSetting(
|
||||
fromPackageName: String,
|
||||
toPackageName: String
|
||||
) {
|
||||
val prefs = this["res/xml/settings_fragment.xml"]
|
||||
|
||||
prefs.writeText(
|
||||
prefs.readText()
|
||||
.replace(
|
||||
"android:targetPackage=\"$fromPackageName",
|
||||
"android:targetPackage=\"$toPackageName"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package app.revanced.patches.shared.packagename
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
@ -18,11 +19,16 @@ import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatc
|
||||
object PackageNamePatch : ResourcePatch() {
|
||||
private const val CLONE_PACKAGE_NAME_YOUTUBE = "com.rvx.android.youtube"
|
||||
private const val DEFAULT_PACKAGE_NAME_YOUTUBE = "app.rvx.android.youtube"
|
||||
internal const val ORIGINAL_PACKAGE_NAME_YOUTUBE = "com.google.android.youtube"
|
||||
|
||||
private const val CLONE_PACKAGE_NAME_YOUTUBE_MUSIC = "com.rvx.android.apps.youtube.music"
|
||||
private const val DEFAULT_PACKAGE_NAME_YOUTUBE_MUSIC = "app.rvx.android.apps.youtube.music"
|
||||
internal const val ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music"
|
||||
|
||||
internal val PackageNameYouTube by stringPatchOption(
|
||||
internal var packageNameYouTube = DEFAULT_PACKAGE_NAME_YOUTUBE
|
||||
internal var packageNameYouTubeMusic = DEFAULT_PACKAGE_NAME_YOUTUBE_MUSIC
|
||||
|
||||
private val PackageNameYouTube by stringPatchOption(
|
||||
key = "PackageNameYouTube",
|
||||
default = DEFAULT_PACKAGE_NAME_YOUTUBE,
|
||||
values = mapOf(
|
||||
@ -34,7 +40,7 @@ object PackageNamePatch : ResourcePatch() {
|
||||
required = true
|
||||
)
|
||||
|
||||
internal val PackageNameYouTubeMusic by stringPatchOption(
|
||||
private val PackageNameYouTubeMusic by stringPatchOption(
|
||||
key = "PackageNameYouTubeMusic",
|
||||
default = DEFAULT_PACKAGE_NAME_YOUTUBE_MUSIC,
|
||||
values = mapOf(
|
||||
@ -47,5 +53,20 @@ object PackageNamePatch : ResourcePatch() {
|
||||
)
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
if (PackageNameYouTube != null && PackageNameYouTube!! != ORIGINAL_PACKAGE_NAME_YOUTUBE) {
|
||||
packageNameYouTube = PackageNameYouTube!!
|
||||
}
|
||||
if (PackageNameYouTubeMusic != null && PackageNameYouTubeMusic!! != ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC) {
|
||||
packageNameYouTubeMusic = PackageNameYouTubeMusic!!
|
||||
}
|
||||
}
|
||||
|
||||
fun getPackageName(originalPackageName: String): String {
|
||||
if (originalPackageName == ORIGINAL_PACKAGE_NAME_YOUTUBE) {
|
||||
return packageNameYouTube
|
||||
} else if (originalPackageName == ORIGINAL_PACKAGE_NAME_YOUTUBE_MUSIC) {
|
||||
return packageNameYouTubeMusic
|
||||
}
|
||||
throw PatchException("Unknown package name!")
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import app.revanced.patches.youtube.overlaybutton.download.pip.DisablePiPPatch
|
||||
import app.revanced.patches.youtube.overlaybutton.fullscreen.FullscreenButtonPatch
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.OVERLAY_BUTTONS_PATH
|
||||
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
|
||||
import app.revanced.patches.youtube.utils.playerbutton.PlayerButtonHookPatch
|
||||
import app.revanced.patches.youtube.utils.playercontrols.PlayerControlsPatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
@ -31,7 +30,6 @@ import org.w3c.dom.Element
|
||||
DownloadButtonHookPatch::class,
|
||||
FullscreenButtonPatch::class,
|
||||
OverrideSpeedHookPatch::class,
|
||||
PlayerButtonHookPatch::class,
|
||||
PlayerControlsPatch::class,
|
||||
SettingsPatch::class,
|
||||
SharedResourceIdPatch::class,
|
||||
|
@ -66,8 +66,10 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
"19.02.39"
|
||||
]
|
||||
)
|
||||
]
|
||||
],
|
||||
use = false
|
||||
)
|
||||
@Suppress("unused")
|
||||
object SpoofPlayerParameterPatch : BytecodePatch(
|
||||
setOf(
|
||||
ParamsMapPutFingerprint,
|
||||
|
@ -0,0 +1,47 @@
|
||||
package app.revanced.patches.youtube.utils.gms
|
||||
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportPatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch.ORIGINAL_PACKAGE_NAME_YOUTUBE
|
||||
import app.revanced.patches.youtube.utils.fix.clientspoof.ClientSpoofPatch
|
||||
import app.revanced.patches.youtube.utils.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.utils.mainactivity.fingerprints.MainActivityFingerprint
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
|
||||
@Suppress("unused")
|
||||
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
|
||||
fromPackageName = ORIGINAL_PACKAGE_NAME_YOUTUBE,
|
||||
mainActivityOnCreateFingerprint = MainActivityFingerprint,
|
||||
integrationsPatchDependency = IntegrationsPatch::class,
|
||||
dependencies = setOf(ClientSpoofPatch::class, PackageNamePatch::class, SettingsPatch::class),
|
||||
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
|
||||
compatiblePackages = setOf(
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
setOf(
|
||||
"18.29.38",
|
||||
"18.30.37",
|
||||
"18.31.40",
|
||||
"18.32.39",
|
||||
"18.33.40",
|
||||
"18.34.38",
|
||||
"18.35.36",
|
||||
"18.36.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.39.41",
|
||||
"18.40.34",
|
||||
"18.41.39",
|
||||
"18.42.41",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.43",
|
||||
"18.46.45",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39"
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
@ -0,0 +1,33 @@
|
||||
package app.revanced.patches.youtube.utils.gms
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patches.shared.gms.BaseGmsCoreSupportResourcePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch.ORIGINAL_PACKAGE_NAME_YOUTUBE
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePackageName
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
|
||||
object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
|
||||
fromPackageName = ORIGINAL_PACKAGE_NAME_YOUTUBE,
|
||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
||||
dependencies = setOf(PackageNamePatch::class, SettingsPatch::class),
|
||||
) {
|
||||
override fun execute(context: ResourceContext) {
|
||||
super.execute(context)
|
||||
|
||||
/**
|
||||
* Add settings
|
||||
*/
|
||||
SettingsPatch.addPreference(
|
||||
arrayOf(
|
||||
"PREFERENCE: GMS_CORE_SETTINGS"
|
||||
)
|
||||
)
|
||||
SettingsPatch.updatePatchStatus("GmsCore support")
|
||||
|
||||
context.updatePackageName(
|
||||
ORIGINAL_PACKAGE_NAME_YOUTUBE,
|
||||
PackageNamePatch.packageNameYouTube
|
||||
)
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg
|
||||
|
||||
object Constants {
|
||||
internal const val PACKAGE_NAME = "com.google.android.youtube"
|
||||
internal const val SPOOFED_PACKAGE_NAME = PACKAGE_NAME
|
||||
internal const val SPOOFED_PACKAGE_SIGNATURE = "24bb24c05e47e0aefa68a58a766179d9b613a600"
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.microg.MicroGBytecodeHelper
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.patches.youtube.utils.fix.clientspoof.ClientSpoofPatch
|
||||
import app.revanced.patches.youtube.utils.fix.parameter.SpoofPlayerParameterPatch
|
||||
import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
|
||||
import app.revanced.patches.youtube.utils.mainactivity.MainActivityResolvePatch
|
||||
import app.revanced.patches.youtube.utils.microg.Constants.PACKAGE_NAME
|
||||
import app.revanced.patches.youtube.utils.microg.fingerprints.CastContextFetchFingerprint
|
||||
import app.revanced.patches.youtube.utils.microg.fingerprints.CastDynamiteModuleFingerprint
|
||||
import app.revanced.patches.youtube.utils.microg.fingerprints.CastDynamiteModuleV2Fingerprint
|
||||
import app.revanced.patches.youtube.utils.microg.fingerprints.GooglePlayUtilityFingerprint
|
||||
import app.revanced.patches.youtube.utils.microg.fingerprints.PrimeFingerprint
|
||||
import app.revanced.patches.youtube.utils.microg.fingerprints.ServiceCheckFingerprint
|
||||
|
||||
@Patch(
|
||||
dependencies = [
|
||||
ClientSpoofPatch::class,
|
||||
MainActivityResolvePatch::class,
|
||||
PackageNamePatch::class,
|
||||
SpoofPlayerParameterPatch::class
|
||||
]
|
||||
)
|
||||
object MicroGBytecodePatch : BytecodePatch(
|
||||
setOf(
|
||||
CastContextFetchFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
PrimeFingerprint,
|
||||
ServiceCheckFingerprint
|
||||
)
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"$UTILS_PATH/MicroGPatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
val packageName = PackageNamePatch.PackageNameYouTube
|
||||
?: throw PatchException("Invalid package name.")
|
||||
|
||||
if (packageName == PACKAGE_NAME)
|
||||
throw PatchException("Original package name is not available as package name for MicroG build.")
|
||||
|
||||
// apply common microG patch
|
||||
MicroGBytecodeHelper.patchBytecode(
|
||||
context, arrayOf(
|
||||
MicroGBytecodeHelper.packageNameTransform(
|
||||
PACKAGE_NAME,
|
||||
packageName
|
||||
)
|
||||
),
|
||||
MicroGBytecodeHelper.PrimeMethodTransformationData(
|
||||
PrimeFingerprint,
|
||||
PACKAGE_NAME,
|
||||
packageName
|
||||
),
|
||||
listOf(
|
||||
ServiceCheckFingerprint,
|
||||
GooglePlayUtilityFingerprint,
|
||||
CastDynamiteModuleFingerprint,
|
||||
CastDynamiteModuleV2Fingerprint,
|
||||
CastContextFetchFingerprint
|
||||
)
|
||||
)
|
||||
|
||||
MainActivityResolvePatch.injectOnCreateMethodCall(INTEGRATIONS_CLASS_DESCRIPTOR, "checkAvailability")
|
||||
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.shared.microg.MicroGManifestHelper.addSpoofingMetadata
|
||||
import app.revanced.patches.shared.microg.MicroGResourceHelper.patchManifest
|
||||
import app.revanced.patches.shared.microg.MicroGResourceHelper.patchSetting
|
||||
import app.revanced.patches.shared.packagename.PackageNamePatch
|
||||
import app.revanced.patches.youtube.utils.microg.Constants.PACKAGE_NAME
|
||||
import app.revanced.patches.youtube.utils.microg.Constants.SPOOFED_PACKAGE_NAME
|
||||
import app.revanced.patches.youtube.utils.microg.Constants.SPOOFED_PACKAGE_SIGNATURE
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.setMicroG
|
||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||
|
||||
@Patch(
|
||||
name = "MicroG support",
|
||||
description = "Allows ReVanced Extended to run without root and under a different package name with MicroG.",
|
||||
dependencies = [
|
||||
MicroGBytecodePatch::class,
|
||||
PackageNamePatch::class,
|
||||
SettingsPatch::class
|
||||
],
|
||||
compatiblePackages = [
|
||||
CompatiblePackage(
|
||||
"com.google.android.youtube",
|
||||
[
|
||||
"18.29.38",
|
||||
"18.30.37",
|
||||
"18.31.40",
|
||||
"18.32.39",
|
||||
"18.33.40",
|
||||
"18.34.38",
|
||||
"18.35.36",
|
||||
"18.36.39",
|
||||
"18.37.36",
|
||||
"18.38.44",
|
||||
"18.39.41",
|
||||
"18.40.34",
|
||||
"18.41.39",
|
||||
"18.42.41",
|
||||
"18.43.45",
|
||||
"18.44.41",
|
||||
"18.45.43",
|
||||
"18.46.45",
|
||||
"18.48.39",
|
||||
"18.49.37",
|
||||
"19.01.34",
|
||||
"19.02.39"
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object MicroGPatch : ResourcePatch() {
|
||||
override fun execute(context: ResourceContext) {
|
||||
|
||||
val packageName = PackageNamePatch.PackageNameYouTube
|
||||
?: throw PatchException("Invalid package name.")
|
||||
|
||||
if (packageName == PACKAGE_NAME)
|
||||
throw PatchException("Original package name is not available as package name for MicroG build.")
|
||||
|
||||
/**
|
||||
* Add settings
|
||||
*/
|
||||
SettingsPatch.addPreference(
|
||||
arrayOf(
|
||||
"PREFERENCE: MICROG_SETTINGS"
|
||||
)
|
||||
)
|
||||
SettingsPatch.updatePatchStatus("MicroG support")
|
||||
|
||||
// update settings fragment
|
||||
context.patchSetting(
|
||||
PACKAGE_NAME,
|
||||
packageName
|
||||
)
|
||||
|
||||
// update manifest
|
||||
context.patchManifest(
|
||||
PACKAGE_NAME,
|
||||
packageName
|
||||
)
|
||||
|
||||
// add metadata to manifest
|
||||
context.addSpoofingMetadata(
|
||||
SPOOFED_PACKAGE_NAME,
|
||||
SPOOFED_PACKAGE_SIGNATURE
|
||||
)
|
||||
|
||||
setMicroG(packageName)
|
||||
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
object CastContextFetchFingerprint : MethodFingerprint(
|
||||
strings = listOf("Error fetching CastContext.")
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
object CastDynamiteModuleFingerprint : MethodFingerprint(
|
||||
strings = listOf("com.google.android.gms.cast.framework.internal.CastDynamiteModuleImpl")
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
object CastDynamiteModuleV2Fingerprint : MethodFingerprint(
|
||||
strings = listOf("Failed to load module via V2: ")
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
object PrimeFingerprint : MethodFingerprint(
|
||||
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
|
||||
)
|
@ -1,15 +0,0 @@
|
||||
package app.revanced.patches.youtube.utils.microg.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
object ServiceCheckFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
parameters = listOf("L", "I"),
|
||||
strings = listOf(
|
||||
"Google Play Services not available",
|
||||
"GooglePlayServices not available due to error "
|
||||
)
|
||||
)
|
@ -5,17 +5,30 @@ import app.revanced.util.doRecursively
|
||||
import app.revanced.util.insertNode
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@Suppress("DEPRECATION", "MemberVisibilityCanBePrivate")
|
||||
object ResourceUtils {
|
||||
|
||||
const val TARGET_PREFERENCE_PATH = "res/xml/revanced_prefs.xml"
|
||||
|
||||
const val YOUTUBE_SETTINGS_PATH = "res/xml/settings_fragment.xml"
|
||||
|
||||
var targetPackage = "com.google.android.youtube"
|
||||
var youtubePackageName = "com.google.android.youtube"
|
||||
|
||||
fun setMicroG(newPackage: String) {
|
||||
targetPackage = newPackage
|
||||
fun ResourceContext.updatePackageName(
|
||||
fromPackageName: String,
|
||||
toPackageName: String
|
||||
) {
|
||||
youtubePackageName = toPackageName
|
||||
|
||||
val prefs = this["res/xml/settings_fragment.xml"]
|
||||
|
||||
prefs.writeText(
|
||||
prefs.readText()
|
||||
.replace(
|
||||
"android:targetPackage=\"$fromPackageName",
|
||||
"android:targetPackage=\"$toPackageName"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun ResourceContext.addEntryValues(
|
||||
@ -107,7 +120,7 @@ object ResourceUtils {
|
||||
ownerDocument.createElement("intent").also { intentNode ->
|
||||
intentNode.setAttribute(
|
||||
"android:targetPackage",
|
||||
targetPackage
|
||||
youtubePackageName
|
||||
)
|
||||
intentNode.setAttribute("android:data", key + "_intent")
|
||||
intentNode.setAttribute("android:targetClass", targetClass)
|
||||
|
@ -4,6 +4,7 @@ package app.revanced.util
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
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.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
@ -309,3 +310,28 @@ fun BytecodeContext.updatePatchStatus(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resolved methods of [MethodFingerprint]s early.
|
||||
*/
|
||||
fun List<MethodFingerprint>.returnEarly(bool: Boolean = false) {
|
||||
val const = if (bool) "0x1" else "0x0"
|
||||
this.forEach { fingerprint ->
|
||||
fingerprint.result?.let { result ->
|
||||
val stringInstructions = when (result.method.returnType.first()) {
|
||||
'L' -> """
|
||||
const/4 v0, $const
|
||||
return-object v0
|
||||
"""
|
||||
'V' -> "return-void"
|
||||
'I', 'Z' -> """
|
||||
const/4 v0, $const
|
||||
return v0
|
||||
"""
|
||||
else -> throw Exception("This case should never happen.")
|
||||
}
|
||||
|
||||
result.mutableMethod.addInstructions(0, stringInstructions)
|
||||
} ?: throw fingerprint.exception
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user