diff --git a/launch_env.sh b/launch_env.sh index 3ad10c881..ff05fc7d0 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.16" + export AGNOS_VERSION="12.8.17" fi export STAGING_ROOT="/data/safe_staging" diff --git a/system/hardware/tici/agnos.json b/system/hardware/tici/agnos.json index ce4bce3e9..00f6c017a 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/uglf3j1ne3pqtlx8j4mzp/system2.img.xz?rlkey=t1dp8rmyv6kuiyagy4p02ah6q&st=iai09tjz&dl=1", - "hash": "06641f9fb602e6b046d1a619cc43dbaa724ff5c3700a32f1ef6677e40146359e", - "hash_raw": "06641f9fb602e6b046d1a619cc43dbaa724ff5c3700a32f1ef6677e40146359e", + "url": "https://www.dropbox.com/scl/fi/gjhc393yvcza4etsv67us/system3.img.xz?rlkey=yrrwvlw2tljrwu5ullicc1oot&st=9d6ptx4x&dl=1", + "hash": "f144e595f907b06b7d583c72563358f49567817de5dfa4f07207e1aeab9af7fd", + "hash_raw": "f144e595f907b06b7d583c72563358f49567817de5dfa4f07207e1aeab9af7fd", "size": 5368709120, "sparse": false, "full_check": false, diff --git a/system/ui/tests/test_updater.py b/system/ui/tests/test_updater.py new file mode 100644 index 000000000..700b77d8c --- /dev/null +++ b/system/ui/tests/test_updater.py @@ -0,0 +1,53 @@ +import importlib +import sys +import types +from unittest.mock import patch + +import pytest + + +@pytest.fixture +def updater_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.updater", None) + module = importlib.import_module("openpilot.system.ui.updater") + yield module + sys.modules.pop("openpilot.system.ui.updater", None) + + +def test_device_tree_tici_uses_big_ui(updater_module): + with patch.object(updater_module, "_device_tree_device_type", return_value="tici"), \ + patch.object(updater_module, "_framebuffer_size", return_value=(536, 240)), \ + patch.object(updater_module, "_reported_device_type", return_value="mici"): + assert updater_module._ui_device_type() == "tici" + + +def test_device_tree_tizi_uses_big_ui(updater_module): + with patch.object(updater_module, "_device_tree_device_type", return_value="tizi"), \ + patch.object(updater_module, "_framebuffer_size", return_value=(536, 240)), \ + patch.object(updater_module, "_reported_device_type", return_value="mici"): + assert updater_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(updater_module, device_tree_type): + with patch.object(updater_module, "_device_tree_device_type", return_value=device_tree_type), \ + patch.object(updater_module, "_framebuffer_size", return_value=(2160, 1080)), \ + patch.object(updater_module, "_reported_device_type", return_value="tici"): + assert updater_module._ui_device_type() == "mici" + + +def test_small_framebuffer_uses_small_ui_without_device_tree(updater_module): + with patch.object(updater_module, "_device_tree_device_type", return_value=None), \ + patch.object(updater_module, "_framebuffer_size", return_value=(536, 240)), \ + patch.object(updater_module, "_reported_device_type", return_value="tici"): + assert updater_module._ui_device_type() == "mici" + + +def test_reported_tici_used_when_no_other_signal(updater_module): + with patch.object(updater_module, "_device_tree_device_type", return_value=None), \ + patch.object(updater_module, "_framebuffer_size", return_value=None), \ + patch.object(updater_module, "_reported_device_type", return_value="tici"): + assert updater_module._ui_device_type() == "tici" diff --git a/system/ui/updater.py b/system/ui/updater.py index a894dd18a..6421c7701 100755 --- a/system/ui/updater.py +++ b/system/ui/updater.py @@ -3,6 +3,27 @@ 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 _framebuffer_size() -> tuple[int, int] | None: fb_path = Path("/sys/class/graphics/fb0/virtual_size") @@ -17,12 +38,40 @@ def _framebuffer_size() -> tuple[int, int] | None: 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: - reported_type = HARDWARE.get_device_type() + # AGNOS' baked-in updater can run before the StarPilot wrapper gets a chance to + # route mici devices through the small UI. Trust the device tree first and only + # allow known large-screen devices to select the tici updater. + 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 + fb_size = _framebuffer_size() if fb_size is not None and max(fb_size) < 1000: - return "mici" - return reported_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(): @@ -30,9 +79,9 @@ def main(): # The updater imports application sizing during module import, so patch the # hardware probe before importing either UI implementation. - HARDWARE.get_device_type = lambda: device_type + _patch_hardware_device_type(device_type) - if device_type in ("tici", "tizi"): + if device_type in BIG_UI_DEVICE_TYPES: import openpilot.system.ui.tici_updater as updater_impl else: import openpilot.system.ui.mici_updater as updater_impl diff --git a/tools/agnos/patch_system_reset_image.py b/tools/agnos/patch_system_reset_image.py index 8ed3d9880..017832ca3 100644 --- a/tools/agnos/patch_system_reset_image.py +++ b/tools/agnos/patch_system_reset_image.py @@ -784,6 +784,11 @@ def patch_updater_zipapp(original: bytes) -> bytes: if payload != replacement: payload = replacement changed = True + elif info.filename == APPLICATION_ENTRY_IN_ZIPAPP: + patched_payload = patch_application_script(payload) + if patched_payload != payload: + payload = patched_payload + changed = True new_info = zipfile.ZipInfo(info.filename, info.date_time) new_info.compress_type = zipfile.ZIP_DEFLATED @@ -891,9 +896,10 @@ def updater_zipapp_has_expected_content(data: bytes) -> bool: with zipfile.ZipFile(BytesIO(zip_payload), "r") as z: try: updater_script = z.read(UPDATER_ENTRY_IN_ZIPAPP) + app_script = z.read(APPLICATION_ENTRY_IN_ZIPAPP) except KeyError: return False - return updater_script == patch_updater_module() + return updater_script == patch_updater_module() and APP_PATCH_MARKER.encode() in app_script def parse_inode(debugfs_output: str) -> int: