diff --git a/launch_env.sh b/launch_env.sh index ff05fc7d0..7e04f65ff 100755 --- a/launch_env.sh +++ b/launch_env.sh @@ -21,7 +21,7 @@ fi export QCOM_PRIORITY=12 if [ -z "$AGNOS_VERSION" ]; then - export AGNOS_VERSION="12.8.17" + export AGNOS_VERSION="12.8.18" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index 00f6c017a..17d5f1d51 100644 --- a/system/hardware/tici/agnos.json +++ b/system/hardware/tici/agnos.json @@ -67,9 +67,9 @@ }, { "name": "system", - "url": "https://www.dropbox.com/scl/fi/gjhc393yvcza4etsv67us/system3.img.xz?rlkey=yrrwvlw2tljrwu5ullicc1oot&st=9d6ptx4x&dl=1", - "hash": "f144e595f907b06b7d583c72563358f49567817de5dfa4f07207e1aeab9af7fd", - "hash_raw": "f144e595f907b06b7d583c72563358f49567817de5dfa4f07207e1aeab9af7fd", + "url": "https://www.dropbox.com/scl/fi/3f8viuu3f0t29wxs8vvkj/system4.img.xz?rlkey=rtynoyi38nnbguj23ar0km42f&st=cn7crvdd&dl=1", + "hash": "ced84edefbb2e917cb60e192701920a8c5fd01014b5da640191092985804a287", + "hash_raw": "ced84edefbb2e917cb60e192701920a8c5fd01014b5da640191092985804a287", "size": 5368709120, "sparse": false, "full_check": false, diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 6c957e338..22f5cd755 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -40,6 +40,7 @@ def python_ui_process_start_method(uses_python_ui: bool, is_pc: bool = PC) -> st PYTHON_UI = python_ui_enabled(device_type) PYTHON_UI_PROCESS_START_METHOD = python_ui_process_start_method(PYTHON_UI) THE_GALAXY_PROCESS_START_METHOD = "fork" if PC else "subprocess" +UPDATED_PROCESS_START_METHOD = "fork" if PC else "subprocess" from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess @@ -156,7 +157,13 @@ procs = [ PythonProcess("radard", "selfdrive.controls.radard", only_onroad), PythonProcess("hardwared", "system.hardware.hardwared", always_run), PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC), - PythonProcess("updated", "system.updated.updated", always_run, enabled=not PC), + PythonProcess( + "updated", + "system.updated.updated", + always_run, + enabled=not PC, + start_method=UPDATED_PROCESS_START_METHOD, + ), PythonProcess("uploader", "system.loggerd.uploader", allow_uploads), PythonProcess("statsd", "system.statsd", always_run), PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad), diff --git a/system/manager/test/test_manager.py b/system/manager/test/test_manager.py index 9b4611fb3..b23547ca4 100644 --- a/system/manager/test/test_manager.py +++ b/system/manager/test/test_manager.py @@ -125,7 +125,7 @@ class TestManager: def test_python_ui_subprocess_is_scoped_to_ui(self): ui_proc = managed_processes["ui"] uses_python_ui = python_ui_enabled(HARDWARE.get_device_type()) - subprocess_scoped_procs = {"the_galaxy"} + subprocess_scoped_procs = {"the_galaxy", "updated"} assert isinstance(ui_proc, PythonProcess) == uses_python_ui if uses_python_ui: diff --git a/system/ui/reset.py b/system/ui/reset.py index c32504a5b..af47693c0 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -1,14 +1,69 @@ #!/usr/bin/env python3 -from openpilot.system.ui.lib.application import gui_app -import openpilot.system.ui.tici_reset as tici_reset -import openpilot.system.ui.mici_reset as mici_reset +from pathlib import Path + +from openpilot.system.hardware import HARDWARE + +BIG_UI_DEVICE_TYPES = ("tici", "tizi") +SMALL_UI_DEVICE_TYPE = "mici" + + +def _normalize_device_type(raw: str) -> str: + device_type = raw.replace("\x00", "").strip().lower() + if "comma " in device_type: + device_type = device_type.rsplit("comma ", 1)[-1].strip() + return device_type + + +def _device_tree_device_type() -> str | None: + model_path = Path("/sys/firmware/devicetree/base/model") + if not model_path.is_file(): + return None + + try: + return _normalize_device_type(model_path.read_text(encoding="utf-8", errors="ignore")) + except Exception: + return None + + +def _reported_device_type() -> str | None: + try: + return _normalize_device_type(HARDWARE.get_device_type()) + except Exception: + return None + + +def _ui_device_type() -> str: + device_tree_type = _device_tree_device_type() + if device_tree_type in BIG_UI_DEVICE_TYPES: + return device_tree_type + if device_tree_type: + return SMALL_UI_DEVICE_TYPE + + reported_type = _reported_device_type() + if reported_type in BIG_UI_DEVICE_TYPES: + return reported_type + return SMALL_UI_DEVICE_TYPE + + +def _patch_hardware_device_type(device_type: str) -> None: + HARDWARE.get_device_type = lambda: device_type + try: + import openpilot.system.hardware.tici.hardware as tici_hardware + tici_hardware.get_device_type = lambda: device_type + except Exception: + pass def main(): - if gui_app.big_ui(): - tici_reset.main() + device_type = _ui_device_type() + _patch_hardware_device_type(device_type) + + if device_type in BIG_UI_DEVICE_TYPES: + import openpilot.system.ui.tici_reset as reset_impl else: - mici_reset.main() + import openpilot.system.ui.mici_reset as reset_impl + + reset_impl.main() if __name__ == "__main__": diff --git a/system/ui/tests/test_reset.py b/system/ui/tests/test_reset.py new file mode 100644 index 000000000..70d423c50 --- /dev/null +++ b/system/ui/tests/test_reset.py @@ -0,0 +1,42 @@ +import importlib +import sys +import types +from unittest.mock import patch + +import pytest + + +@pytest.fixture +def reset_module(monkeypatch): + hardware = types.ModuleType("openpilot.system.hardware") + hardware.HARDWARE = types.SimpleNamespace(get_device_type=lambda: "tici") + monkeypatch.setitem(sys.modules, "openpilot.system.hardware", hardware) + sys.modules.pop("openpilot.system.ui.reset", None) + module = importlib.import_module("openpilot.system.ui.reset") + yield module + sys.modules.pop("openpilot.system.ui.reset", None) + + +def test_device_tree_tici_uses_big_ui(reset_module): + with patch.object(reset_module, "_device_tree_device_type", return_value="tici"), \ + patch.object(reset_module, "_reported_device_type", return_value="mici"): + assert reset_module._ui_device_type() == "tici" + + +def test_device_tree_tizi_uses_big_ui(reset_module): + with patch.object(reset_module, "_device_tree_device_type", return_value="tizi"), \ + patch.object(reset_module, "_reported_device_type", return_value="mici"): + assert reset_module._ui_device_type() == "tizi" + + +@pytest.mark.parametrize("device_tree_type", ["mici", "comma 4", "comma four"]) +def test_non_tici_device_tree_uses_small_ui(reset_module, device_tree_type): + with patch.object(reset_module, "_device_tree_device_type", return_value=device_tree_type), \ + patch.object(reset_module, "_reported_device_type", return_value="tici"): + assert reset_module._ui_device_type() == "mici" + + +def test_reported_tici_used_when_no_device_tree(reset_module): + with patch.object(reset_module, "_device_tree_device_type", return_value=None), \ + patch.object(reset_module, "_reported_device_type", return_value="tici"): + assert reset_module._ui_device_type() == "tici" diff --git a/system/updated/updated.py b/system/updated/updated.py index 8e7c8890c..b7c4c45fe 100644 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -26,6 +26,7 @@ from openpilot.starpilot.common.starpilot_variables import BACKUP_PATH, get_star LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock") STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging") +GIT_CLEANUP_TIMEOUT = int(os.getenv("UPDATER_GIT_CLEANUP_TIMEOUT", "30")) OVERLAY_UPPER = os.path.join(STAGING_ROOT, "upper") OVERLAY_METADATA = os.path.join(STAGING_ROOT, "metadata") @@ -233,10 +234,12 @@ def finalize_update() -> None: cloudlog.info("Starting git cleanup in finalized update") t = time.monotonic() try: - run(["git", "gc"], FINALIZED) - run(["git", "lfs", "prune"], FINALIZED) + subprocess.run(["git", "gc", "--auto"], cwd=FINALIZED, check=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, encoding="utf8", timeout=GIT_CLEANUP_TIMEOUT) + subprocess.run(["git", "lfs", "prune"], cwd=FINALIZED, check=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, encoding="utf8", timeout=GIT_CLEANUP_TIMEOUT) cloudlog.event("Done git cleanup", duration=time.monotonic() - t) - except subprocess.CalledProcessError: + except (subprocess.CalledProcessError, subprocess.TimeoutExpired): cloudlog.exception(f"Failed git cleanup, took {time.monotonic() - t:.3f} s") set_consistent_flag(True) @@ -492,9 +495,6 @@ def main() -> None: update_failed_count = 0 # TODO: Load from param? wait_helper = WaitTimeHelper() - # invalidate old finalized update - set_consistent_flag(False) - # set initial state params.put("UpdaterState", "idle") diff --git a/tools/agnos/patch_system_reset_image.py b/tools/agnos/patch_system_reset_image.py index 017832ca3..353d4ad9a 100644 --- a/tools/agnos/patch_system_reset_image.py +++ b/tools/agnos/patch_system_reset_image.py @@ -863,6 +863,8 @@ def zipapp_has_markers(data: bytes) -> bool: app_script = z.read(APPLICATION_ENTRY_IN_ZIPAPP) return ( PATCH_MARKER.encode() in reset_script + and b"_device_tree_device_type" in reset_script + and b"gui_app.big_ui()" not in reset_script and TICI_RESET_PATCH_MARKER.encode() in tici_reset_script and MICI_RESET_PATCH_MARKER.encode() in mici_reset_script and APP_PATCH_MARKER.encode() in app_script