009: Polish bacon fixes, OTA host scripts, and migration docs.
Restore disable_configstore, EuiccDisabler and thermal CDEV guards, branding and rebuild/sync helpers; document device roles, laptop migration, and Gitea. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
3f941e8451
commit
2d145c2878
3193 files changed
+21264
-371
No files matched your search
+11
-4
@@ -14,8 +14,9 @@
|
||||
4. **Magisk preroot** — EXTENDROM-class boot/`init_boot` patch using Magisk APK
|
||||
from `/srv/rom/prebuilts/magisk/` (see `docs/009-magisk-preinstall-runbook.md`).
|
||||
Stub: `vendor/sunfish-a16/magisk.mk`. Attribute: Magisk upstream + sfX
|
||||
extendrom Magisk patcher. **Host tree currently has no `vendor/extendrom`**
|
||||
— must vendor minimal patcher or checkout extendrom before first Magisk bacon.
|
||||
extendrom Magisk patcher. **Shipped on LAB (2026-10):** Magisk 30.7 preroot
|
||||
via host EXTENDROM at `/srv/rom/references/extendrom/` (wired by the ship
|
||||
bacon). Do not treat the old “blocked on T011” note as current.
|
||||
5. **Bundled apps** — `/srv/rom/prebuilts/axp-apps/` + `apps.mk` (Aurora,
|
||||
Droidify, Fossify Gallery). Symlink APKs into `vendor/sunfish-a16/prebuilt/`.
|
||||
6. **Private DNS presets** — `apply_009_private_dns_providers.py` (GOLDEN list).
|
||||
@@ -31,6 +32,10 @@
|
||||
`vendor/sunfish-a16/patches/009-kernel-hardening/apply_stage1.sh`
|
||||
(AXP Graphene/AH allowlist). Priority: AXP → LOS → Iodé → optional
|
||||
Graphene; no Divest hunt; no extra location lockdown.
|
||||
11. **2026-10-16 polish** (ship bacon) — `apply_009_euicc_disabler_guard.py`,
|
||||
`apply_009_thermal_cdev_log.py`; wave4
|
||||
`apply_009_drop_lab_only_commands.py` must **keep/restore**
|
||||
`disable_configstore`. Details: `docs/009-lab-polish-20261016.md`.
|
||||
|
||||
## Product includes
|
||||
|
||||
@@ -54,5 +59,7 @@ Wire Magisk-resurrection product from `vendor/sunfish-a16/` into
|
||||
| Vanilla baseline | `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish/` |
|
||||
| Optional KSU boots | `/srv/rom/artifacts/007-kernelsu/` (non-ship) |
|
||||
|
||||
**Gap:** `vendor/extendrom` is **not** checked out in the lineage tree yet.
|
||||
Magisk bacon blocked on T011 (symlink/checkout + PREROOT or slim wire).
|
||||
**Host wiring (current):** EXTENDROM Magisk patcher lives under
|
||||
`/srv/rom/references/extendrom/` and is invoked by
|
||||
`scripts/009-bacon-axp-stage1-combo4-cve-hi-releasekeys.sh`. Magisk APK pin:
|
||||
`/srv/rom/prebuilts/magisk/Magisk-v30.7.apk`.
|
||||
+75
-35
@@ -33,6 +33,7 @@ KEYGUARD = (
|
||||
BASE / "packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java"
|
||||
)
|
||||
QSTILE = BASE / "packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java"
|
||||
SYSTEM_SERVER = BASE / "services/java/com/android/server/SystemServer.java"
|
||||
|
||||
XML_OLD = """ <permission name="android.permission.REAL_GET_TASKS"/>
|
||||
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
|
||||
@@ -65,6 +66,42 @@ IMPORT_NEW = (
|
||||
)
|
||||
|
||||
|
||||
REQUIRED_MARKERS = (
|
||||
"settings_reboot_after_timeout",
|
||||
"android.permission.REBOOT",
|
||||
"cancelDoRebootForOwnerAfterTimeoutIfEnabled();",
|
||||
"class SystemServerExt",
|
||||
"class WifiAutoOff",
|
||||
"class BluetoothAutoOff",
|
||||
"import android.bluetooth.BluetoothProfile;",
|
||||
"WIFI_OFF_TIMEOUT",
|
||||
"BLUETOOTH_OFF_TIMEOUT",
|
||||
"isAllowedWhenLocked",
|
||||
"KeyguardStateController",
|
||||
)
|
||||
|
||||
|
||||
def markers_blob() -> str:
|
||||
paths = (
|
||||
BASE / "core/java/android/provider/Settings.java",
|
||||
XML,
|
||||
KEYGUARD,
|
||||
BASE / "services/core/java/com/android/server/ext/SystemServerExt.java",
|
||||
BASE / "services/core/java/com/android/server/ext/WifiAutoOff.java",
|
||||
BASE / "services/core/java/com/android/server/ext/BluetoothAutoOff.java",
|
||||
QSTILE,
|
||||
)
|
||||
return "\n".join(path.read_text() for path in paths)
|
||||
|
||||
|
||||
def markers_present() -> bool:
|
||||
try:
|
||||
blob = markers_blob()
|
||||
except OSError:
|
||||
return False
|
||||
return all(item in blob for item in REQUIRED_MARKERS)
|
||||
|
||||
|
||||
def replace_once(path: pathlib.Path, old: str, new: str) -> None:
|
||||
text = path.read_text()
|
||||
if new in text:
|
||||
@@ -97,18 +134,43 @@ def apply_patch(name: str) -> None:
|
||||
)
|
||||
sys.stdout.write(result.stdout)
|
||||
sys.stdout.write(result.stderr)
|
||||
if result.returncode != 0 and "FAILED" in result.stderr + result.stdout:
|
||||
print(f"patch_incomplete: {name}")
|
||||
elif result.returncode != 0:
|
||||
print(f"patch_exit_{result.returncode}: {name}")
|
||||
combined = result.stdout + result.stderr
|
||||
dismiss_stale_rejects()
|
||||
if result.returncode != 0 or "FAILED" in combined:
|
||||
if markers_present():
|
||||
print(f"patch_skip_ok (markers present): {name}")
|
||||
return
|
||||
if (
|
||||
name == "0015-System_Server_Extensions.patch"
|
||||
and "com.android.server.ext.SystemServerExt.init" in SYSTEM_SERVER.read_text()
|
||||
):
|
||||
print(f"patch_skip_ok (SystemServerExt.init present): {name}")
|
||||
return
|
||||
sys.exit(f"FAIL patch {name}: {combined[-800:]}")
|
||||
|
||||
|
||||
def dismiss_stale_rejects() -> None:
|
||||
"""LOS A16 moved anchors; AXP hunks may leave .rej while the code is already in."""
|
||||
rej = SYSTEM_SERVER.parent / f"{SYSTEM_SERVER.name}.rej"
|
||||
if rej.is_file() and "com.android.server.ext.SystemServerExt.init" in SYSTEM_SERVER.read_text():
|
||||
rej.unlink()
|
||||
print("OK: dismissed stale SystemServer.java.rej")
|
||||
|
||||
|
||||
def clean_rejects() -> None:
|
||||
for path in (XML, KEYGUARD, QSTILE):
|
||||
for suffix in (".rej", ".orig"):
|
||||
extra = path.with_name(path.name + suffix)
|
||||
if extra.exists():
|
||||
extra.unlink()
|
||||
rejs = list(BASE.rglob("*.rej"))
|
||||
if markers_present():
|
||||
for path in rejs:
|
||||
path.unlink()
|
||||
print(f"OK: purged stale reject {path}")
|
||||
return
|
||||
for path in rejs:
|
||||
sys.exit(f"FAIL: reject file {path}")
|
||||
|
||||
|
||||
def scrub_patch_artifacts() -> None:
|
||||
for path in BASE.rglob("*.orig"):
|
||||
path.unlink()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -124,33 +186,11 @@ def main() -> None:
|
||||
):
|
||||
apply_patch(name)
|
||||
replace_once(QSTILE, IMPORT_OLD, IMPORT_NEW)
|
||||
scrub_patch_artifacts()
|
||||
dismiss_stale_rejects()
|
||||
clean_rejects()
|
||||
required = [
|
||||
"settings_reboot_after_timeout",
|
||||
"android.permission.REBOOT",
|
||||
"cancelDoRebootForOwnerAfterTimeoutIfEnabled();",
|
||||
"class SystemServerExt",
|
||||
"class WifiAutoOff",
|
||||
"class BluetoothAutoOff",
|
||||
"import android.bluetooth.BluetoothProfile;",
|
||||
"WIFI_OFF_TIMEOUT",
|
||||
"BLUETOOTH_OFF_TIMEOUT",
|
||||
"isAllowedWhenLocked",
|
||||
"KeyguardStateController",
|
||||
]
|
||||
blob = "\n".join(
|
||||
path.read_text()
|
||||
for path in (
|
||||
BASE / "core/java/android/provider/Settings.java",
|
||||
XML,
|
||||
KEYGUARD,
|
||||
BASE / "services/core/java/com/android/server/ext/SystemServerExt.java",
|
||||
BASE / "services/core/java/com/android/server/ext/WifiAutoOff.java",
|
||||
BASE / "services/core/java/com/android/server/ext/BluetoothAutoOff.java",
|
||||
QSTILE,
|
||||
)
|
||||
)
|
||||
missing = [item for item in required if item not in blob]
|
||||
blob = markers_blob()
|
||||
missing = [item for item in REQUIRED_MARKERS if item not in blob]
|
||||
if missing:
|
||||
sys.exit("missing after apply: " + ", ".join(missing))
|
||||
print("done: SUNFISH_A16_009_A15_REBOOT_TIMEOUTS")
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
"""009: AXP visual branding from android_vendor_lineage (Disroot AXP.OS).
|
||||
|
||||
Copies bootanimation + vendor/lineage/overlay tree from
|
||||
vendor/sunfish-a16/branding/ into the LOS tree. Adds Resurrection copy on
|
||||
setup welcome where AXP/Lineage would show a generic OS name.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
MARKER = "SUNFISH_A16_009_AXP_BRANDING"
|
||||
TREE = pathlib.Path("/srv/rom/src/lineage")
|
||||
BRANDING = TREE / "vendor/sunfish-a16/branding"
|
||||
BOOTANIM_SRC = BRANDING / "bootanimation"
|
||||
BOOTANIM_DST = TREE / "vendor/lineage/bootanimation"
|
||||
GEN_BOOTANIM_SH = BOOTANIM_DST / "gen-bootanimation.sh"
|
||||
OVERLAY_SRC = BRANDING / "overlay"
|
||||
OVERLAY_DST = TREE / "vendor/lineage/overlay"
|
||||
SETUP_STRINGS = TREE / "packages/apps/SetupWizard/res/values/strings.xml"
|
||||
|
||||
WELCOME_OLD = re.compile(
|
||||
r'<string name="setup_welcome_message">.*?</string>',
|
||||
re.DOTALL,
|
||||
)
|
||||
WELCOME_NEW = (
|
||||
f" <!-- {MARKER}: AXP visual track; Resurrection suffix on welcome -->\n"
|
||||
' <string name="setup_welcome_message">Welcome to AXP Resurrection</string>'
|
||||
)
|
||||
|
||||
|
||||
def _copy_file(src: pathlib.Path, dst: pathlib.Path) -> None:
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
|
||||
def _sync_tree(src_root: pathlib.Path, dst_root: pathlib.Path) -> int:
|
||||
if not src_root.is_dir():
|
||||
sys.exit(f"missing branding dir: {src_root}")
|
||||
n = 0
|
||||
for src in src_root.rglob("*"):
|
||||
if not src.is_file():
|
||||
continue
|
||||
# Android.bp under branding/ is a Soong duplicate of vendor/lineage
|
||||
# modules; never leave/copy them into the sunfish-a16 tree scan path.
|
||||
if src.name in ("Android.bp", "Android.mk"):
|
||||
continue
|
||||
rel = src.relative_to(src_root)
|
||||
dst = dst_root / rel
|
||||
if src.stat().st_size != (dst.stat().st_size if dst.is_file() else -1) or (
|
||||
not dst.is_file()
|
||||
or src.read_bytes() != dst.read_bytes()
|
||||
):
|
||||
_copy_file(src, dst)
|
||||
n += 1
|
||||
return n
|
||||
|
||||
|
||||
def _patch_gen_bootanimation_sh() -> None:
|
||||
"""LOS 23 builds bootanimation.zip via Soong gen-bootanimation.sh, not Android.mk."""
|
||||
if not GEN_BOOTANIM_SH.is_file():
|
||||
sys.exit(f"missing {GEN_BOOTANIM_SH}")
|
||||
text = GEN_BOOTANIM_SH.read_text()
|
||||
marker = "AXP_BOOTANIMATION_FULLSCREEN"
|
||||
if marker in text and 'grep -q "^c 0 0 part2"' in text:
|
||||
print(f"already: {GEN_BOOTANIM_SH} ({marker})")
|
||||
return
|
||||
if marker in text:
|
||||
sys.exit(
|
||||
f"{GEN_BOOTANIM_SH} has stale {marker} patch; "
|
||||
"git checkout vendor/lineage/bootanimation/gen-bootanimation.sh then re-run"
|
||||
)
|
||||
old = (
|
||||
"IMAGESCALEHEIGHT=$(expr $IMAGESCALEWIDTH / 3);\n\n"
|
||||
'if [ "$PARAM_TARGET_BOOTANIMATION_HALF_RES" = "true" ]; then\n'
|
||||
' IMAGEWIDTH="$(expr "$IMAGEWIDTH" / 2)"\n'
|
||||
"fi\n\n"
|
||||
"IMAGEHEIGHT=$(expr $IMAGEWIDTH / 3);\n"
|
||||
)
|
||||
new = (
|
||||
f"# {marker}: 3-part AXP desc => full screen (Soong sandbox has no flag file)\n"
|
||||
"AXP_FULLSCREEN=0\n"
|
||||
'if grep -q "^c 0 0 part2" "$PARAM_DESC_TXT" 2>/dev/null; then\n'
|
||||
" AXP_FULLSCREEN=1\n"
|
||||
"fi\n"
|
||||
'if [ "$AXP_FULLSCREEN" = 1 ]; then\n'
|
||||
" IMAGESCALEHEIGHT=$PARAM_TARGET_SCREEN_HEIGHT\n"
|
||||
"else\n"
|
||||
" IMAGESCALEHEIGHT=$(expr $IMAGESCALEWIDTH / 3)\n"
|
||||
"fi\n\n"
|
||||
'if [ "$PARAM_TARGET_BOOTANIMATION_HALF_RES" = "true" ]; then\n'
|
||||
' IMAGEWIDTH="$(expr "$IMAGEWIDTH" / 2)"\n'
|
||||
"fi\n\n"
|
||||
'if [ "$AXP_FULLSCREEN" = 1 ]; then\n'
|
||||
" IMAGEHEIGHT=$PARAM_TARGET_SCREEN_HEIGHT\n"
|
||||
"else\n"
|
||||
" IMAGEHEIGHT=$(expr $IMAGEWIDTH / 3)\n"
|
||||
"fi\n"
|
||||
)
|
||||
if old not in text:
|
||||
sys.exit(f"gen-bootanimation.sh anchor missing in {GEN_BOOTANIM_SH}")
|
||||
GEN_BOOTANIM_SH.write_text(text.replace(old, new, 1))
|
||||
print(f"patched: {GEN_BOOTANIM_SH}")
|
||||
|
||||
|
||||
def _patch_setup_welcome() -> None:
|
||||
if not SETUP_STRINGS.is_file():
|
||||
sys.exit(f"missing {SETUP_STRINGS}")
|
||||
text = SETUP_STRINGS.read_text()
|
||||
if MARKER in text:
|
||||
print(f"already: {SETUP_STRINGS} ({MARKER})")
|
||||
return
|
||||
if not WELCOME_OLD.search(text):
|
||||
sys.exit(f"setup_welcome_message anchor missing in {SETUP_STRINGS}")
|
||||
text = WELCOME_OLD.sub(WELCOME_NEW, text, count=1)
|
||||
SETUP_STRINGS.write_text(text)
|
||||
print(f"patched: {SETUP_STRINGS}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not BOOTANIM_SRC.is_dir():
|
||||
sys.exit(f"missing {BOOTANIM_SRC}")
|
||||
boot_n = _sync_tree(BOOTANIM_SRC, BOOTANIM_DST)
|
||||
over_n = _sync_tree(OVERLAY_SRC, OVERLAY_DST) if OVERLAY_SRC.is_dir() else 0
|
||||
_patch_gen_bootanimation_sh()
|
||||
_patch_setup_welcome()
|
||||
print(f"OK: bootanimation files updated={boot_n} overlay files updated={over_n}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -7,17 +7,22 @@
|
||||
- Drops the old TARGET_UNOFFICIAL_BUILD_ID insert from lineage_sunfish.mk.
|
||||
- Vendor SPL stays the LOS 23.2 2023-09-05 (AXP GOLDEN ships 2023-08-05).
|
||||
- build/make/core/config.mk only tags release-keys when the default cert is
|
||||
under vendor/lineage-priv/. Copies the project certs there (same keys, so
|
||||
an existing install updates without a wipe).
|
||||
under vendor/lineage-priv/. Copies the host-only project certs there (same
|
||||
keys, so an existing install updates without a wipe). Secrets never live in
|
||||
vendor/sunfish-a16.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path("/srv/rom/src/lineage")
|
||||
SRC_CERTS = TREE / "vendor/sunfish-a16/certs"
|
||||
SRC_CERTS = pathlib.Path(
|
||||
os.environ.get("ANDROID_CERTS_DIR", "/srv/rom/secrets/android-certs")
|
||||
)
|
||||
DST_CERTS = TREE / "vendor/lineage-priv/keys"
|
||||
OLD_ID = (
|
||||
"# sunfish-a16: must precede vendor/lineage version.mk (via inherit below)\n"
|
||||
@@ -38,9 +43,30 @@ VENDOR_SPL_ROLLBACK = (
|
||||
)
|
||||
|
||||
|
||||
def los_date() -> str | None:
|
||||
date = os.environ.get("AXP_LOS_DATE")
|
||||
pin = TREE / ".axp-los-date"
|
||||
if not date and pin.is_file():
|
||||
date = pin.read_text().strip()
|
||||
if date and not re.fullmatch(r"\d{8}", date):
|
||||
sys.exit(f"bad LOS date {date!r}, want YYYYMMDD")
|
||||
return date
|
||||
|
||||
|
||||
def main() -> None:
|
||||
version = TREE / "vendor/lineage/config/version.mk"
|
||||
text = version.read_text()
|
||||
date = los_date()
|
||||
if date:
|
||||
# Build name carries the upstream LOS nightly date, not the build day.
|
||||
text, n = re.subn(
|
||||
r"(LINEAGE_BUILD_DATE := )(\$\(shell date -u \+%Y%m%d\)|\d{8})\n",
|
||||
rf"\g<1>{date}\n",
|
||||
text,
|
||||
)
|
||||
if n != 1:
|
||||
sys.exit(f"LINEAGE_BUILD_DATE anchor missing in {version}")
|
||||
version.write_text(text)
|
||||
if "LINEAGE_BUILDTYPE := PRO\n" not in text:
|
||||
old = " LINEAGE_BUILDTYPE := UNOFFICIAL\n"
|
||||
if old not in text:
|
||||
@@ -64,6 +90,14 @@ def main() -> None:
|
||||
|
||||
if not (SRC_CERTS / "releasekey.pk8").is_file():
|
||||
sys.exit(f"missing {SRC_CERTS}/releasekey.pk8")
|
||||
avb_dir = TREE / "vendor/sunfish-a16/certs"
|
||||
avb_dir.mkdir(parents=True, exist_ok=True)
|
||||
for name in ("avb.pem", "avb_pkmd.bin"):
|
||||
src = SRC_CERTS / name
|
||||
if not src.is_file():
|
||||
sys.exit(f"missing {src}")
|
||||
shutil.copy2(src, avb_dir / name)
|
||||
|
||||
DST_CERTS.mkdir(parents=True, exist_ok=True)
|
||||
for src in SRC_CERTS.iterdir():
|
||||
if src.name.startswith("avb"):
|
||||
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
"""US-S4-2 — drop Lineage Jelly + Gallery2 to match AXP.OS PRO product surface.
|
||||
"""US-S4-2 — optional mk scrubber for Lineage Jelly/Gallery2 package lists.
|
||||
|
||||
Oracle: AXP.OS-22.2-20260324-PRO-sunfish.zip product/app has neither Jelly nor
|
||||
Gallery2 (browser stub + FossifyGallery_FD live on system_ext). See
|
||||
docs/009-axp-pro-product-apps-20260324.md.
|
||||
Ship policy (2026-10-07+, handoff): **Jelly stays** on product; gallery is
|
||||
FossifyGallery on system_ext; **Gallery2/Glimpse stay out**. That is enforced
|
||||
by vendor/sunfish-a16/hardening.mk (filter-out + PRODUCT_PACKAGES += Jelly)
|
||||
via magisk.mk — **not** by running this script on the OTA bacon path.
|
||||
|
||||
FossifyGallery already declares LOCAL_OVERRIDES_PACKAGES := Gallery Gallery2
|
||||
Glimpse, but Gallery2 still landed in product/app on the 162840Z ship — same
|
||||
class of failure as OpenSSH filter-out. Patch the Lineage PRODUCT_PACKAGES
|
||||
add sites and add belt-and-suspenders stubs.
|
||||
This file remains for one-off mk surgery if vendor/lineage re-adds Gallery2
|
||||
despite hardening.mk (e.g. "Build Gallery2 until Glimpse can edit"). Do not
|
||||
strip Jelly here when using the current ship gates.
|
||||
|
||||
Usage (from ANDROID_BUILD_TOP):
|
||||
python3 vendor/sunfish-a16/patches/009-axp-los23-port/apply_009_drop_jelly_gallery2.py
|
||||
|
||||
+54
-5
@@ -7,6 +7,11 @@ dumpstate and apexd require them. Remove only those required entries.
|
||||
pbtombstone stays in PRODUCT_HOST_PACKAGES; only the system copy goes.
|
||||
Qualcomm diag binaries and imsrcsd go. imsdatadaemon and the other IMS
|
||||
call daemons stay.
|
||||
|
||||
Never drop disable_configstore. It is the AOSP Soong override for
|
||||
android.hardware.configstore@1.1-service (base_vendor SHIPPING_API_LEVEL_29).
|
||||
Without it the HIDL HAL installs, is absent from A16 VINTF, and crash-loops
|
||||
every ~5s (LAB polish 2026-10-16). Lineage device-lineage.mk must keep it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -38,13 +43,17 @@ MAKE_DROPS: tuple[tuple[str, tuple[str, ...], tuple[str, ...]], ...] = (
|
||||
"ims_rtp_daemon",
|
||||
),
|
||||
),
|
||||
(
|
||||
"device/google/sunfish/device-lineage.mk",
|
||||
("disable_configstore",),
|
||||
("chre_daemon_msm",),
|
||||
),
|
||||
)
|
||||
|
||||
# Restored if a prior wave4 pass emptied the Configstore stanza.
|
||||
CONFIGSTORE_MK = "device/google/sunfish/device-lineage.mk"
|
||||
CONFIGSTORE_NEEDLE = "disable_configstore"
|
||||
CONFIGSTORE_BLOCK = """# Configstore
|
||||
PRODUCT_PACKAGES += \\
|
||||
disable_configstore
|
||||
|
||||
"""
|
||||
|
||||
HOST_SAFE = {"pbtombstone"}
|
||||
|
||||
BP_LINES: tuple[tuple[str, str], ...] = (
|
||||
@@ -164,6 +173,41 @@ def apply_bp(path: Path, needle: str) -> str:
|
||||
return "dropped"
|
||||
|
||||
|
||||
def ensure_disable_configstore(path: Path) -> str:
|
||||
"""Keep Lineage's override so the HIDL configstore HAL is not packaged."""
|
||||
text = path.read_text()
|
||||
if not _has(text, "chre_daemon_msm"):
|
||||
raise SystemExit(f"FAIL: {path} lost chre_daemon_msm")
|
||||
if _has(text, CONFIGSTORE_NEEDLE):
|
||||
return "already"
|
||||
# Prior wave4 left an empty "# Configstore / PRODUCT_PACKAGES +=" block.
|
||||
empty = (
|
||||
"# Configstore\n"
|
||||
"PRODUCT_PACKAGES += \\\n"
|
||||
"\n"
|
||||
)
|
||||
if empty in text:
|
||||
new = text.replace(
|
||||
empty,
|
||||
"# Configstore\nPRODUCT_PACKAGES += \\\n disable_configstore\n\n",
|
||||
1,
|
||||
)
|
||||
elif "# Configstore\n" in text:
|
||||
raise SystemExit(f"FAIL: {path} has Configstore stanza but no restore site")
|
||||
else:
|
||||
# Insert before CHRE block (Lineage stock order).
|
||||
marker = "# CHRE\n"
|
||||
if marker not in text:
|
||||
raise SystemExit(f"FAIL: {path} missing CHRE marker for Configstore insert")
|
||||
new = text.replace(marker, CONFIGSTORE_BLOCK + marker, 1)
|
||||
if not _has(new, CONFIGSTORE_NEEDLE):
|
||||
raise SystemExit(f"FAIL: could not restore {CONFIGSTORE_NEEDLE} in {path}")
|
||||
if not _has(new, "chre_daemon_msm"):
|
||||
raise SystemExit(f"FAIL: edit would drop chre_daemon_msm from {path}")
|
||||
path.write_text(new)
|
||||
return "restored"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
results: list[str] = []
|
||||
@@ -173,6 +217,11 @@ def main() -> int:
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
results.append(f"{apply_make(path, packages, keep)} {rel}")
|
||||
cfg = top / CONFIGSTORE_MK
|
||||
if not cfg.is_file():
|
||||
print(f"FAIL: missing {cfg}", file=sys.stderr)
|
||||
return 1
|
||||
results.append(f"{ensure_disable_configstore(cfg)} {CONFIGSTORE_MK}")
|
||||
pb = top / "build/make/target/product/base_system.mk"
|
||||
if not pb.is_file():
|
||||
print(f"FAIL: missing {pb}", file=sys.stderr)
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Guard GoogleParts EuiccDisabler before setApplicationEnabledSetting.
|
||||
|
||||
After wave1 drops com.google.android.euicc, Lineage GoogleParts still walks
|
||||
EUICC_PACKAGES and calls setApplicationEnabledSetting unconditionally →
|
||||
IllegalArgumentException: Unknown package: com.google.android.euicc.
|
||||
|
||||
Match EuiccPolicy's pattern: only touch packages that are installed.
|
||||
Do not rename targets to OpenEUICC and do not disable OpenEUICC.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REL = "device/google/sunfish/parts/src/org/lineageos/settings/EuiccDisabler.java"
|
||||
|
||||
OLD = """ public static void enableOrDisableEuicc(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
boolean disable = shouldDisable(pm);
|
||||
int flag = disable
|
||||
? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
: PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
|
||||
for (String pkg : EUICC_PACKAGES) {
|
||||
pm.setApplicationEnabledSetting(pkg, flag, 0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
NEW = """ private static boolean isInstalled(PackageManager pm, String pkgName) {
|
||||
try {
|
||||
pm.getPackageInfo(pkgName, 0);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableOrDisableEuicc(Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
boolean disable = shouldDisable(pm);
|
||||
int flag = disable
|
||||
? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
: PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
|
||||
for (String pkg : EUICC_PACKAGES) {
|
||||
// Skip missing packages (e.g. EuiccGoogle after wave1 drop).
|
||||
// Do not retarget to OpenEUICC; leave im.angry.openeuicc alone.
|
||||
if (!isInstalled(pm, pkg)) {
|
||||
continue;
|
||||
}
|
||||
pm.setApplicationEnabledSetting(pkg, flag, 0);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
path = top / REL
|
||||
if not path.is_file():
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
text = path.read_text()
|
||||
if "Skip missing packages (e.g. EuiccGoogle after wave1 drop)" in text:
|
||||
print(f"OK: already {REL}")
|
||||
return 0
|
||||
if OLD not in text:
|
||||
print(f"FAIL: enableOrDisableEuicc block not found in {path}", file=sys.stderr)
|
||||
return 1
|
||||
path.write_text(text.replace(OLD, NEW, 1))
|
||||
if "Skip missing packages (e.g. EuiccGoogle after wave1 drop)" not in path.read_text():
|
||||
print(f"FAIL: patch did not land in {path}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"OK: patched {REL}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wire AXP device makefiles into LOS sunfish (product + board).
|
||||
|
||||
repo sync / forall reset drops uncommitted device/*.mk edits. Re-apply every
|
||||
bacon:
|
||||
|
||||
* lineage_sunfish.mk → gms.mk + magisk.mk + apps.mk (Aurora, Droidify, …)
|
||||
* BoardConfigLineage.mk → vendor/sunfish-a16/BoardConfig.mk (AVB, super size)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
TREE = pathlib.Path(os.environ.get("SUNFISH_A16_TREE", "/srv/rom/src/lineage"))
|
||||
PRODUCT_MK = TREE / "device/google/sunfish/lineage_sunfish.mk"
|
||||
BOARD_MK = TREE / "device/google/sunfish/BoardConfigLineage.mk"
|
||||
MARKER_PRODUCT = "sunfish-a16-009-product-includes"
|
||||
MARKER_BOARD = "sunfish-a16-009-board-config"
|
||||
PRODUCT_ANCHOR = "$(call inherit-product, vendor/google/sunfish/sunfish-vendor.mk)\n"
|
||||
PRODUCT_BLOCK = (
|
||||
"\n"
|
||||
f"# {MARKER_PRODUCT}\n"
|
||||
"$(call inherit-product-if-exists, vendor/sunfish-a16/gms.mk)\n"
|
||||
"$(call inherit-product-if-exists, vendor/sunfish-a16/magisk.mk)\n"
|
||||
"$(call inherit-product-if-exists, vendor/sunfish-a16/apps.mk)\n"
|
||||
)
|
||||
BOARD_ANCHOR = "include vendor/google/sunfish/BoardConfigVendor.mk\n"
|
||||
BOARD_BLOCK = (
|
||||
f"\n# {MARKER_BOARD}\n"
|
||||
"-include vendor/sunfish-a16/BoardConfig.mk\n"
|
||||
)
|
||||
|
||||
|
||||
APPS_INHERIT = "$(call inherit-product-if-exists, vendor/sunfish-a16/apps.mk)\n"
|
||||
MAGISK_INHERIT = "$(call inherit-product-if-exists, vendor/sunfish-a16/magisk.mk)\n"
|
||||
|
||||
|
||||
def insert_once(path: pathlib.Path, anchor: str, block: str, marker: str, label: str) -> None:
|
||||
text = path.read_text()
|
||||
if marker in text:
|
||||
print(f"OK: {label} already present")
|
||||
return
|
||||
if anchor not in text:
|
||||
sys.exit(f"anchor missing in {path}")
|
||||
path.write_text(text.replace(anchor, anchor + block, 1))
|
||||
print(f"OK: {label} applied")
|
||||
|
||||
|
||||
def ensure_apps_mk_in_product(path: pathlib.Path) -> None:
|
||||
"""Marker can exist from an older block without apps.mk; repair without duplicating."""
|
||||
text = path.read_text()
|
||||
if MARKER_PRODUCT not in text or APPS_INHERIT.strip() in text:
|
||||
return
|
||||
if MAGISK_INHERIT not in text:
|
||||
sys.exit(f"{path}: {MARKER_PRODUCT} present but magisk.mk inherit missing")
|
||||
path.write_text(text.replace(MAGISK_INHERIT, MAGISK_INHERIT + APPS_INHERIT, 1))
|
||||
print("OK: repaired product includes (added apps.mk)")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
insert_once(PRODUCT_MK, PRODUCT_ANCHOR, PRODUCT_BLOCK, MARKER_PRODUCT, "product includes")
|
||||
ensure_apps_mk_in_product(PRODUCT_MK)
|
||||
insert_once(BOARD_MK, BOARD_ANCHOR, BOARD_BLOCK, MARKER_BOARD, "board config include")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Silence false pixel-thermal CDEV ERROR on sunfish split thermal stack.
|
||||
|
||||
sunfish ships CoolingDevices in thermal_info_config for Android reporting, but
|
||||
does not BindedCdev/CdevRequest any of them — Qualcomm thermal-engine owns
|
||||
CPU/GPU/FCC/modem mitigation. Newer pixel thermal HAL still calls
|
||||
logCoolingDeviceStatus() for every CoolingDevice and getCdevMaxRequest()
|
||||
ERROR-spams when the HAL vote map is empty (no sensor binds that cdev).
|
||||
|
||||
Only sunfish among device/google/*/thermal_info_config*.json has CoolingDevices
|
||||
without BindedCdev. Skip unbound cdevs in the periodic CDEV log so both stacks
|
||||
can run in parallel without ERROR noise. Real throttling paths still ERROR if a
|
||||
binded cdev is missing from the vote map.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REL = "hardware/google/pixel/thermal/utils/thermal_throttling.cpp"
|
||||
|
||||
OLD = """void ThermalThrottling::logCoolingDeviceStatus(
|
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
|
||||
int max_state = 0;
|
||||
std::ostringstream cdev_log;
|
||||
for (const auto &[cdev_name, cdev_info] : cooling_device_info_map) {
|
||||
if (getCdevMaxRequest(cdev_name, &max_state)) {
|
||||
ATRACE_INT((cdev_name + std::string("-state")).c_str(), max_state);
|
||||
if (!cdev_info.apply_powercap) {
|
||||
cdev_log << cdev_name << " state:" << max_state << " ";
|
||||
} else {
|
||||
const auto budget = static_cast<int>(
|
||||
std::lround(cdev_info.state2power[max_state] / cdev_info.multiplier));
|
||||
cdev_log << cdev_name << " state:" << max_state << ",budget:" << budget << " ";
|
||||
ATRACE_INT((cdev_name + std::string("-budget")).c_str(), budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "CDEV log " << cdev_log.str();
|
||||
}
|
||||
"""
|
||||
|
||||
NEW = """void ThermalThrottling::logCoolingDeviceStatus(
|
||||
const std::unordered_map<std::string, CdevInfo> &cooling_device_info_map) {
|
||||
int max_state = 0;
|
||||
std::ostringstream cdev_log;
|
||||
for (const auto &[cdev_name, cdev_info] : cooling_device_info_map) {
|
||||
// Sunfish (and any device that lists CoolingDevices for Android
|
||||
// reporting while an external mitigator votes): HAL vote map has no
|
||||
// entry. Do not call getCdevMaxRequest — it would ERROR every ~5 min.
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> _lock(cdev_all_request_map_mutex_);
|
||||
if (!cdev_all_request_map_.count(cdev_name)) {
|
||||
LOG(VERBOSE) << "Cooling device [" << cdev_name
|
||||
<< "] has no HAL throttle votes (external mitigator)";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (getCdevMaxRequest(cdev_name, &max_state)) {
|
||||
ATRACE_INT((cdev_name + std::string("-state")).c_str(), max_state);
|
||||
if (!cdev_info.apply_powercap) {
|
||||
cdev_log << cdev_name << " state:" << max_state << " ";
|
||||
} else {
|
||||
const auto budget = static_cast<int>(
|
||||
std::lround(cdev_info.state2power[max_state] / cdev_info.multiplier));
|
||||
cdev_log << cdev_name << " state:" << max_state << ",budget:" << budget << " ";
|
||||
ATRACE_INT((cdev_name + std::string("-budget")).c_str(), budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "CDEV log " << cdev_log.str();
|
||||
}
|
||||
"""
|
||||
|
||||
MARKER = "has no HAL throttle votes (external mitigator)"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
top = Path(os.environ.get("ANDROID_BUILD_TOP", ".")).resolve()
|
||||
path = top / REL
|
||||
if not path.is_file():
|
||||
print(f"FAIL: missing {path}", file=sys.stderr)
|
||||
return 1
|
||||
text = path.read_text()
|
||||
if MARKER in text:
|
||||
print(f"OK: already {REL}")
|
||||
return 0
|
||||
if OLD not in text:
|
||||
print(f"FAIL: logCoolingDeviceStatus block not found in {path}", file=sys.stderr)
|
||||
return 1
|
||||
path.write_text(text.replace(OLD, NEW, 1))
|
||||
if MARKER not in path.read_text():
|
||||
print(f"FAIL: patch did not land in {path}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"OK: patched {REL}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -62,6 +62,6 @@ FossifyGallery present.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- `CONFIG_SECURITY_YAMA` (kernel / US-S5 — **done** on `162840Z`)
|
||||
- `CONFIG_SECURITY_YAMA` (kernel / US-S5 — unset like GOLDEN since 2026-10-10; scope 2 broke PairIP)
|
||||
- Switching lunch to `user` (US-S6)
|
||||
- NeoLauncher / Aosmium (wont / exception)
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/bin/bash
|
||||
# US-S5-1 — enable remaining Graphene/AXP-class defconfig knobs on msm-4.14.
|
||||
# US-S5-1 — remaining Graphene/AXP-class defconfig knobs on msm-4.14.
|
||||
#
|
||||
# Oracle: AXP/Graphene kernel hardening expects Yama + slab freelist random
|
||||
# (combo4 already ships SLAB_FREELIST_HARDENED). LOS sunfish_defconfig left both
|
||||
# off; CONFIG_LSM already lists yama but SECURITY_YAMA was unset so the LSM
|
||||
# never built.
|
||||
# SLAB_FREELIST_RANDOM: LOS sunfish_defconfig leaves it off (combo4 already
|
||||
# ships SLAB_FREELIST_HARDENED).
|
||||
#
|
||||
# SECURITY_YAMA: GOLDEN's running 4.14.355 config leaves it unset, so AXP's
|
||||
# init ptrace_scope writes are inert there. With Yama built, scope 2 denies
|
||||
# PairIP's self-ptrace (Wallapop SIGSEGV, LAB 2026-10-10). Match GOLDEN.
|
||||
#
|
||||
# Does NOT apply high-offset CVE leftovers (nf_tables deliberately skipped).
|
||||
# CVE-2022-49980's one-liner stays on the denylist. The bacon applies
|
||||
@@ -20,16 +22,15 @@ set -euo pipefail
|
||||
K=${1:?kernel tree}
|
||||
DEF="$K/arch/arm64/configs/sunfish_defconfig"
|
||||
test -f "$DEF"
|
||||
test -f "$K/security/yama/Kconfig"
|
||||
|
||||
for k in SECURITY_YAMA SLAB_FREELIST_RANDOM USERFAULTFD HAVE_ARCH_USERFAULTFD_MINOR; do
|
||||
sed -i "/CONFIG_${k}/d" "$DEF"
|
||||
done
|
||||
|
||||
{
|
||||
echo "CONFIG_SECURITY_YAMA=y"
|
||||
echo "# CONFIG_SECURITY_YAMA is not set"
|
||||
echo "CONFIG_SLAB_FREELIST_RANDOM=y"
|
||||
echo "# CONFIG_USERFAULTFD is not set"
|
||||
} >> "$DEF"
|
||||
|
||||
echo "OK: US-S5-1 defconfig (YAMA + SLAB_FREELIST_RANDOM, USERFAULTFD off) on $K"
|
||||
echo "OK: US-S5-1 defconfig (SLAB_FREELIST_RANDOM; YAMA, USERFAULTFD off) on $K"
|
||||
@@ -1,4 +1,4 @@
|
||||
# US-S5-1 — Yama + SLAB freelist random (defconfig)
|
||||
# US-S5-1 — SLAB freelist random (defconfig); Yama off like GOLDEN
|
||||
|
||||
**Track**: AXP Resurection Pro Magisk, unlocked sunfish.
|
||||
**On top of**: Stage1 + combo4 + FORTIFY + CVE clean-429 + hi-batch1–3.
|
||||
@@ -12,9 +12,17 @@ quarantine:
|
||||
|
||||
| Knob | Before | After | Attribution |
|
||||
|------|--------|-------|-------------|
|
||||
| `CONFIG_SECURITY_YAMA` | unset (but `CONFIG_LSM` listed yama) | `=y` | Graphene/AXP LSM stack; ptrace scope |
|
||||
| `CONFIG_SECURITY_YAMA` | unset (but `CONFIG_LSM` listed yama) | unset | GOLDEN 4.14.355 config: not set |
|
||||
| `CONFIG_SLAB_FREELIST_RANDOM` | unset | `=y` | Graphene/AH slab hardening companion to existing `SLAB_FREELIST_HARDENED` |
|
||||
|
||||
Yama was `=y` from `162840Z` until 2026-10-10. With `apply_009_yama_ptrace.py`
|
||||
init set `ptrace_scope=2`, which denies PairIP's self-ptrace. Wallapop then
|
||||
died with SIGSEGV right after `libpairipcore.so` loaded. dmesg showed
|
||||
`ptrace attach of "com.wallapop"[…] was attempted by "com.wallapop"[…]`.
|
||||
With `persist.native_debug=true` (scope 0), LAB passed PairIP. GOLDEN has no
|
||||
Yama, so AXP's init writes are inert there. The AXP init/sepolicy patch stays,
|
||||
inert in the same way.
|
||||
|
||||
## Apply
|
||||
|
||||
```bash
|
||||
@@ -28,17 +36,12 @@ Stage1+combo4+CVE apply.
|
||||
## LAB verify
|
||||
|
||||
```text
|
||||
zcat /proc/config.gz | grep CONFIG_SECURITY_YAMA=y
|
||||
zcat /proc/config.gz | grep 'CONFIG_SECURITY_YAMA is not set'
|
||||
zcat /proc/config.gz | grep CONFIG_SLAB_FREELIST_RANDOM=y
|
||||
cat /proc/sys/kernel/yama/ptrace_scope # expect 2 (ship policy)
|
||||
ls /proc/sys/kernel/yama # expect: no such file
|
||||
pidof magiskd # still up
|
||||
```
|
||||
|
||||
The ship script now applies `apply_009_yama_ptrace.py`, so the current LAB
|
||||
policy is `ptrace_scope=2`, not the kernel default `1`. Matching AXP's
|
||||
`persist.native_debug` 0/2 toggle is a separate userspace change and must not
|
||||
be mixed into the CVE-2022-49980 boot test.
|
||||
|
||||
## Out of scope here
|
||||
|
||||
- The original `CVE-2022-49980/ANY/0001.patch` remains denylisted. The next
|
||||
|
||||
+23
-11
@@ -57,28 +57,42 @@ def rewrite_region_comment(bp: pathlib.Path) -> None:
|
||||
print(f"region comment: {bp}")
|
||||
|
||||
|
||||
def _git(dest: pathlib.Path, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
|
||||
cmd = ["git", "-c", f"safe.directory={dest}", "-C", str(dest), *args]
|
||||
if check:
|
||||
return subprocess.run(cmd, check=True, text=True, capture_output=True)
|
||||
return subprocess.run(cmd, text=True, capture_output=True)
|
||||
|
||||
|
||||
def ensure_library() -> None:
|
||||
dest = TREE / "external/hardened_malloc"
|
||||
if (dest / ".git").is_dir():
|
||||
head = subprocess.check_output(
|
||||
["git", "-C", dest, "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
head = _git(dest, "rev-parse", "HEAD").stdout.strip()
|
||||
bp_text = (dest / "Android.bp").read_text()
|
||||
if head == PIN and MARKER in bp_text and REGION_39BIT in bp_text:
|
||||
rewrite_region_comment(dest / "Android.bp")
|
||||
print(f"hardened_malloc already at {PIN}")
|
||||
return
|
||||
subprocess.check_call(["git", "-C", dest, "fetch", "--depth", "1", "origin", PIN])
|
||||
subprocess.check_call(["git", "-C", dest, "checkout", "--detach", PIN])
|
||||
_git(dest, "fetch", "--depth", "1", "origin", PIN)
|
||||
_git(dest, "checkout", "--detach", PIN)
|
||||
else:
|
||||
if dest.exists():
|
||||
sys.exit(f"{dest} exists and is not a git checkout")
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.check_call(
|
||||
["git", "clone", "--depth", "1", REPO, str(dest)]
|
||||
[
|
||||
"git",
|
||||
"-c",
|
||||
f"safe.directory={dest}",
|
||||
"clone",
|
||||
"--depth",
|
||||
"1",
|
||||
REPO,
|
||||
str(dest),
|
||||
]
|
||||
)
|
||||
subprocess.check_call(["git", "-C", dest, "fetch", "--depth", "1", "origin", PIN])
|
||||
subprocess.check_call(["git", "-C", dest, "checkout", "--detach", PIN])
|
||||
_git(dest, "fetch", "--depth", "1", "origin", PIN)
|
||||
_git(dest, "checkout", "--detach", PIN)
|
||||
bp = dest / "Android.bp"
|
||||
text = bp.read_text()
|
||||
mte = """ device_has_arm_mte: {
|
||||
@@ -106,9 +120,7 @@ def ensure_library() -> None:
|
||||
1,
|
||||
)
|
||||
bp.write_text(text)
|
||||
head = subprocess.check_output(
|
||||
["git", "-C", dest, "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
head = _git(dest, "rev-parse", "HEAD").stdout.strip()
|
||||
if head != PIN:
|
||||
sys.exit(f"hardened_malloc HEAD {head} != {PIN}")
|
||||
print(f"hardened_malloc {PIN}")
|
||||
|
||||
Reference in new issue
Block a user