AXP Magisk resurrection track: Speckit 009 port AXP/LOS22 to LOS23.

Ship Magisk like GOLDEN; KernelSU optional recycle only; parallel to sunfish-a16-rom standby.
This commit is contained in:
sunfish-admin committed 2026-09-16 21:56:22 +00:00
commit 12d2179c20
150 files changed
+26263

No files matched your search

@@ -0,0 +1,634 @@
#!/usr/bin/env python3
"""Apply sunfish-a16 005 signature spoofing extensions on LOS 23.2 tree.
Builds on LuK1337 microG allow-list spoofing already in ComputerEngine:
- Accept project-signed Phonesky (not only MICROG_REAL)
- Gate on Settings.Secure.allow_signature_fake (default off)
- Require FAKE_PACKAGE_SIGNATURE grant (EXTENDROM-style; Self-Check)
- Add permission + Developer Options toggle (EXTENDROM A15 port)
Attribution: EXTENDROM (steadfasterX), LineageOS microG spoof (LuK1337).
Run on rom-dev01: python3 apply_005_sigspoof.py
"""
from __future__ import annotations
import re
import shutil
from pathlib import Path
TREE = Path("/srv/rom/src/lineage")
PHONESKY_HEX = Path("/srv/rom/artifacts/005-spoofing/phonesky-cert.hex").read_text().strip()
MARKER = "SUNFISH_A16_005_SIGSPOOF"
def backup(path: Path) -> None:
# Never leave *.bak inside source trees (aapt2 compiles everything under res/).
bak_root = Path("/srv/rom/artifacts/005-spoofing/bak-snapshots")
bak_root.mkdir(parents=True, exist_ok=True)
rel = path
try:
rel = path.relative_to(TREE)
except ValueError:
pass
bak = bak_root / str(rel).replace("/", "_")
if not bak.exists():
shutil.copy2(path, bak)
def patch_computer_engine() -> None:
path = TREE / "frameworks/base/services/core/java/com/android/server/pm/ComputerEngine.java"
text = path.read_text()
if MARKER in text:
print("ComputerEngine: already patched")
return
backup(path)
real_pat = re.compile(
r'(private static final Signature MICROG_REAL_SIGNATURE = new Signature\("[0-9a-f]+"\);)'
)
m = real_pat.search(text)
if not m:
raise SystemExit("MICROG_REAL_SIGNATURE not found")
insert = (
m.group(1)
+ "\n\n"
+ " // %s: project JKS for patched Phonesky (sha256 "
"313186cb7df2d40133721207b72973e852f61c8767c321e5c75039011b83bd56)\n"
% MARKER
+ ' private static final Signature SUNFISH_PHONESKY_SIGNATURE = new Signature("%s");'
% PHONESKY_HEX
)
text = real_pat.sub(insert, text, count=1)
old_is = """ public static boolean isMicrogSigned(AndroidPackage p) {
if (!isDebuggable()) {
return false;
}
// Allowlist the following apps:
// * com.android.vending - microG Companion
// * com.google.android.gms - microG Services
if (!p.getPackageName().equals("com.android.vending") &&
!p.getPackageName().equals("com.google.android.gms")) {
return false;
}
return Signature.areExactMatch(
p.getSigningDetails(), new Signature[]{MICROG_REAL_SIGNATURE});
}"""
new_is = """ public static boolean isMicrogSigned(AndroidPackage p) {
if (!isDebuggable()) {
return false;
}
// Allowlist the following apps:
// * com.android.vending - microG Companion / patched Phonesky
// * com.google.android.gms - microG Services
if (!p.getPackageName().equals("com.android.vending") &&
!p.getPackageName().equals("com.google.android.gms")) {
return false;
}
if (Signature.areExactMatch(
p.getSigningDetails(), new Signature[]{MICROG_REAL_SIGNATURE})) {
return true;
}
// %s: accept project-signed Phonesky (AXP patch-playstore + our JKS)
return p.getPackageName().equals("com.android.vending")
&& Signature.areExactMatch(
p.getSigningDetails(), new Signature[]{SUNFISH_PHONESKY_SIGNATURE});
}""" % MARKER
if old_is not in text:
raise SystemExit("isMicrogSigned block not found exactly")
text = text.replace(old_is, new_is, 1)
old_gen = """ private static Optional<Signature> generateFakeSignature(AndroidPackage p) {
if (!isMicrogSigned(p)) {
return Optional.empty();
}
Bundle metadata = p.getMetaData();
if (metadata == null) {
return Optional.empty();
}
String fakeSignatureStr = metadata.getString("fake-signature");
if (TextUtils.isEmpty(fakeSignatureStr)) {
return Optional.empty();
}
// Only MICROG_FAKE_SIGNATURE can be faked
Signature fakeSignature = new Signature(fakeSignatureStr);
if (!fakeSignature.equals(MICROG_FAKE_SIGNATURE)) {
return Optional.empty();
}
return Optional.of(fakeSignature);
}"""
new_gen = """ private boolean requestsFakeSignature(AndroidPackage p) {
return p.getMetaData() != null
&& p.getMetaData().getString("fake-signature") != null;
}
private Optional<Signature> generateFakeSignature(AndroidPackage p,
Set<String> permissions) {
// %s: EXTENDROM global gate (Developer Options)
try {
if (android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
android.provider.Settings.Secure.ALLOW_SIGNATURE_FAKE, 0) == 0) {
return Optional.empty();
}
} catch (Throwable t) {
Slog.w(TAG, "allow_signature_fake read failed", t);
return Optional.empty();
}
if (!isMicrogSigned(p)) {
return Optional.empty();
}
// EXTENDROM: require FAKE_PACKAGE_SIGNATURE grant (Self-Check path)
if (!permissions.contains("android.permission.FAKE_PACKAGE_SIGNATURE")) {
return Optional.empty();
}
Bundle metadata = p.getMetaData();
if (metadata == null) {
return Optional.empty();
}
String fakeSignatureStr = metadata.getString("fake-signature");
if (TextUtils.isEmpty(fakeSignatureStr)) {
return Optional.empty();
}
// Only MICROG_FAKE_SIGNATURE (platform Google) can be faked
Signature fakeSignature = new Signature(fakeSignatureStr);
if (!fakeSignature.equals(MICROG_FAKE_SIGNATURE)) {
return Optional.empty();
}
return Optional.of(fakeSignature);
}""" % MARKER
if old_gen not in text:
raise SystemExit("generateFakeSignature block not found exactly")
text = text.replace(old_gen, new_gen, 1)
old_perm = """ // Compute granted permissions only if package has requested permissions
final Set<String> grantedPermissions = ((flags & PackageManager.GET_PERMISSIONS) == 0
|| ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet()
: mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);"""
new_perm = """ // Compute granted permissions only if package has requested permissions
// SUNFISH_A16_005_SIGSPOOF: also when fake-signature meta present (need grant check)
final Set<String> grantedPermissions = (((flags & PackageManager.GET_PERMISSIONS) == 0
&& !requestsFakeSignature(p))
|| ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet()
: mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);"""
if old_perm not in text:
raise SystemExit("grantedPermissions block not found")
text = text.replace(old_perm, new_perm, 1)
old_call = "generateFakeSignature(p).ifPresent(fakeSignature -> {"
new_call = "generateFakeSignature(p, grantedPermissions).ifPresent(fakeSignature -> {"
if old_call not in text:
raise SystemExit("generateFakeSignature call site not found")
text = text.replace(old_call, new_call, 1)
path.write_text(text)
print("ComputerEngine: patched OK")
def patch_settings_secure() -> None:
path = TREE / "frameworks/base/core/java/android/provider/Settings.java"
text = path.read_text()
if MARKER in text:
print("Settings.java: already patched")
return
backup(path)
needle = (
' public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";\n'
"\n"
" /**\n"
" * A flag to tell {@link com.android.server.devicepolicy.DevicePolicyManagerService} that\n"
" * the default for {@link #INSTALL_NON_MARKET_APPS} is reversed for this user on OTA. So it\n"
)
insert = (
' public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";\n'
"\n"
" /**\n"
" * %s / EXTENDROM: whether applications can fake a signature.\n"
" *\n"
" * <p>1 = permit apps to fake signature\n"
" * <p>0 = disable this feature\n"
" * @hide\n"
" */\n"
' public static final String ALLOW_SIGNATURE_FAKE = "allow_signature_fake";\n'
"\n"
" /**\n"
" * A flag to tell {@link com.android.server.devicepolicy.DevicePolicyManagerService} that\n"
" * the default for {@link #INSTALL_NON_MARKET_APPS} is reversed for this user on OTA. So it\n"
) % MARKER
if needle not in text:
raise SystemExit("Settings.java insertion point not found")
path.write_text(text.replace(needle, insert, 1))
print("Settings.java: patched OK")
def patch_android_manifest() -> None:
path = TREE / "frameworks/base/core/res/AndroidManifest.xml"
text = path.read_text()
if MARKER in text:
print("AndroidManifest: already patched")
return
backup(path)
m = re.search(
r'( <permission-group android:name="android\.permission-group\.UNDEFINED"[\s\S]*?/>\s*)',
text,
)
if not m:
m = re.search(
r'( <permission-group android:name="android\.permission-group\.UNDEFINED"[^>]*>[\s\S]*?</permission-group>\s*)',
text,
)
if not m:
raise SystemExit("UNDEFINED permission-group not found")
block = (
m.group(1)
+ "\n"
+ " <!-- %s / EXTENDROM: signature spoofing -->\n" % MARKER
+ ' <permission-group android:name="android.permission-group.FAKE_PACKAGE"\n'
+ ' android:label="@string/permgrouplab_fake_package_signature"\n'
+ ' android:description="@string/permgrouprequest_fake_package_signature"\n'
+ ' android:request="@string/permgrouprequest_fake_package_signature"\n'
+ ' android:priority="100" />\n'
+ "\n"
+ ' <permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE"\n'
+ ' android:permissionGroup="android.permission-group.UNDEFINED"\n'
+ ' android:protectionLevel="dangerous"\n'
+ ' android:label="@string/permlab_fakePackageSignature"\n'
+ ' android:description="@string/permdesc_fakePackageSignature" />\n'
+ "\n"
)
text = text[: m.start()] + block + text[m.end() :]
path.write_text(text)
print("AndroidManifest: patched OK")
def patch_framework_strings() -> None:
path = TREE / "frameworks/base/core/res/res/values/strings.xml"
text = path.read_text()
if MARKER in text:
print("framework strings: already patched")
return
backup(path)
addition = (
"\n"
" <!-- %s / EXTENDROM signature spoofing -->\n"
' <string name="permlab_fakePackageSignature">Spoof package signature</string>\n'
' <string name="permdesc_fakePackageSignature">Allows applications to impersonate another application (known as \\"signature spoofing\\").</string>\n'
' <string name="permgrouplab_fake_package_signature">Spoof package signature</string>\n'
' <string name="permgrouprequest_fake_package_signature">Spoof package signature</string>\n'
) % MARKER
if "</resources>" not in text:
raise SystemExit("framework strings: no </resources>")
path.write_text(text.replace("</resources>", addition + "</resources>", 1))
print("framework strings: patched OK")
def patch_api_current() -> None:
path = TREE / "frameworks/base/core/api/current.txt"
text = path.read_text()
if "FAKE_PACKAGE_SIGNATURE" in text:
print("current.txt: already has FAKE_PACKAGE_SIGNATURE")
return
backup(path)
text2 = text.replace(
' field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST";\n',
' field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST";\n'
' field public static final String FAKE_PACKAGE_SIGNATURE = "android.permission.FAKE_PACKAGE_SIGNATURE";\n',
1,
)
if 'field public static final String FAKE_PACKAGE = "android.permission-group.FAKE_PACKAGE";' not in text2:
text2 = text2.replace(
' field public static final String CONTACTS = "android.permission-group.CONTACTS";\n',
' field public static final String CONTACTS = "android.permission-group.CONTACTS";\n'
' field public static final String FAKE_PACKAGE = "android.permission-group.FAKE_PACKAGE";\n',
1,
)
if text2 == text:
raise SystemExit("current.txt: failed to insert")
path.write_text(text2)
print("current.txt: patched OK")
def patch_lint_baseline() -> None:
path = TREE / "frameworks/base/core/api/lint-baseline.txt"
text = path.read_text()
if "FAKE_PACKAGE_SIGNATURE" in text:
print("lint-baseline: already patched")
return
backup(path)
addition = """UnflaggedApi: android.Manifest.permission#FAKE_PACKAGE_SIGNATURE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.FAKE_PACKAGE_SIGNATURE
UnflaggedApi: android.Manifest.permission_group#FAKE_PACKAGE:
New API must be flagged with @FlaggedApi: field android.Manifest.permission_group.FAKE_PACKAGE
"""
path.write_text(text + addition)
print("lint-baseline: patched OK")
def patch_activity() -> None:
path = TREE / "frameworks/base/core/java/android/app/Activity.java"
text = path.read_text()
if MARKER in text:
print("Activity.java: already patched")
return
backup(path)
needle = """ final PackageManager packageManager = context.getPackageManager();
final Intent intent = packageManager.buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
@NonNull
private int[] getPermissionRequestStates(@NonNull Context deviceContext,
"""
insert = """ final PackageManager packageManager = context.getPackageManager();
// %s / EXTENDROM: deny FAKE_PACKAGE dialog when spoofing toggle off
java.util.List<String> permlist = java.util.Arrays.asList(permissions);
if (permlist.contains("android.permission.FAKE_PACKAGE_SIGNATURE")) {
if (android.provider.Settings.Secure.getInt(getContentResolver(),
android.provider.Settings.Secure.ALLOW_SIGNATURE_FAKE, 0) == 0) {
Log.w(TAG, "FAKE_PACKAGE_SIGNATURE denied: allow_signature_fake is off");
return;
}
}
final Intent intent = packageManager.buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
@NonNull
private int[] getPermissionRequestStates(@NonNull Context deviceContext,
""" % MARKER
if needle not in text:
raise SystemExit("Activity.java insertion point not found")
path.write_text(text.replace(needle, insert, 1))
print("Activity.java: patched OK")
def patch_permission_mapping() -> None:
path = (
TREE
/ "packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt"
)
text = path.read_text()
if MARKER in text:
print("PermissionMapping: already patched")
return
backup(path)
needle = "PLATFORM_PERMISSIONS[Manifest.permission.BODY_SENSORS] = Manifest.permission_group.SENSORS"
addition = (
"\n\n // %s\n"
" PLATFORM_PERMISSIONS[Manifest.permission.FAKE_PACKAGE_SIGNATURE] ="
" Manifest.permission_group.FAKE_PACKAGE" % MARKER
)
if needle in text:
text = text.replace(needle, needle + addition, 1)
else:
needle2 = (
"PLATFORM_PERMISSIONS[Manifest.permission.GET_ACCOUNTS] = Manifest.permission_group.CONTACTS"
)
if needle2 not in text:
raise SystemExit("PermissionMapping insertion point not found")
text = text.replace(needle2, needle2 + addition, 1)
path.write_text(text)
print("PermissionMapping: patched OK")
def patch_settings_app() -> None:
dash = (
TREE
/ "packages/apps/Settings/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java"
)
text = dash.read_text()
if MARKER not in text:
backup(dash)
needle = " controllers.add(new SensitiveContentProtectionPreferenceController(context));\n"
if needle not in text:
raise SystemExit("Dashboard insertion not found")
text = text.replace(
needle,
needle
+ " // %s\n" % MARKER
+ " controllers.add(new SpoofSignaturePreferenceController(context));\n"
+ " controllers.add(new SpoofSignatureInfo(context));\n",
1,
)
dash.write_text(text)
print("Settings dashboard: patched OK")
else:
print("Settings dashboard: already patched")
ctrl = (
TREE
/ "packages/apps/Settings/src/com/android/settings/development/SpoofSignaturePreferenceController.java"
)
if not ctrl.exists():
ctrl.write_text(
"""/*
* Copyright (C) 2019-2024 steadfasterX <steadfasterX |AT| binbash #DOT# rocks>
* LICENSE: GNU General Public License v3.0
* Ported to LOS 23.2 / sunfish-a16-rom (%s).
*/
package com.android.settings.development;
import android.content.Context;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class SpoofSignaturePreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String ALLOW_SIGNATURE_FAKE_KEY = "allow_signature_fake";
@VisibleForTesting
static final int SETTING_VALUE_ON = 1;
@VisibleForTesting
static final int SETTING_VALUE_OFF = 0;
public SpoofSignaturePreferenceController(Context context) {
super(context);
}
@Override
public String getPreferenceKey() {
return ALLOW_SIGNATURE_FAKE_KEY;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (Boolean) newValue;
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ALLOW_SIGNATURE_FAKE,
isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
return true;
}
@Override
public void updateState(Preference preference) {
final int spoofingMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ALLOW_SIGNATURE_FAKE, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(spoofingMode != SETTING_VALUE_OFF);
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ALLOW_SIGNATURE_FAKE, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(false);
}
}
"""
% MARKER
)
print("SpoofSignaturePreferenceController: written")
info = (
TREE
/ "packages/apps/Settings/src/com/android/settings/development/SpoofSignatureInfo.java"
)
if not info.exists():
info.write_text(
"""/*
* Copyright (C) 2019-2024 steadfasterX <steadfasterX |AT| binbash #DOT# rocks>
* LICENSE: GNU General Public License v3.0
* Ported to LOS 23.2 / sunfish-a16-rom (%s).
*/
package com.android.settings.development;
import android.content.Context;
import androidx.preference.Preference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class SpoofSignatureInfo extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String ALLOW_SIGNATURE_FAKE_KEY_INFO = "allow_signature_fake_info";
public SpoofSignatureInfo(Context context) {
super(context);
}
@Override
public String getPreferenceKey() {
return ALLOW_SIGNATURE_FAKE_KEY_INFO;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return true;
}
}
"""
% MARKER
)
print("SpoofSignatureInfo: written")
xml = TREE / "packages/apps/Settings/res/xml/development_settings.xml"
xtext = xml.read_text()
if MARKER not in xtext:
backup(xml)
needle = """ android:key="development_prefs_screen"
android:title="@string/development_settings_title">
<PreferenceCategory
android:key="debug_misc_category"
"""
insert = """ android:key="development_prefs_screen"
android:title="@string/development_settings_title">
<!-- %s / EXTENDROM signature spoofing -->
<PreferenceCategory
android:key="signature_spoof_category"
android:title="@string/signature_spoof_category"
android:order="9999">
<Preference
android:key="allow_signature_fake_info"
android:summary="@string/allow_signature_fake_details" />
<SwitchPreference
android:key="allow_signature_fake"
android:title="@string/allow_signature_fake"
android:summary="@string/allow_signature_fake_summary"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:key="debug_misc_category"
""" % MARKER
if needle not in xtext:
raise SystemExit("development_settings.xml insertion not found")
xml.write_text(xtext.replace(needle, insert, 1))
print("development_settings.xml: patched OK")
else:
print("development_settings.xml: already patched")
sxml = TREE / "packages/apps/Settings/res/values/strings.xml"
stext = sxml.read_text()
if MARKER not in stext:
backup(sxml)
addition = (
"\n"
" <!-- %s / EXTENDROM signature spoofing -->\n"
' <string name="signature_spoof_category">Signature spoofing support</string>\n'
' <string name="allow_signature_fake">Allow signature spoofing</string>\n'
' <string name="allow_signature_fake_summary">If disabled, any previously granted signature spoofing permissions remain in place, but no longer have any effect</string>\n'
' <string name="allow_signature_fake_details">Allows applications to impersonate another application (known as \\"signature spoofing\\").\\n\\nLegitimate applications include microG or Phonesky (Google Play Store for microG).\\n\\nThis option is a global ON/OFF setting, i.e. applications must explicitly request signature spoofing, or it must be manually enabled within each App\\\'s \\"Permission\\" screen (this permission can also be revoked there).</string>\n'
) % MARKER
sxml.write_text(stext.replace("</resources>", addition + "</resources>", 1))
print("Settings strings: patched OK")
else:
print("Settings strings: already patched")
def main() -> None:
if not PHONESKY_HEX or len(PHONESKY_HEX) < 100:
raise SystemExit("phonesky-cert.hex missing/short")
print("Phonesky cert hex len=%d" % len(PHONESKY_HEX))
patch_computer_engine()
patch_settings_secure()
patch_android_manifest()
patch_framework_strings()
patch_api_current()
patch_lint_baseline()
patch_activity()
patch_permission_mapping()
patch_settings_app()
print("ALL DONE")
if __name__ == "__main__":
main()