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

+49
View File
@@ -0,0 +1,49 @@
LOCAL_PATH := $(call my-dir)
# AXP GOLDEN layout (002): GmsCore + GsfProxy on system_ext/app (not priv-app).
# LOCAL_REPLACE_PREBUILT_APK_INSTALLED: copy PRESIGNED APKs unchanged. Without it,
# Make uncompresses JNI / realigns and strips APK Signature Scheme v2/v3 — PackageManager
# then skips Phonesky (and corrupts microG signatures). See docs/a15-a16-delta.md.
include $(CLEAR_VARS)
LOCAL_MODULE := GmsCore
LOCAL_MODULE_CLASS := APPS
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_OVERRIDES_PACKAGES := com.google.android.gms
LOCAL_SRC_FILES := prebuilt/GmsCore.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/GmsCore.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_OPTIONAL_USES_LIBRARIES := \
org.apache.http.legacy \
androidx.window.extensions \
androidx.window.sidecar
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE := GsfProxy
LOCAL_MODULE_CLASS := APPS
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_OVERRIDES_PACKAGES := com.google.android.gsf
LOCAL_SRC_FILES := prebuilt/GsfProxy.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/GsfProxy.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)
# AXP GOLDEN: Phonesky on /system/priv-app/
include $(CLEAR_VARS)
LOCAL_MODULE := Phonesky
LOCAL_MODULE_CLASS := APPS
LOCAL_PRIVILEGED_MODULE := true
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_OVERRIDES_PACKAGES := com.android.vending FakeStore
LOCAL_SRC_FILES := prebuilt/Phonesky.apk
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/prebuilt/Phonesky.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_OPTIONAL_USES_LIBRARIES := \
org.apache.http.legacy \
vendor.google.libraries.desktop.serviceability \
android.ext.adservices \
androidx.window.extensions \
androidx.window.sidecar
include $(BUILD_PREBUILT)
+9
View File
@@ -0,0 +1,9 @@
# sunfish-a16-rom — board tweaks for feature 004 package set
#
# Vanilla LOS sunfish ERROR_LIMIT/2 was ~73MiB too small once microG+Phonesky
# (AXP-sized fat APKs) are added. Raise the *error* ceiling only; keep under
# BOARD_GOOGLE_DYNAMIC_PARTITIONS_SIZE (4873781248). AXP/Divest images carry a
# fuller package set on the same device class — prefer this over ABI-stripping
# official microG (which breaks F-Droid same-signer updates).
BOARD_SUPER_PARTITION_ERROR_LIMIT := 9441378304
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 ale5000
This file was created by ale5000 (ale5000-git on GitHub).
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version, w/ zip exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!-- This file contains permissions to be granted by default.
Default permissions are granted to special platform components and to apps
that are approved to get default grants.
The special components are apps that are expected to work out-of-the-box
as they provide core use cases. These grants are managed by the platform.
Fixed permissions cannot be controlled by the user and need a special
approval. Typically these are to ensure either legally mandated functions
or the app is considered a part of the OS.
Granting these permissions could prevent issues on some ROMs or
on non-clean installations.
-->
<exception package="com.android.vending">
<!-- Contacts -->
<permission name="android.permission.READ_CONTACTS" fixed="false"/>
<permission name="android.permission.WRITE_CONTACTS" fixed="false"/>
<permission name="android.permission.GET_ACCOUNTS" fixed="false"/>
<!-- Location -->
<permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false"/>
<permission name="android.permission.ACCESS_COARSE_LOCATION" fixed="false"/>
<!-- Phone -->
<permission name="android.permission.READ_PHONE_STATE" fixed="false"/>
<permission name="android.permission.CALL_PHONE" fixed="false"/>
<permission name="android.permission.READ_CALL_LOG" fixed="false"/>
<permission name="android.permission.WRITE_CALL_LOG" fixed="false"/>
<permission name="android.permission.ADD_VOICEMAIL" fixed="false"/>
<permission name="android.permission.USE_SIP" fixed="false"/>
<permission name="android.permission.PROCESS_OUTGOING_CALLS" fixed="false"/>
<!-- SMS -->
<permission name="android.permission.SEND_SMS " fixed="false"/>
<permission name="android.permission.RECEIVE_SMS" fixed="false"/>
<permission name="android.permission.READ_SMS" fixed="false"/>
<permission name="android.permission.RECEIVE_WAP_PUSH" fixed="false"/>
<permission name="android.permission.RECEIVE_MMS" fixed="false"/>
<permission name="android.permission.READ_CELL_BROADCASTS" fixed="false"/>
<!-- Storage -->
<permission name="android.permission.READ_EXTERNAL_STORAGE" fixed="false"/>
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" fixed="false"/>
<!-- Signature Spoofing -->
<permission name="android.permission.FAKE_PACKAGE_SIGNATURE" fixed="false"/>
</exception>
+25
View File
@@ -0,0 +1,25 @@
# sunfish-a16-rom — microG + GSF + patched Phonesky (feature 004)
# Layout mirrors AXP GOLDEN: Phonesky→system priv-app; GmsCore/Gsf→system_ext app.
# No FakeStore. APKs are PRESIGNED prebuilts (not in Git meta-repo).
PRODUCT_PACKAGES += \
GmsCore \
GsfProxy \
Phonesky
# A16 / generic_system artifact rules forbid device-specific files on system
# unless allow-listed. Keep AXP GOLDEN paths (system priv-app + XMLs) via allow-list
# rather than moving Phonesky to product.
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
system/priv-app/Phonesky/Phonesky.apk \
system/etc/permissions/com.android.vending.xml \
system/etc/permissions/privapp-permissions-com.android.vending.xml \
system/etc/default-permissions/phonesky-permissions.xml
# Phonesky only — AXP GOLDEN ships these with system priv-app Phonesky.
# microG on GOLDEN is system_ext *app* (not priv-app): do not invent
# privapp-permissions for GmsCore until Self-Check proves A16 needs them.
PRODUCT_COPY_FILES += \
vendor/sunfish-a16/permissions/privapp-permissions-com.android.vending.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/permissions/privapp-permissions-com.android.vending.xml \
vendor/sunfish-a16/permissions/com.android.vending.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/permissions/com.android.vending.xml \
vendor/sunfish-a16/default-permissions/phonesky-permissions.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/default-permissions/phonesky-permissions.xml
+12
View File
@@ -0,0 +1,12 @@
# 005 sigspoof patches
Idempotent apply script for `rom-dev01` LOS 23.2 tree.
```bash
python3 vendor/sunfish-a16/patches/005-sigspoof/apply_005_sigspoof.py
```
Requires `/srv/rom/artifacts/005-spoofing/phonesky-cert.hex` (DER hex of
project Phonesky signer).
See `docs/005-spoofing-runbook.md`.
@@ -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()
@@ -0,0 +1,32 @@
# 008 call-rec + FLAG_SECURE patches
Idempotent apply for LOS 23.2 on `rom-dev01`.
```bash
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py
```
## Attribution
- **Call recording**: EXTENDROM `er.sh` F_CALLREC / AXP GOLDEN
(`extendrom_call_recording`). steadfasterX EXTENDROM.
- **FLAG_SECURE**: AOSP `Settings.Secure.DISABLE_SECURE_WINDOWS` already
wired in `WindowManagerService` on A16; DO preference
`allow_flag_secure_override` writes that key.
## Touched paths
| Area | Path under `/srv/rom/src/lineage/` |
|------|-------------------------------------|
| Secure call-rec | `frameworks/base/core/java/android/provider/Settings.java` (`ER_ALLOW_ANY_CALL_REC`) |
| Dialer gate | `packages/apps/Dialer/java/com/android/incallui/call/CallRecorder.java` |
| Dialer config | already `call_recording_enabled=true` / audio `4` (verified) |
| Settings controllers | `…/ER_CallRec*.java`, `…/FlagSecureOverride*.java` |
| Settings DO XML + strings | `res/xml/development_settings.xml`, `res/values/strings.xml` |
| Dashboard | `DevelopmentSettingsDashboardFragment.java` |
| Capture path | stock WMS `disable_secure_windows` (no extra WM patch) |
Backups: `/srv/rom/artifacts/008-call-rec-flag-secure/bak-snapshots/`
Marker: `SUNFISH_A16_008_CALL_REC_FLAG_SECURE`
See `docs/008-call-rec-flag-secure-runbook.md`.
@@ -0,0 +1,482 @@
#!/usr/bin/env python3
"""Apply sunfish-a16 008 call-rec + FLAG_SECURE Developer Options on LOS 23.2.
Call recording (AXP GOLDEN / EXTENDROM F_CALLREC):
- Settings.Secure.ER_ALLOW_ANY_CALL_REC = \"extendrom_call_recording\"
- DO switch + Dialer CallRecorder.canRecordInCurrentCountry early-return
FLAG_SECURE (AOSP A16 already implements WM path):
- DO switch writes Settings.Secure.DISABLE_SECURE_WINDOWS
(\"disable_secure_windows\"). WindowManagerService.updateDisableSecureWindows()
already honours it on debuggable builds (LAB userdebug).
Attribution: EXTENDROM (steadfasterX) for call-rec; AOSP DISABLE_SECURE_WINDOWS
for capture; sunfish-a16 DO wiring.
Run on rom-dev01:
python3 vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py
"""
from __future__ import annotations
import shutil
from pathlib import Path
TREE = Path("/srv/rom/src/lineage")
MARKER = "SUNFISH_A16_008_CALL_REC_FLAG_SECURE"
BAK_ROOT = Path("/srv/rom/artifacts/008-call-rec-flag-secure/bak-snapshots")
CALL_REC_CTRL = """/*
* Copyright (C) 2024 steadfasterX <steadfasterX |AT| binbash #DOT# rocks>
* LICENSE: GNU General Public License v3.0
* Ported to LOS 23.2 / sunfish-a16-rom ({marker}).
*/
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 ER_CallRecPreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {{
private static final String ER_ALLOW_ANY_CALL_REC_KEY = "extendrom_call_recording";
@VisibleForTesting
static final int SETTING_VALUE_ON = 1;
@VisibleForTesting
static final int SETTING_VALUE_OFF = 0;
public ER_CallRecPreferenceController(Context context) {{
super(context);
}}
@Override
public String getPreferenceKey() {{
return ER_ALLOW_ANY_CALL_REC_KEY;
}}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {{
final boolean isEnabled = (Boolean) newValue;
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ER_ALLOW_ANY_CALL_REC,
isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
return true;
}}
@Override
public void updateState(Preference preference) {{
final int recMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ER_ALLOW_ANY_CALL_REC, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(recMode != SETTING_VALUE_OFF);
}}
@Override
protected void onDeveloperOptionsSwitchDisabled() {{
super.onDeveloperOptionsSwitchDisabled();
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ER_ALLOW_ANY_CALL_REC, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(false);
}}
}}
""".format(marker=MARKER)
CALL_REC_INFO = """/*
* Copyright (C) 2024 steadfasterX <steadfasterX |AT| binbash #DOT# rocks>
* LICENSE: GNU General Public License v3.0
* Ported to LOS 23.2 / sunfish-a16-rom ({marker}).
*/
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 ER_CallRecInfo extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {{
private static final String ER_ALLOW_ANY_CALL_REC_KEY_INFO = "extendrom_call_recording_info";
public ER_CallRecInfo(Context context) {{
super(context);
}}
@Override
public String getPreferenceKey() {{
return ER_ALLOW_ANY_CALL_REC_KEY_INFO;
}}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {{
return true;
}}
}}
""".format(marker=MARKER)
FLAG_SECURE_CTRL = """/*
* sunfish-a16-rom ({marker}): Developer Options gate for AOSP
* Settings.Secure.DISABLE_SECURE_WINDOWS (WindowManagerService already
* honours this on debuggable builds).
*/
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 FlagSecureOverridePreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {{
private static final String PREF_KEY = "allow_flag_secure_override";
@VisibleForTesting
static final int SETTING_VALUE_ON = 1;
@VisibleForTesting
static final int SETTING_VALUE_OFF = 0;
public FlagSecureOverridePreferenceController(Context context) {{
super(context);
}}
@Override
public String getPreferenceKey() {{
return PREF_KEY;
}}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {{
final boolean isEnabled = (Boolean) newValue;
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.DISABLE_SECURE_WINDOWS,
isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
return true;
}}
@Override
public void updateState(Preference preference) {{
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DISABLE_SECURE_WINDOWS, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
}}
@Override
protected void onDeveloperOptionsSwitchDisabled() {{
super.onDeveloperOptionsSwitchDisabled();
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.DISABLE_SECURE_WINDOWS, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(false);
}}
}}
""".format(marker=MARKER)
FLAG_SECURE_INFO = """/*
* sunfish-a16-rom ({marker}).
*/
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 FlagSecureOverrideInfo extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {{
private static final String PREF_KEY = "allow_flag_secure_override_info";
public FlagSecureOverrideInfo(Context context) {{
super(context);
}}
@Override
public String getPreferenceKey() {{
return PREF_KEY;
}}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {{
return true;
}}
}}
""".format(marker=MARKER)
def backup(path: Path) -> None:
BAK_ROOT.mkdir(parents=True, exist_ok=True)
try:
rel = path.relative_to(TREE)
except ValueError:
rel = path
bak = BAK_ROOT / str(rel).replace("/", "_")
if not bak.exists():
shutil.copy2(path, bak)
def write_new(path: Path, content: str) -> None:
if path.exists() and MARKER in path.read_text(errors="ignore"):
print(f"{path.name}: already present")
return
path.parent.mkdir(parents=True, exist_ok=True)
if path.exists():
backup(path)
path.write_text(content)
print(f"{path.name}: written")
def patch_settings_secure() -> None:
path = TREE / "frameworks/base/core/java/android/provider/Settings.java"
text = path.read_text()
if "ER_ALLOW_ANY_CALL_REC" in text:
print("Settings.java: ER_ALLOW_ANY_CALL_REC already present")
return
backup(path)
needle = ' public static final String ALLOW_SIGNATURE_FAKE = "allow_signature_fake";'
if needle not in text:
raise SystemExit("ALLOW_SIGNATURE_FAKE anchor not found in Settings.java")
insert = (
needle
+ "\n\n"
+ " /**\n"
+ f" * {MARKER} / EXTENDROM: unrestricted call recording (AXP GOLDEN).\n"
+ " *\n"
+ " * <p>1 = permit any country to record\n"
+ " * <p>0 = allow only defined countries to record\n"
+ " * @hide\n"
+ " */\n"
+ ' public static final String ER_ALLOW_ANY_CALL_REC = "extendrom_call_recording";'
)
text = text.replace(needle, insert, 1)
path.write_text(text)
print("Settings.java: ER_ALLOW_ANY_CALL_REC added")
def patch_call_recorder() -> None:
path = (
TREE
/ "packages/apps/Dialer/java/com/android/incallui/call/CallRecorder.java"
)
text = path.read_text()
if "extendrom_call_recording" in text or MARKER in text:
print("CallRecorder.java: already patched")
return
backup(path)
if "import android.provider.Settings;" not in text:
# after last import
lines = text.splitlines(keepends=True)
last_import = max(i for i, ln in enumerate(lines) if ln.startswith("import "))
lines.insert(last_import + 1, "import android.provider.Settings; // " + MARKER + "\n")
text = "".join(lines)
old = """ public boolean canRecordInCurrentCountry() {
if (!isEnabled()) {
return false;
}
if (RECORD_ALLOWED_STATE_BY_COUNTRY.isEmpty()) {
loadAllowedStates();
}
String currentCountryIso = GeoUtil.getCurrentCountryIso(context);
Boolean allowedState = RECORD_ALLOWED_STATE_BY_COUNTRY.get(currentCountryIso);
return allowedState != null && allowedState;
}"""
new = """ public boolean canRecordInCurrentCountry() {
if (!isEnabled()) {
return false;
}
// """ + MARKER + """ / EXTENDROM F_CALLREC (AXP GOLDEN)
int ER_CallRecordingEnabled = Settings.Secure.getInt(
context.getContentResolver(),
"extendrom_call_recording", 0);
if (ER_CallRecordingEnabled == 1) {
return true;
}
// END: """ + MARKER + """
if (RECORD_ALLOWED_STATE_BY_COUNTRY.isEmpty()) {
loadAllowedStates();
}
String currentCountryIso = GeoUtil.getCurrentCountryIso(context);
Boolean allowedState = RECORD_ALLOWED_STATE_BY_COUNTRY.get(currentCountryIso);
return allowedState != null && allowedState;
}"""
if old not in text:
raise SystemExit("canRecordInCurrentCountry block not found exactly")
path.write_text(text.replace(old, new, 1))
print("CallRecorder.java: patched")
def patch_development_xml() -> None:
path = TREE / "packages/apps/Settings/res/xml/development_settings.xml"
text = path.read_text()
if "extendrom_call_recording" in text and "allow_flag_secure_override" in text:
print("development_settings.xml: already patched")
return
backup(path)
block = f"""
<!-- {MARKER} / EXTENDROM call recording (AXP GOLDEN) -->
<PreferenceCategory
android:key="extendrom_call_recording_category"
android:title="@string/extendrom_call_recording_category"
android:order="9980">
<Preference
android:key="extendrom_call_recording_info"
android:summary="@string/extendrom_call_recording_details" />
<SwitchPreference
android:key="extendrom_call_recording"
android:title="@string/extendrom_call_recording"
android:summary="@string/extendrom_call_recording_summary"
android:defaultValue="false" />
</PreferenceCategory>
<!-- end: {MARKER} call recording -->
<!-- {MARKER}: FLAG_SECURE / DISABLE_SECURE_WINDOWS DO gate -->
<PreferenceCategory
android:key="flag_secure_override_category"
android:title="@string/flag_secure_override_category"
android:order="9970">
<Preference
android:key="allow_flag_secure_override_info"
android:summary="@string/allow_flag_secure_override_details" />
<SwitchPreference
android:key="allow_flag_secure_override"
android:title="@string/allow_flag_secure_override"
android:summary="@string/allow_flag_secure_override_summary"
android:defaultValue="false" />
</PreferenceCategory>
<!-- end: {MARKER} FLAG_SECURE -->
"""
# Insert after signature spoof category (after first PreferenceCategory block that ends before next)
anchor = ' <!-- SUNFISH_A16_005_SIGSPOOF / EXTENDROM signature spoofing -->'
if anchor not in text:
# fall back: after development_prefs_screen open
needle = 'android:title="@string/development_settings_title">'
if needle not in text:
raise SystemExit("development_settings.xml anchor not found")
text = text.replace(needle, needle + "\n" + block, 1)
else:
# place our categories immediately before 005 category
text = text.replace(anchor, block + " " + anchor.lstrip(), 1)
path.write_text(text)
print("development_settings.xml: patched")
def patch_strings() -> None:
path = TREE / "packages/apps/Settings/res/values/strings.xml"
text = path.read_text()
if "extendrom_call_recording_category" in text and "allow_flag_secure_override" in text:
print("strings.xml: already patched")
return
backup(path)
if not text.rstrip().endswith("</resources>"):
raise SystemExit("strings.xml does not end with </resources>")
add = f"""
<!-- {MARKER} / EXTENDROM call recording -->
<string name="extendrom_call_recording_category">Call recording support</string>
<string name="extendrom_call_recording">Disable call recording restrictions</string>
<string name="extendrom_call_recording_summary">If activated call recording limitations per country will be disabled</string>
<string name="extendrom_call_recording_details">YOU are responsible for checking whether call recording is legal in your country before overriding the default restrictions with this setting!\\nSee: https://axpos.org/r</string>
<!-- {MARKER}: FLAG_SECURE override (writes AOSP disable_secure_windows) -->
<string name="flag_secure_override_category">Secure window screenshots</string>
<string name="allow_flag_secure_override">Allow screenshots of secure windows</string>
<string name="allow_flag_secure_override_summary">When enabled, apps that set FLAG_SECURE can still be captured by the system screenshot / screen record paths (debuggable builds). Weakens app privacy.</string>
<string name="allow_flag_secure_override_details">Writes Settings.Secure.disable_secure_windows. On userdebug/eng, WindowManager treats FLAG_SECURE windows as capturable. Default OFF restores stock secure-window behaviour.</string>
"""
text = text.replace("</resources>", add + "</resources>", 1)
path.write_text(text)
print("strings.xml: patched")
def patch_dashboard() -> None:
path = (
TREE
/ "packages/apps/Settings/src/com/android/settings/development/"
"DevelopmentSettingsDashboardFragment.java"
)
text = path.read_text()
if "ER_CallRecPreferenceController" in text and "FlagSecureOverridePreferenceController" in text:
print("DevelopmentSettingsDashboardFragment: already patched")
return
backup(path)
needle = """ // SUNFISH_A16_005_SIGSPOOF
controllers.add(new SpoofSignaturePreferenceController(context));
controllers.add(new SpoofSignatureInfo(context));"""
if needle not in text:
raise SystemExit("005 SpoofSignature controllers anchor not found")
insert = (
needle
+ f"""
// {MARKER} call-rec (EXTENDROM / AXP GOLDEN)
controllers.add(new ER_CallRecPreferenceController(context));
controllers.add(new ER_CallRecInfo(context));
// {MARKER} FLAG_SECURE (AOSP DISABLE_SECURE_WINDOWS)
controllers.add(new FlagSecureOverridePreferenceController(context));
controllers.add(new FlagSecureOverrideInfo(context));"""
)
path.write_text(text.replace(needle, insert, 1))
print("DevelopmentSettingsDashboardFragment: patched")
def verify_dialer_config() -> None:
path = (
TREE
/ "packages/apps/Dialer/java/com/android/dialer/callrecord/res/values/config.xml"
)
text = path.read_text()
if 'call_recording_enabled">true' in text or "call_recording_enabled\">true" in text:
print("Dialer config.xml: call_recording_enabled=true (OK)")
else:
print("WARNING: Dialer call_recording_enabled not true — check overlay")
if "call_recording_audio_source\">4" in text or 'call_recording_audio_source">4' in text:
print("Dialer config.xml: call_recording_audio_source=4 (OK)")
def main() -> None:
if not TREE.is_dir():
raise SystemExit(f"LOS tree missing: {TREE}")
BAK_ROOT.mkdir(parents=True, exist_ok=True)
settings_dev = TREE / "packages/apps/Settings/src/com/android/settings/development"
write_new(settings_dev / "ER_CallRecPreferenceController.java", CALL_REC_CTRL)
write_new(settings_dev / "ER_CallRecInfo.java", CALL_REC_INFO)
write_new(settings_dev / "FlagSecureOverridePreferenceController.java", FLAG_SECURE_CTRL)
write_new(settings_dev / "FlagSecureOverrideInfo.java", FLAG_SECURE_INFO)
patch_settings_secure()
patch_call_recorder()
patch_strings()
patch_development_xml()
patch_dashboard()
verify_dialer_config()
print("done:", MARKER)
if __name__ == "__main__":
main()
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<privapp-permissions package="com.android.vending">
<permission name="android.permission.FAKE_PACKAGE_SIGNATURE"/>
<permission name="android.permission.ALLOCATE_AGGRESSIVE"/>
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.BATTERY_STATS"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.DUMP"/>
<permission name="android.permission.FORCE_STOP_PACKAGES"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.GET_APP_OPS_STATS"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.PACKAGE_VERIFICATION_AGENT"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.SEND_SMS_NO_CONFIRMATION"/>
<permission name="android.permission.SET_PREFERRED_APPLICATIONS"/>
<permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.READ_RUNTIME_PROFILES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"/>
<permission name="android.permission.GRANT_RUNTIME_PERMISSIONS"/>
<permission name="android.permission.REVOKE_RUNTIME_PERMISSIONS"/>
<permission name="android.permission.SET_PREFERRED_APPLICATIONS"/>
<permission name="android.permission.ACCESS_INSTANT_APPS"/>
<permission name="android.permission.MANAGE_APP_OPS_MODES"/>
<permission name="android.permission.SET_HARMFUL_APP_WARNINGS"/>
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
</privapp-permissions>
</permissions>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<privapp-permissions package="com.android.vending">
<permission name="android.permission.FAKE_PACKAGE_SIGNATURE"/>
<permission name="android.permission.ALLOCATE_AGGRESSIVE"/>
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.BATTERY_STATS"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.DUMP"/>
<permission name="android.permission.FORCE_STOP_PACKAGES"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.GET_APP_OPS_STATS"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.PACKAGE_VERIFICATION_AGENT"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.SEND_SMS_NO_CONFIRMATION"/>
<permission name="android.permission.SET_PREFERRED_APPLICATIONS"/>
<permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.READ_RUNTIME_PROFILES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"/>
<permission name="android.permission.GRANT_RUNTIME_PERMISSIONS"/>
<permission name="android.permission.REVOKE_RUNTIME_PERMISSIONS"/>
<permission name="android.permission.SET_PREFERRED_APPLICATIONS"/>
<permission name="android.permission.ACCESS_INSTANT_APPS"/>
<permission name="android.permission.MANAGE_APP_OPS_MODES"/>
<permission name="android.permission.SET_HARMFUL_APP_WARNINGS"/>
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"/>
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.MANAGE_ROLLBACKS" />
<permission name="android.permission.LOADER_USAGE_STATS" />
<permission name="android.permission.USE_INSTALLER_V2" />
<permission name="com.android.permission.USE_INSTALLER_V2" />
<permission name="android.permission.MANAGE_CLOUDSEARCH" />
<permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
<permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" />
<!-- Play 52.x / Android 16 — required or PackageManager aborts boot on userdebug -->
<permission name="android.permission.HIDE_STATUS_BAR_NOTIFICATION" />
<permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
<!-- Play 52 / targetSdk 37 FGS — without these, VpaService/DownloadService crash -->
<permission name="android.permission.FOREGROUND_SERVICE" />
<permission name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
<permission name="android.permission.SCHEDULE_EXACT_ALARM" />
<permission name="android.permission.USE_EXACT_ALARM" />
</privapp-permissions>
</permissions>