From edfd69571dc1907d2126983a69abc959b6332200 Mon Sep 17 00:00:00 2001 From: Lotus Li Date: Fri, 9 Oct 2026 11:43:14 -0700 Subject: [PATCH] Disconnect when the oob program quits. Signed-off-by: Lotus Li --- .../cloudxr/oob_teleop_lifecycle.py | 22 +++++++++++++++++++ .../core/cloudxr/test_oob_teleop_lifecycle.py | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/python/isaaccapture/cloudxr/oob_teleop_lifecycle.py b/src/python/isaaccapture/cloudxr/oob_teleop_lifecycle.py index 9701b642b5..9f28b97002 100644 --- a/src/python/isaaccapture/cloudxr/oob_teleop_lifecycle.py +++ b/src/python/isaaccapture/cloudxr/oob_teleop_lifecycle.py @@ -569,6 +569,27 @@ async def _cleanup_owned_rules(self) -> None: "Owned ADB mapping cleanup failed: %s", command, exc_info=True ) + async def _close_teleop_tab(self) -> None: + """Close the headset's teleop tab on shutdown so it leaves the CloudXR session. + + Without this the page treats the vanished server as a dropped stream and keeps + retrying inside the XR session. + """ + if not self.selected: + return + try: + closed = await asyncio.shield( + asyncio.to_thread(adb._close_stale_teleop_tabs) + ) + except Exception: + log.debug("Teleop tab close on shutdown failed", exc_info=True) + return + if closed: + oob_progress( + "usb-local" if self.usb_local else "setup-oob", + f"closed {closed} teleop tab(s) on the headset; session ended", + ) + async def _shielded_cleanup(self) -> None: task = asyncio.create_task(self._cleanup_owned_rules()) await asyncio.shield(task) @@ -2045,6 +2066,7 @@ async def run(self) -> None: ): self._record_usb_wait("STOPPED", "stopped_before_checks") await self._stop_monitor() + await self._close_teleop_tab() await self._shielded_cleanup() if self.coturn is not None: await asyncio.to_thread(adb.stop_coturn, self.coturn) diff --git a/tests/python/core/cloudxr/test_oob_teleop_lifecycle.py b/tests/python/core/cloudxr/test_oob_teleop_lifecycle.py index 4e50ac6ba3..cb1fb43d53 100644 --- a/tests/python/core/cloudxr/test_oob_teleop_lifecycle.py +++ b/tests/python/core/cloudxr/test_oob_teleop_lifecycle.py @@ -1978,7 +1978,8 @@ async def rebuild(): assert lifecycle.browser_client == "surviving-page" automate.assert_not_called() run_oob_connect.assert_not_called() - close_tabs.assert_not_called() + # The repair closes no tab; the one call is run()'s shutdown close. + close_tabs.assert_called_once_with() navigate.assert_not_called()