mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-06 20:43:46 +08:00
StarPort: Remember which version reply each v4 head unit started with
This commit is contained in:
@@ -146,6 +146,7 @@ DEFAULT_CONFIG = {
|
||||
"phone_class": True, # while pairing/connecting, present as a phone: HFP gateway + smartphone Class of Device
|
||||
"auto_connect": True, # start projection on its own when the chosen car is on (onroad, or it reaches the comma)
|
||||
"rfcomm_cache": {}, # car address -> Android Auto RFCOMM channel learned over SDP, to skip discovery next time
|
||||
"modern_reply_cache": {}, # car address -> True when its v4+ head unit only started after the phone-style version reply
|
||||
"car_type": "", # wireless setup shown in Galaxy: "builtin" or "adapter"; "" = work it out from the saved companion car
|
||||
"car_profile_override": "", # "" = pick by fingerprint (a wireless adapter must choose); "default" = standard; otherwise a car_profiles id
|
||||
"car_fingerprint": {}, # what the chosen car's head unit reported last time, used to pick a profile before it connects
|
||||
@@ -193,6 +194,8 @@ def load_config(path: Path | None = None) -> dict:
|
||||
config["render_profile_kb"] = max(16, min(4096, int(config["render_profile_kb"])))
|
||||
config["rfcomm_cache"] = {str(address).upper(): channel for address, channel in config["rfcomm_cache"].items()
|
||||
if type(channel) is int and 1 <= channel <= 30}
|
||||
config["modern_reply_cache"] = {str(address).upper(): True for address, modern in config["modern_reply_cache"].items()
|
||||
if modern is True}
|
||||
return config
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ from typing import Any
|
||||
from openpilot.common.hardware import COMMA_HARDWARE
|
||||
from openpilot.starpilot.system.starport import car_profiles, device_health, identity as identity_store, system_slice
|
||||
from openpilot.starpilot.system.starport.auto_connect import AutoConnectPolicy
|
||||
from openpilot.starpilot.system.starport.bootstrap import NAMES, BootstrapError, WirelessBootstrap
|
||||
from openpilot.starpilot.system.starport.bootstrap import MODERN_MAJOR, NAMES, BootstrapError, WirelessBootstrap
|
||||
from openpilot.starpilot.system.starport.bugsink_report import PairingAttempt
|
||||
from openpilot.starpilot.system.starport.frame_source import (DEFAULT_PATH as DEFAULT_FRAME_PATH, FLAG_ASYNC_READBACK, FLAG_NV12,
|
||||
FORMAT_NV12, FrameRequest)
|
||||
@@ -498,11 +498,13 @@ class Supervisor:
|
||||
self.stop()
|
||||
with self._lock:
|
||||
companion = self.config.get("companion_address", "").upper() == address
|
||||
cache = dict(self.config["rfcomm_cache"])
|
||||
cache, replies = dict(self.config["rfcomm_cache"]), dict(self.config.get("modern_reply_cache", {}))
|
||||
cached = cache.pop(address, None) is not None
|
||||
cached = replies.pop(address, None) is not None or cached
|
||||
if not (chosen or companion or cached):
|
||||
return False # nothing of this device was kept
|
||||
self.config["rfcomm_cache"] = cache
|
||||
self.config.update(rfcomm_cache=cache, modern_reply_cache=replies)
|
||||
self._modern_reply.pop(address, None)
|
||||
if chosen:
|
||||
if self._bluez is not None:
|
||||
self._bluez.release()
|
||||
@@ -987,6 +989,21 @@ class Supervisor:
|
||||
else:
|
||||
bluez.restore_class()
|
||||
|
||||
def _remember_reply(self, address: str, modern: bool) -> None:
|
||||
"""Keep the version reply a v4+ head unit started with, so the next session leads with it."""
|
||||
with self._lock:
|
||||
self._modern_reply[address] = modern
|
||||
replies = dict(self.config.get("modern_reply_cache", {}))
|
||||
if modern == replies.get(address, False):
|
||||
return
|
||||
if modern:
|
||||
replies[address] = True
|
||||
else:
|
||||
replies.pop(address, None)
|
||||
self.config["modern_reply_cache"] = replies
|
||||
identity_store.save_config(self.config)
|
||||
self.log("bootstrap_reply_remembered", modern=modern)
|
||||
|
||||
def _remember_channel(self, address: str, channel: int | None) -> None:
|
||||
with self._lock:
|
||||
cache = dict(self.config["rfcomm_cache"])
|
||||
@@ -1242,14 +1259,14 @@ class Supervisor:
|
||||
boot = WirelessBootstrap(rfcomm, self._bootstrap_log, device_serial=config["device_name"],
|
||||
version_status=int(config.get("version_status", 0)),
|
||||
experiment="all" if car_profiles.tuning(config)["bootstrap_experiments"] else "",
|
||||
modern_reply=self._modern_reply.get(address, False))
|
||||
modern_reply=self._modern_reply.get(address, config.get("modern_reply_cache", {}).get(address, False)))
|
||||
self._set(state="wifi_start")
|
||||
result = boot.run(lambda credentials: lease.acquire(credentials, cancelled=boot.join_is_cancelled), cancelled=self._stop.is_set)
|
||||
except Exception as error:
|
||||
if _stray_link(error):
|
||||
raise StrayLinkError(f"Bluetooth link to the car is stuck: {error}") from error # says nothing about the cached channel
|
||||
if boot is not None and boot.silent_after_modern_version(error):
|
||||
self._modern_reply[address] = not self._modern_reply.get(address, False) # try the other version reply next
|
||||
self._modern_reply[address] = not boot.modern_reply # try the other version reply next
|
||||
self.log("bootstrap_reply_switched", modern=self._modern_reply[address])
|
||||
if source == "cache" and not self._stop.is_set() and self._status["last_stage"] in ("rfcomm", "wifi_start") and car_never_answered(error):
|
||||
self._remember_channel(address, None) # the car never answered there; ask it over SDP next time
|
||||
@@ -1257,6 +1274,8 @@ class Supervisor:
|
||||
raise
|
||||
if source == "sdp":
|
||||
self._remember_channel(address, channel)
|
||||
if boot.version_major >= MODERN_MAJOR:
|
||||
self._remember_reply(address, boot.modern_reply)
|
||||
self._set(head_unit=result.head_unit)
|
||||
self._remember_fingerprint(result.head_unit)
|
||||
bluez.restore_class() # the car has accepted the comma; stop looking like a phone to everything else
|
||||
|
||||
@@ -67,3 +67,13 @@ def test_forget_car_edits_the_saved_config_when_the_daemon_is_not_running(tmp_pa
|
||||
assert saved()["companion_address"] == "" and saved()["receiver_address"] == ADAPTER
|
||||
assert protocol.forget_car(ADAPTER.lower(), client) is True
|
||||
assert saved()["receiver_address"] == "" and saved()["rfcomm_cache"] == {}
|
||||
|
||||
|
||||
def test_the_version_reply_a_car_started_with_is_kept_and_forgotten_with_it(sup):
|
||||
sup._remember_reply(ADAPTER, True)
|
||||
sup._remember_reply(OTHER, True)
|
||||
assert saved()["modern_reply_cache"] == {ADAPTER: True, OTHER: True}
|
||||
sup._remember_reply(OTHER, False)
|
||||
assert saved()["modern_reply_cache"] == {ADAPTER: True}
|
||||
daemon.handle(sup, {"command": "forget_receiver", "address": ADAPTER})
|
||||
assert saved()["modern_reply_cache"] == {} and ADAPTER not in sup._modern_reply
|
||||
|
||||
@@ -28,3 +28,11 @@ def test_existing_identity_survives_directory_migration(tmp_path, monkeypatch, o
|
||||
|
||||
assert directory == (legacy if outcome == 'denied' else current)
|
||||
assert (directory / 'certificate').read_bytes() == b'existing identity'
|
||||
|
||||
|
||||
def test_remembered_version_replies_load_only_as_true_per_address(tmp_path):
|
||||
path = tmp_path / "config.json"
|
||||
path.write_text('{"config_version": 7, "modern_reply_cache": {"aa:bb:cc:dd:ee:01": true, "AA:BB:CC:DD:EE:02": false, "x": 1}}')
|
||||
assert identity.load_config(path)["modern_reply_cache"] == {"AA:BB:CC:DD:EE:01": True}
|
||||
path.write_text('{"config_version": 7}')
|
||||
assert identity.load_config(path)["modern_reply_cache"] == {}
|
||||
|
||||
Reference in New Issue
Block a user