Skip to content

Commit a5fd647

Browse files
authored
if appium_driver is available, don't call uiautomator, otherwise it will crash (#676)
1 parent e335676 commit a5fd647

3 files changed

Lines changed: 97 additions & 13 deletions

File tree

Apps/Mobile/Android_Inspector_ZeuZ.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,23 @@ def run_adb_command(command):
2424

2525
def capture_ui_dump():
2626
"""Capture the current UI hierarchy from the device and take a screenshot."""
27-
run_adb_command(f"{ADB_PATH} shell uiautomator dump /sdcard/ui.xml")
28-
run_adb_command(f"{ADB_PATH} pull /sdcard/ui.xml {UI_XML_PATH}")
27+
xml_saved = False
28+
appium_driver = None
29+
try:
30+
from Framework.Built_In_Automation.Shared_Resources import BuiltInFunctionSharedResources as Shared_Resources
31+
appium_driver = Shared_Resources.Get_Shared_Variables("appium_driver", log=False)
32+
if appium_driver is not None:
33+
page_src = appium_driver.page_source
34+
with open(UI_XML_PATH, "w") as xml_file:
35+
xml_file.write(page_src)
36+
xml_saved = True
37+
except Exception:
38+
pass
39+
# even if it fails don't try adb if appium driver is available
40+
if not xml_saved and appium_driver is None:
41+
run_adb_command(f"{ADB_PATH} shell uiautomator dump /sdcard/ui.xml")
42+
run_adb_command(f"{ADB_PATH} pull /sdcard/ui.xml {UI_XML_PATH}")
43+
2944
run_adb_command(f"{ADB_PATH} shell screencap -p /sdcard/screen.png")
3045
run_adb_command(f"{ADB_PATH} pull /sdcard/screen.png {SCREENSHOT_PATH}")
3146
update_treeview()

Framework/Built_In_Automation/Mobile/Android/adb_calls/adbOptions.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -782,11 +782,38 @@ def check_if_device_is_unlocked(serial=""):
782782
"adb %s shell input keyevent 82" % (serial), shell=True, encoding="utf-8"
783783
) # Wakeup device and bring it unlock window
784784
time.sleep(1)
785-
output = subprocess.check_output(
786-
"adb %s exec-out uiautomator dump /dev/tty" % serial,
787-
shell=True,
788-
encoding="utf-8",
789-
)
785+
786+
try:
787+
from Framework.Built_In_Automation.Shared_Resources import BuiltInFunctionSharedResources as Shared_Resources
788+
789+
appium_driver = None
790+
if serial and serial.startswith("-s "):
791+
extracted_serial = serial.split("-s ")[1].strip()
792+
appium_details = Shared_Resources.Get_Shared_Variables("appium_details", log=False)
793+
if isinstance(appium_details, dict):
794+
for name, details in appium_details.items():
795+
stored_serial = details.get("serial")
796+
if stored_serial and (stored_serial == extracted_serial or stored_serial in extracted_serial or extracted_serial in stored_serial):
797+
appium_driver = details.get("driver")
798+
break
799+
800+
if appium_driver is None and not serial:
801+
appium_driver = Shared_Resources.Get_Shared_Variables("appium_driver", log=False)
802+
803+
if appium_driver is not None:
804+
output = appium_driver.page_source
805+
else:
806+
output = subprocess.check_output(
807+
"adb %s exec-out uiautomator dump /dev/tty" % serial,
808+
shell=True,
809+
encoding="utf-8",
810+
)
811+
except Exception:
812+
output = subprocess.check_output(
813+
"adb %s exec-out uiautomator dump /dev/tty" % serial,
814+
shell=True,
815+
encoding="utf-8",
816+
)
790817

