feat(GmsCore support): switch to ReVanced GmsCore vendor

This commit is contained in:
inotia00
2024-04-01 09:25:11 +09:00
parent 135f8d852d
commit 19adea7a86
41 changed files with 709 additions and 957 deletions

View File

@ -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",

View File

@ -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"
),
),
)
)

View File

@ -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
)
}
}

View File

@ -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"
)
}
}

View File

@ -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"
}

View File

@ -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")
}
}

View File

@ -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
)
}
}

View File

@ -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"
)
)

View File

@ -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",

View File

@ -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 + "\""
)
)

View File

@ -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
}

View File

@ -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>",
),
)
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"
}

View File

@ -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"
)
)

View File

@ -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")
)

View File

@ -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

View File

@ -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"
)
}

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

@ -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"
)
)
}
}

View File

@ -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!")
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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"
),
),
)
)

View File

@ -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
)
}
}

View File

@ -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"
}

View File

@ -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")
}
}

View File

@ -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)
}
}

View File

@ -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.")
)

View File

@ -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")
)

View File

@ -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: ")
)

View File

@ -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")
)

View File

@ -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 "
)
)

View File

@ -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)

View File

@ -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
}
}