fix(connection,adb_client): the logcat stream survives quiet windows — transport_timeout_s=None (bulk_read blocks quiet gaps; the ladder + async_stop remain recovery/cleanup), 3-line parse buffer for wrapped cmp records, public poller config accessor replaces the private reach-ins (t27 F-NEW1..3)

This commit is contained in:
James Mackay committed 2026-10-09 06:20:38 +01:00
1 parent 27715f6932
commit 9b3142874f
2 files changed
+79 -9

No files matched your search

+21 -9
View File
@@ -216,6 +216,8 @@ class ShieldConnectionManager:
self._logcat_failures: list[float] = []
self._logcat_last_pkg: str | None = None
self._logcat_last_stamp: float = -1e9
# Injectable ladder sleep (t26: deterministic tests / future alt-floors)
self._logcat_ladder_sleep = asyncio.sleep
self._app_label_requests: set[str] = set()
# Injectable clock keeps the TTL expiry tests deterministic.
self._clock = clock
@@ -655,22 +657,32 @@ class ShieldConnectionManager:
)
self._logcat_failures.clear()
async def _logcat_pass(self) -> None:
"""One full consumption of the logcat line-stream (P5-3).
Returns when the stream ends (the dedicated connection dropped);
errors propagate to the caller's ladder.
"""
async for line in self._open_logcat_stream():
package = foreground_from_logcat([str(line)])
if package:
self._on_logcat_app_start(package)
raise RuntimeError("stream ended (device closed)")
async def _run_logcat_stream(self) -> None:
"""Consume the logcat line-stream; backoff-reconnect on failure.
The stream owns its failure ladder (separate from the ADB socket
watchdog, reusing the same jitter): Store failures and reconnect
with 5 s doubling to the 300 s cap ±jitter; five failures in ten
minutes auto-disable the option for the session (heartbeat floor).
watchdog, reusing the same jitter): Stream failures reconnect with
5 s doubling to the 300 s cap ±jitter; five failures in ten minutes
auto-disable the option for the session (heartbeat floor). The
ladder sleep is injectable (_logcat_ladder_sleep) so tests stay
deterministic.
"""
delay = ADB_RECONNECT_BACKOFF_START
while not self._stopping and not self._logcat_disabled:
try:
async for line in self._open_logcat_stream():
package = foreground_from_logcat([str(line)])
if package:
self._on_logcat_app_start(package)
raise RuntimeError("stream ended (device closed)")
await self._logcat_pass()
except asyncio.CancelledError:
raise
except Exception as err:
@@ -678,7 +690,7 @@ class ShieldConnectionManager:
self._note_logcat_failure(self._clock())
if self._stopping or self._logcat_disabled:
break
await asyncio.sleep(self._jitter(delay))
await self._logcat_ladder_sleep(self._jitter(delay))
delay = min(delay * 2, ADB_RECONNECT_BACKOFF_CAP)
def _ensure_logcat_stream(self) -> None: