Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Release

on:
push:
tags:
- "v*"

jobs:
release:
runs-on: ubuntu-latest
name: Release
steps:
- uses: actions/checkout@v6

- name: Cache Gradle
uses: burrunan/gradle-cache-action@v3

- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: "17"

- name: Build
run: ./gradlew --no-daemon :play-services-core:assembleMapboxUser :play-services-core:assembleMapboxHuawei

- name: Move
run: |
mkdir play-services-core/build/outputs/apk/releases
mv \
play-services-core/build/outputs/apk/mapboxUser/release/*.apk \
play-services-core/build/outputs/apk/mapboxHuawei/release/*.apk \
play-services-core/build/outputs/apk/releases
- name: Sign
id: sign_apk
uses: ilharp/sign-android-release@v2
with:
releaseDir: play-services-core/build/outputs/apk/releases
signingKey: ${{ secrets.SIGNING_KEYSTORE }}
keyAlias: ${{ secrets.SIGNING_KEY_ALIAS }}
keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }}
keyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}

- name: Split
uses: jungwinter/split@v2
id: signed_files
with:
msg: ${{ steps.sign_apk.outputs.signedFiles }}
separator: ':'

- name: Release
uses: ncipollo/release-action@v1
with:
artifacts: play-services-core/build/outputs/apk/releases/*-signed.apk
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ microG Services is a FLOSS (Free/Libre Open Source Software) framework to allow

If you'd like to help translate microG, take a look at [TRANSLATION](TRANSLATION.md).

About this fork
-------
This repository is a fork of [microG GmsCore](https://github.com/microg/GmsCore) to allow patched Google apps to run without root and under a different package name using GmsCore instead of Google Play Services, via the "GmsCore support" patch by ReVanced from [ReVanced Patches](https://github.com/revanced/revanced-patches) with the minimum amount of changes necessary to upstream.

Goals of this fork:
- Run next to an existing GMS installation such as [microG GmsCore](https://github.com/microg/GmsCore) or Google Play Services.
- Usage with the "GmsCore support" patch by ReVanced from [ReVanced Patches](https://github.com/revanced/revanced-patches)
- Feature parity with upstream

Nongoals of this fork:
- New features that do not contribute to the goal of this fork
- Bug fixes that do not contribute to the goal of this fork

Contributing to the fork
-------
You can create an issue or pull request to contribute to the fork. In both cases, ensure it aligns with this fork's goals.

License
-------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ abstract class AdvertisingIdConfiguration(private val context: Context) {
for (packageName in packageNames) {
val applicationInfo = context.packageManager.getApplicationInfo(packageName, 0)
if (applicationInfo.targetSdkVersion > 33) {
if (context.packageManager.checkPermission("com.google.android.gms.permission.AD_ID", packageName) == PackageManager.PERMISSION_DENIED) {
if (context.packageManager.checkPermission("app.revanced.android.gms.permission.AD_ID", packageName) == PackageManager.PERMISSION_DENIED) {
throw SecurityException("Permission not granted")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.microg.gms.auth.phone.SmsRetrieverRequestType.RETRIEVER
import org.microg.gms.auth.phone.SmsRetrieverRequestType.USER_CONSENT
import org.microg.gms.common.Constants
import org.microg.gms.utils.getSignatures
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
Expand Down Expand Up @@ -140,7 +139,7 @@ class SmsRetrieverCore(private val context: Context, override val lifecycle: Lif

private fun sendUserConsentBroadcast(request: SmsRetrieverRequest, messageBody: String) {
val userConsentIntent = Intent(context, UserConsentPromptActivity::class.java)
userConsentIntent.setPackage(Constants.GMS_PACKAGE_NAME)
userConsentIntent.setPackage(context.packageName)
userConsentIntent.putExtra(EXTRA_MESSENGER, Messenger(object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
if (Binder.getCallingUid() == Process.myUid()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class SmsRetriever {
* Permission that's used to register the receiver to detect that the broadcaster is the SMS Retriever.
*/
@NonNull
public static final String SEND_PERMISSION = "com.google.android.gms.auth.api.phone.permission.SEND";
public static final String SEND_PERMISSION = "app.revanced.android.gms.auth.api.phone.permission.SEND";
/**
* Intent action when SMS message is retrieved.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public class PackageUtils {
@Deprecated
public static boolean isGooglePackage(@NonNull Context context, @Nullable String packageName) {
if (packageName == null) return false;

packageName = PackageSpoofUtils.spoofPackageName(context.getPackageManager(), packageName);

return new ExtendedPackageInfo(context, packageName).isGoogleOrPlatformPackage();
}

Expand All @@ -59,7 +62,7 @@ public static boolean isGooglePackage(@NonNull Context context, @Nullable String
*/
@Deprecated
public static boolean callerHasExtendedAccessPermission(@NonNull Context context) {
return context.checkCallingPermission("org.microg.gms.EXTENDED_ACCESS") == PackageManager.PERMISSION_GRANTED;
return context.checkCallingPermission("app.revanced.org.microg.gms.EXTENDED_ACCESS") == PackageManager.PERMISSION_GRANTED;
}

