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:
commit
12d2179c20
150 files changed
+26263
No files matched your search
Vendored
+49
@@ -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)
|
||||
Vendored
+9
@@ -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>
|
||||
Vendored
+25
@@ -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
|
||||
@@ -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`.
|
||||
BIN
Binary file not shown.
@@ -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`.
|
||||
+482
@@ -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()
|
||||
@@ -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>
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
From: sunfish-a16-rom local integration
|
||||
Subject: [PATCH] exec: avoid filename use-after-free in ReSukiSU post hook
|
||||
|
||||
Applies to our existing manual-hook integration, not pristine LineageOS.
|
||||
__do_execve_file() consumes filename, including putname() on return paths.
|
||||
The current wrapper then passes the freed object to a helper that reads
|
||||
filename->name. Call ReSukiSU's post handler directly with NULL instead;
|
||||
that handler explicitly accepts missing arguments for its LSM adapter.
|
||||
|
||||
Reference: ReSukiSU 6d674e50a022a85076dcfe4498af9a8bfead2cf9,
|
||||
kernel/feature/sucompat.c (ksu_handle_post_execve and post_execveat).
|
||||
This fixes a source-level lifetime defect; no boot-panic causality claimed.
|
||||
Prepared during the DEBUG build without modifying the live build inputs.
|
||||
|
||||
--- a/fs/exec.c
|
||||
+++ b/fs/exec.c
|
||||
@@ -1885,7 +1885,7 @@
|
||||
extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
__attribute__((hot))
|
||||
-extern int ksu_handle_post_execveat(int *fd, struct filename **filename_ptr,
|
||||
+extern int ksu_handle_post_execve(int *fd, const char *filename,
|
||||
void *argv, void *envp, int *flags, int *retval);
|
||||
#endif
|
||||
|
||||
@@ -1898,7 +1898,11 @@
|
||||
int retval;
|
||||
ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
|
||||
retval = __do_execve_file(fd, filename, argv, envp, flags, NULL);
|
||||
- ksu_handle_post_execveat(&fd, &filename, &argv, &envp, &flags, &retval);
|
||||
+ /* __do_execve_file() already released filename on success and failure.
|
||||
+ * The ReSukiSU post hook accepts NULL filename (also used by its LSM
|
||||
+ * adapter); do not dereference the freed struct filename wrapper.
|
||||
+ */
|
||||
+ ksu_handle_post_execve(&fd, NULL, &argv, &envp, &flags, &retval);
|
||||
return retval;
|
||||
#else
|
||||
return __do_execve_file(fd, filename, argv, envp, flags, NULL);
|
||||
Reference in new issue
Block a user