From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: oxmc 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 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" /> + + + 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 @@ + + Spoof package signature + + 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! + disable or modify status bar diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 3f0c0e1ae..e986335fd 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -1476,6 +1476,55 @@ public class ComputerEngine implements Computer { return result; } + private String getFakeSignature(AndroidPackage p) { + android.os.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 { + android.content.res.ApkAssets apkAssets = + android.content.res.ApkAssets.loadFromPath(p.getBaseApkPath()); + try { + 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); + } finally { + apkAssets.close(); + } + } 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 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 grantedPermissions = ((flags & PackageManager.GET_PERMISSIONS) == 0 + final Set 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;