791818
if "EMERGENCY" in output or "emergency_call_button" in output:
792819
CommonUtil.ExecLog(

server/mobile.py

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,43 @@ def run_adb_command_bytes(cmd: str, timeout: int = 30) -> bytes:
166166
raise RuntimeError(f"ADB command timed out after {timeout}s: {cmd}")
167167

168168

169+
def get_appium_driver_for_serial(device_serial: str | None = None):
170+
"""
171+
Retrieve the specific Appium driver instance for the given Android device serial.
172+
Defaults to the global appium_driver if no serial is provided.
173+
"""
174+
from Framework.Built_In_Automation.Shared_Resources import BuiltInFunctionSharedResources as Shared_Resources
175+
176+
if not device_serial:
177+
return Shared_Resources.Get_Shared_Variables("appium_driver", log=False)
178+
179+
appium_details = Shared_Resources.Get_Shared_Variables("appium_details", log=False)
180+
if isinstance(appium_details, dict):
181+
for name, details in appium_details.items():
182+
stored_serial = details.get("serial")
183+
if stored_serial and (stored_serial == device_serial or stored_serial in device_serial or device_serial in stored_serial):
184+
return details.get("driver")
185+
186+
return None
187+
188+
169189
def fetch_xml_and_screenshot(device_serial: str | None = None) -> tuple[str, bytes]:
170190
"""
171191
Single-function fetch. Primary path uses ONE adb exec-out command to capture
172192
UI XML + PNG (base64) in a single stream. Falls back (still inside this function)
173193
if markers or outputs are invalid.
174194
"""
195+
# Try Appium first
196+
try:
197+
appium_driver = get_appium_driver_for_serial(device_serial)
198+
if appium_driver is not None:
199+
xml = appium_driver.page_source
200+
png = appium_driver.get_screenshot_as_png()
201+
if xml and png:
202+
return xml, png
203+
except Exception:
204+
pass
205+
175206
device_flag = f"-s {device_serial}" if device_serial else ""
176207

177208
SPLIT = "__ZEUZ_SPLIT__"
@@ -206,6 +237,8 @@ def fetch_xml_and_screenshot(device_serial: str | None = None) -> tuple[str, byt
206237
if "<hierarchy" in xml and png.startswith(b"\x89PNG"):
207238
return xml, png
208239

240+
return "", b""
241+
209242

210243
@router.get("/inspect", response_model=InspectorResponse)
211244
async def inspect(device_serial: str | None = None):
@@ -346,15 +379,21 @@ def capture_ui_dump(device_serial: str | None = None):
346379
"""Capture the current UI hierarchy from the device"""
347380
# Try to get from active Appium driver first (like web does)
348381
try:
349-
from Framework.Built_In_Automation.Mobile.CrossPlatform.Appium.BuiltInFunctions import appium_driver
382+
appium_driver = get_appium_driver_for_serial(device_serial)
350383

351384
if appium_driver is not None:
352385
page_src = appium_driver.page_source
353386
with open(UI_XML_PATH, "w") as xml_file:
354387
xml_file.write(page_src)
355388
return
356389
except Exception as e:
357-
pass
390+
# If Appium driver is active but failed, do NOT fallback to ADB as it will kill the session
391+
try:
392+
appium_driver = get_appium_driver_for_serial(device_serial)
393+
if appium_driver is not None:
394+
return
395+
except Exception:
396+
pass
358397

359398
# Fallback to ADB
360399
device_flag = f"-s {device_serial}" if device_serial else ""
@@ -368,11 +407,9 @@ def capture_ui_dump(device_serial: str | None = None):
368407
if out.startswith("Error:"):
369408
return
370409

371-
out = run_adb_command(
410+
run_adb_command(
372411
f"{ADB_PATH} {device_flag} pull /sdcard/ui.xml {UI_XML_PATH}"
373412
)
374-
if os.path.exists(UI_XML_PATH):
375-
size = os.path.getsize(UI_XML_PATH)
376413

377414

378415
def capture_screenshot(device_serial: str | None = None):
@@ -496,7 +533,12 @@ async def upload_android_ui_dump():
496533
prev_xml_hash = ""
497534
while True:
498535
try:
499-
await asyncio.to_thread(capture_ui_dump)
536+
device_to_capture = None
537+
devices = get_devices()
538+
if devices:
539+
device_to_capture = devices[0].serial
540+
541+
await asyncio.to_thread(capture_ui_dump, device_to_capture)
500542
try:
501543
with open(UI_XML_PATH, "r") as xml_file:
502544
xml_content = xml_file.read()

0 commit comments

Comments
 (0)