From d6eddbb07637808b9055a9ab9b8afa1576bae179 Mon Sep 17 00:00:00 2001 From: Ricardo-M-L Date: Tue, 14 Apr 2026 22:12:32 +0800 Subject: [PATCH 1/2] fix: prevent IndexError in Type action parser and fix screenshot temp file leak 1. handler.py parse_action(): split("text=", 1) can produce a single- element list when the text parameter is missing from a malformed Type action. Add a length check before accessing [1] and raise a clear ValueError instead of an opaque IndexError. 2. adb/screenshot.py: temp files created with unique UUIDs are only cleaned up on the success path. If Image.open() or img.save() raises, the file leaks. Wrap the image processing in try/finally and also clean up in the outer except block. Co-Authored-By: Claude Opus 4.6 (1M context) --- phone_agent/actions/handler.py | 5 ++++- phone_agent/adb/screenshot.py | 25 ++++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/phone_agent/actions/handler.py b/phone_agent/actions/handler.py index 0bef1c3a2..25c832554 100644 --- a/phone_agent/actions/handler.py +++ b/phone_agent/actions/handler.py @@ -348,7 +348,10 @@ def parse_action(response: str) -> dict[str, Any]: if response.startswith('do(action="Type"') or response.startswith( 'do(action="Type_Name"' ): - text = response.split("text=", 1)[1][1:-2] + parts = response.split("text=", 1) + if len(parts) < 2: + raise ValueError(f"Missing 'text=' parameter in Type action: {response}") + text = parts[1][1:-2] action = {"_metadata": "do", "action": "Type", "text": text} return action elif response.startswith("do"): diff --git a/phone_agent/adb/screenshot.py b/phone_agent/adb/screenshot.py index bdc5b0922..394eb3151 100644 --- a/phone_agent/adb/screenshot.py +++ b/phone_agent/adb/screenshot.py @@ -66,22 +66,25 @@ def get_screenshot(device_id: str | None = None, timeout: int = 10) -> Screensho return _create_fallback_screenshot(is_sensitive=False) # Read and encode image - img = Image.open(temp_path) - width, height = img.size + try: + img = Image.open(temp_path) + width, height = img.size - buffered = BytesIO() - img.save(buffered, format="PNG") - base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8") + buffered = BytesIO() + img.save(buffered, format="PNG") + base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8") - # Cleanup - os.remove(temp_path) - - return Screenshot( - base64_data=base64_data, width=width, height=height, is_sensitive=False - ) + return Screenshot( + base64_data=base64_data, width=width, height=height, is_sensitive=False + ) + finally: + if os.path.exists(temp_path): + os.remove(temp_path) except Exception as e: print(f"Screenshot error: {e}") + if os.path.exists(temp_path): + os.remove(temp_path) return _create_fallback_screenshot(is_sensitive=False) From ac3c7970f57395feb71d3753942bcf2993f4404f Mon Sep 17 00:00:00 2001 From: Ricardo-M-L Date: Fri, 17 Apr 2026 01:23:58 +0800 Subject: [PATCH 2/2] fix: apply same temp file leak fix to HDC screenshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HarmonyOS HDC path in phone_agent/hdc/screenshot.py had the identical leak pattern as the ADB path — if Image.open() or img.save() raised, the outer except swallowed the error and the temp file under $TMPDIR was never removed. Apply the same try/finally + except-path cleanup as ADB to guarantee the temp screenshot file is removed on every path. Co-Authored-By: Claude Opus 4.6 (1M context) --- phone_agent/hdc/screenshot.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/phone_agent/hdc/screenshot.py b/phone_agent/hdc/screenshot.py index 332d198c2..f8ecc644d 100644 --- a/phone_agent/hdc/screenshot.py +++ b/phone_agent/hdc/screenshot.py @@ -82,22 +82,25 @@ def get_screenshot(device_id: str | None = None, timeout: int = 10) -> Screensho # Read JPEG image and convert to PNG for model inference # PIL automatically detects the image format from file content - img = Image.open(temp_path) - width, height = img.size + try: + img = Image.open(temp_path) + width, height = img.size - buffered = BytesIO() - img.save(buffered, format="PNG") - base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8") + buffered = BytesIO() + img.save(buffered, format="PNG") + base64_data = base64.b64encode(buffered.getvalue()).decode("utf-8") - # Cleanup - os.remove(temp_path) - - return Screenshot( - base64_data=base64_data, width=width, height=height, is_sensitive=False - ) + return Screenshot( + base64_data=base64_data, width=width, height=height, is_sensitive=False + ) + finally: + if os.path.exists(temp_path): + os.remove(temp_path) except Exception as e: print(f"Screenshot error: {e}") + if os.path.exists(temp_path): + os.remove(temp_path) return _create_fallback_screenshot(is_sensitive=False)