public static void assertGooglePackagePermission(@NonNull Context context, GooglePackagePermission permission) {
Expand All @@ -74,7 +77,17 @@ public static void assertGooglePackagePermission(@NonNull Context context, Googl

public static boolean callerHasGooglePackagePermission(@NonNull Context context, GooglePackagePermission permission) {
for (String packageCandidate : getCallingPackageCandidates(context)) {
if (new ExtendedPackageInfo(context, packageCandidate).hasGooglePackagePermission(permission)) {
String packageName = PackageSpoofUtils.spoofPackageName(
context.getPackageManager(),
packageCandidate
);

// See https://github.com/ReVanced/GmsCore/issues/10.
ExtendedPackageInfo extendedPackageInfo = new ExtendedPackageInfo(context, packageName);
if (!extendedPackageInfo.isInstalled())
return true;

if (new ExtendedPackageInfo(context, packageName).hasGooglePackagePermission(permission)) {
return true;
}
}
Expand Down Expand Up @@ -162,7 +175,7 @@ public static byte[] firstSignatureDigestBytes(@NonNull PackageManager packageMa
for (Signature sig : info.signingInfo.getSigningCertificateHistory()) {
byte[] digest = sha1bytes(sig.toByteArray());
if (digest != null) {
return digest;
return PackageSpoofUtils.spoofBytesSignature(packageManager, packageName, digest);
}
}
}
Expand All @@ -171,7 +184,7 @@ public static byte[] firstSignatureDigestBytes(@NonNull PackageManager packageMa
for (Signature sig : info.signatures) {
byte[] digest = sha1bytes(sig.toByteArray());
if (digest != null) {
return digest;
return PackageSpoofUtils.spoofBytesSignature(packageManager, packageName, digest);
}
}
}
Expand All @@ -185,7 +198,7 @@ public static String getCallingPackage(@NonNull Context context) {
if (packageName == null) {
packageName = firstPackageFromUserId(context, callingUid);
}
return packageName;
return PackageSpoofUtils.spoofPackageName(context.getPackageManager(), packageName);
}

public static String[] getCallingPackageCandidates(@NonNull Context context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package org.microg.gms.common

import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import com.google.android.gms.common.BuildConfig
import java.util.*

// Slightly modified implementation from https://github.com/shadow578.
/**
* Utilities to spoof package information.
*/
object PackageSpoofUtils {
private const val TAG = "SpoofUtils"
private const val META_SPOOF_PACKAGE_NAME =
Constants.USER_MICROG_PACKAGE_NAME + ".SPOOFED_PACKAGE_NAME"
private const val META_SPOOF_PACKAGE_SIGNATURE =
Constants.USER_MICROG_PACKAGE_NAME + ".SPOOFED_PACKAGE_SIGNATURE"

private val spoofedPackageNameCache = HashMap<String, String>()
private val spoofedPackageSignatureCache = HashMap<String, String>()

/**
* Spoof the package name of a package, if a spoofed name is set.
*
* @param packageManager [PackageManager] used to get package information.
* @param realPackageName The real package name.
* @return Either the spoofed or the real package name, depending on if the package requested spoofing.
*/
@JvmStatic
fun spoofPackageName(
packageManager: PackageManager,
realPackageName: String?
): String? {
if (realPackageName.isNullOrEmpty()) return realPackageName

val spoofedPackageName = getSpoofedPackageName(packageManager, realPackageName)
return if (!spoofedPackageName.isNullOrEmpty()) {
Log.i(TAG, "Package name of $realPackageName spoofed to $spoofedPackageName")
spoofedPackageName
} else realPackageName
}

/**
* Spoof the signature of a package, if a spoofed name is set.
*
* @param packageManager [PackageManager] used to get package information.
* @param packageName Name of the package to check.
* @param realSignature The real package signature.
* @return Either the spoofed or the real signature, depending on if the package requested spoofing.
*/
@JvmStatic
@JvmName("spoofStringSignature")
fun spoofSignature(
packageManager: PackageManager,
packageName: String,
realSignature: String?
): String? {
val spoofedSignature = getSpoofedSignature(packageManager, packageName)

if (spoofedSignature.isNullOrEmpty()) return realSignature

Log.i(TAG, "Package signature of $packageName spoofed to $spoofedSignature")
return spoofedSignature
}

/**
* Spoof the signature of a package, if a spoofed name is set.
*
* @param packageManager [PackageManager] used to get package information.
* @param packageName Name of the package to check.
* @param realSignature The real package signature.
* @return Either the spoofed or the real signature, depending on if the package requested spoofing.
*/
@JvmStatic
@JvmName("spoofBytesSignature")
fun spoofSignature(
packageManager: PackageManager,
packageName: String,
realSignature: ByteArray?
): ByteArray? {
val spoofedSignatureString = getSpoofedSignature(packageManager, packageName)

if (spoofedSignatureString.isNullOrEmpty()) return realSignature

Log.i(TAG, "Package signature of $packageName spoofed to $spoofedSignatureString")

return spoofedSignatureString.chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}

/**
* Get the spoofed package name for a package.
*
* @param packageManager [PackageManager] used to get package information.
* @param packageName Name of the package to get the spoofed name of.
* @return Spoofed name string, or null if none set.
*/
private fun getSpoofedPackageName(
packageManager: PackageManager,
packageName: String
): String? {
return spoofedPackageNameCache[packageName] ?: run {
val meta = getPackageMetadata(packageManager, packageName)
val spoofedPackageName = meta?.getString(META_SPOOF_PACKAGE_NAME)
if (spoofedPackageName != null) {
spoofedPackageNameCache[packageName] = spoofedPackageName
}

spoofedPackageName
}
}

/**
* Get the spoofed package signature for a package.
*
* @param packageManager [PackageManager] used to get package information.
* @param packageName Name of the package to get spoofed signature of.
* @return Spoofed signature string, or null if none set.
*/
private fun getSpoofedSignature(
packageManager: PackageManager,
packageName: String
): String? {
return spoofedPackageSignatureCache[packageName] ?: run {
val meta = getPackageMetadata(packageManager, packageName)
val spoofedSignature = meta?.getString(META_SPOOF_PACKAGE_SIGNATURE)
if (spoofedSignature != null) {
spoofedPackageSignatureCache[packageName] = spoofedSignature
}

spoofedSignature
}
}

/**
* Get package metadata.
*
* @param packageManager [PackageManager] used to get package information.
* @param packageName Name of the package to get metadata of.
* @return Package metadata bundle.
*/
private fun getPackageMetadata(packageManager: PackageManager, packageName: String): Bundle? {
return try {
// PackageManager.getPackageInfo() has been deprecated in targetSdkVersion 30+
// To solve this, add the QUERY_ALL_PACKAGES permission to AndroidManifest.xml
packageManager
.getPackageInfo(packageName, PackageManager.GET_META_DATA)
?.applicationInfo
?.metaData
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to get application metadata for $packageName", e)
null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public static String getOpenSourceSoftwareLicenseInfo(Context context) {
*/
public static Context getRemoteContext(Context context) {
try {
return context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
return context.createPackageContext(context.getPackageName(), Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (PackageManager.NameNotFoundException unused) {
return null;
}
Expand All @@ -150,7 +150,7 @@ public static Context getRemoteContext(Context context) {
*/
public static Resources getRemoteResources(Context context) {
try {
return context.getPackageManager().getResourcesForApplication(Constants.GMS_PACKAGE_NAME);
return context.getPackageManager().getResourcesForApplication(context.getPackageName());
} catch (PackageManager.NameNotFoundException unused) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ private String sha1sum(byte[] bytes) {
}

private String getTargetPackageWithoutPref() {
// Must be self, as we want to bind to our own services, and not to GMS or microG ones,
// in case system GMS is installed alongside.
if (true) return context.getPackageName();

// Pref: gms > microG > self
PackageManager pm = context.getPackageManager();
try {
Expand Down Expand Up @@ -254,7 +258,7 @@ private Intent getIntent() {
if (requireMicrog && !isMicrog(resolveInfo)) {
Log.w(TAG, "GMS service found for " + actionString + " but looks not like microG");
} else {
if (isSystemGoogleOrMicrogSig(pm, targetPackage)){
if (isSystemGoogleOrMicrogSig(pm, targetPackage)) {
Log.d(TAG, "GMS service found for " + actionString);
return intent;
} else {
Expand Down Expand Up @@ -298,7 +302,7 @@ public boolean isMicrog(ResolveInfo resolveInfo) {
if (resolveInfo == null || resolveInfo.serviceInfo == null) return false;
if (resolveInfo.serviceInfo.name.startsWith("org.microg.")) return true;
try {
PermissionInfo info = context.getPackageManager().getPermissionInfo("org.microg.gms.EXTENDED_ACCESS", 0);
PermissionInfo info = context.getPackageManager().getPermissionInfo("app.revanced.org.microg.gms.EXTENDED_ACCESS", 0);
return info.packageName.equals(resolveInfo.serviceInfo.packageName);
} catch (PackageManager.NameNotFoundException e) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class AuthConstants {
public static final String PROVIDER_METHOD_CLEAR_PASSWORD = "clear_password";
public static final String PROVIDER_EXTRA_CLEAR_PASSWORD = "clear_password";
public static final String PROVIDER_EXTRA_ACCOUNTS = "accounts";
public static final String DEFAULT_ACCOUNT_TYPE = "com.google";
public static final String DEFAULT_ACCOUNT_TYPE = "app.revanced";
public static final String WORK_ACCOUNT_TYPE = "com.google.work";

public static final String KEY_ACCOUNT_SERVICES = "services";
Expand Down
Loading