Files
patches/frameworks/base/android_frameworks_base-signature-spoofing.patch
oxmc d6605ad6ac patches: fix signature spoofing for android:resource meta-data
Bundle.getString(fake-signature) silently returns null when GmsCore
declares the cert via android:resource=@string/fake_signature — the
Bundle entry is an Integer (resource ID), not a String.

Add getFakeSignature() which falls back to meta.getInt() and resolves
the resource ID by loading the APK via ApkAssets.loadFromPath() +
AssetManager.Builder, avoiding re-entry into PackageManager.

requestsFakeSignature() and mayFakeSignature() now delegate to this
helper instead of calling getString() directly.
2026-06-16 23:09:33 -07:00

157 lines
8.7 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: oxmc <me@oxmc.me>
Date: Wed, 03 Jun 2026 00:00:00 +0000
Subject: [PATCH] Add support for app signature spoofing
This is needed by microG GmsCore to pretend to be the official Google
Play Services package, because client apps check the package signature
to make sure it matches Google's official certificate.
FAKE_PACKAGE_SIGNATURE is declared signature|privileged so it cannot
be requested by arbitrary apps. It is auto-granted to MicroG packages
via a privapp-permissions XML shipped in vendor/pawlet.
Originally by Danny Lin <danny@kdrag0n.dev> for Android 12.
Forward-ported to Android 16 for PawletOS: logic lives in the
lock-free ComputerEngine subclass and uses the current API
(state.getFirstInstallTimeMillis() / ps.getLastUpdateTime()).
Change-Id: Ied7d6ce0b83a2d2345c3abba0429998d86494a88
---
diff --git a/core/api/current.txt b/core/api/current.txt
index a1d5522e6ace..dffe19603ab1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -108,6 +108,7 @@ package android {
field @FlaggedApi("android.xr.xr_manifest_entries") public static final String EYE_TRACKING_FINE = "android.permission.EYE_TRACKING_FINE";
field @FlaggedApi("android.xr.xr_manifest_entries") public static final String FACE_TRACKING = "android.permission.FACE_TRACKING";
field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST";
+ field public static final String FAKE_PACKAGE_SIGNATURE = "android.permission.FAKE_PACKAGE_SIGNATURE";
field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
field public static final String FOREGROUND_SERVICE_CAMERA = "android.permission.FOREGROUND_SERVICE_CAMERA";
field public static final String FOREGROUND_SERVICE_CONNECTED_DEVICE = "android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE";
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1194,6 +1194,8 @@ Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.da
Documentation mentions 'TODO'
+UnflaggedApi: android.Manifest.permission#FAKE_PACKAGE_SIGNATURE:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.FAKE_PACKAGE_SIGNATURE
UnflaggedApi: android.R.color#on_surface_disabled_material:
New API must be flagged with @FlaggedApi: field android.R.color.on_surface_disabled_material
UnflaggedApi: android.R.color#outline_disabled_material:
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3a5dbd08dba5..ebba48a1acb1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5001,6 +5001,15 @@
android:description="@string/permdesc_getPackageSize"
android:protectionLevel="normal" />
+ <!-- Allows an application to change the package signature as seen by
+ other applications. Declared signature|privileged so it cannot be
+ requested at runtime — granted only via privapp-permissions XML. -->
+ <permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/permlab_fakePackageSignature"
+ android:description="@string/permdesc_fakePackageSignature" />
+
<!-- @deprecated No longer useful, see
{@link android.content.pm.PackageManager#addPackageToPreferred}
for details. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f9abbcd1b24a..1a87b2ad4500 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1142,6 +1142,11 @@
<!-- Permissions -->
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_fakePackageSignature">Spoof package signature</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_fakePackageSignature">Allows the app to pretend to be a different app. Malicious applications might be able to use this to access private application data. Legitimate uses include an emulator pretending to be what it emulates. Grant this permission with caution only!</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_statusBar">disable or modify status bar</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3f0c0e1aead4..e3c5a0ca8da1 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1476,6 +1476,52 @@ public class ComputerEngine implements Computer {
return result;
}
+ private String getFakeSignature(AndroidPackage p) {
+ Bundle meta = p.getMetaData();
+ if (meta == null) return null;
+ String sig = meta.getString("fake-signature");
+ if (sig != null) return sig;
+ int resId = meta.getInt("fake-signature", 0);
+ if (resId == 0) return null;
+ try {
+ try (android.content.res.ApkAssets apkAssets =
+ android.content.res.ApkAssets.loadFromPath(p.getBaseApkPath())) {
+ android.content.res.AssetManager am =
+ new android.content.res.AssetManager.Builder()
+ .addApkAssets(apkAssets).build();
+ android.content.res.Resources res = new android.content.res.Resources(
+ am, android.content.res.Resources.getSystem().getDisplayMetrics(),
+ android.content.res.Resources.getSystem().getConfiguration());
+ return res.getString(resId);
+ }
+ } catch (Exception e) {
+ Log.w("PackageManagerService.FAKE_PACKAGE_SIGNATURE",
+ "Failed to resolve fake-signature resource for "
+ + p.getPackageName() + ": " + e);
+ return null;
+ }
+ }
+
+ private boolean requestsFakeSignature(AndroidPackage p) {
+ return getFakeSignature(p) != null;
+ }
+
+ private PackageInfo mayFakeSignature(AndroidPackage p, PackageInfo pi,
+ Set<String> permissions) {
+ try {
+ if (p.getMetaData() != null && p.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ String sig = getFakeSignature(p);
+ if (sig != null && permissions.contains("android.permission.FAKE_PACKAGE_SIGNATURE")) {
+ pi.signatures = new Signature[] {new Signature(sig)};
+ }
+ }
+ } catch (Throwable t) {
+ // We should never die because of any failures, this is system code!
+ Log.w("PackageManagerService.FAKE_PACKAGE_SIGNATURE", t);
+ }
+ return pi;
+ }
+
public final PackageInfo generatePackageInfo(PackageStateInternal ps,
@PackageManager.PackageInfoFlagsBits long flags, int userId) {
if (!mUserManager.exists(userId)) return null;
@@ -1509,13 +1555,15 @@ public class ComputerEngine implements Computer {
|| ArrayUtils.isEmpty(p.getPermissions())) ? Collections.emptySet()
: mPermissionManager.getInstalledPermissions(ps.getPackageName());
// Compute granted permissions only if package has requested permissions
- final Set<String> grantedPermissions = ((flags & PackageManager.GET_PERMISSIONS) == 0
+ final Set<String> permissions = (((flags & PackageManager.GET_PERMISSIONS) == 0
+ && !requestsFakeSignature(p))
|| ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet()
: mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);
- PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
+ PackageInfo packageInfo = mayFakeSignature(p, PackageInfoUtils.generate(p, gids, flags,
state.getFirstInstallTimeMillis(), ps.getLastUpdateTime(), installedPermissions,
- grantedPermissions, state, userId, ps);
+ permissions, state, userId, ps),
+ permissions);
if (packageInfo == null) {
return null;