dtmilano / AndroidViewClient

Android ViewServer and ADB client
Apache License 2.0
1.61k stars 344 forks source link

Fix XML parsing error when using MIUI 12 on Pocophone X3 #283 #284

Closed Marc--Olivier closed 3 years ago

Marc--Olivier commented 3 years ago

My script uses AndroidViewClient but cannot create a ViewClient

from com.dtmilano.android.viewclient import ViewClient

def createViewClient():
    return ViewClient(*ViewClient.connectToDeviceOrExit(serialno='.*'))

vc = createViewClient()

The script returns the following stack trace:

  File "/home/bob/workspace/script.py", line 4, in createViewClient
    return ViewClient(*ViewClient.connectToDeviceOrExit(serialno='.*'))
  File "/home/bob/.local/lib/python3.6/site-packages/com/dtmilano/android/viewclient.py", line 2690, in __init__
    self.dump()
  File "/home/bob/.local/lib/python3.6/site-packages/com/dtmilano/android/viewclient.py", line 3522, in dump
    self.setViewsFromUiAutomatorDump(received)
  File "/home/bob/.local/lib/python3.6/site-packages/com/dtmilano/android/viewclient.py", line 3105, in setViewsFromUiAutomatorDump
    self.__parseTreeFromUiAutomatorDump(received)
  File "/home/bob/.local/lib/python3.6/site-packages/com/dtmilano/android/viewclient.py", line 3323, in __parseTreeFromUiAutomatorDump
    self.root = parser.Parse(receivedXml[start_xml_index:end_xml_index + 1])
  File "/home/bob/.local/lib/python3.6/site-packages/com/dtmilano/android/viewclient.py", line 2394, in Parse
    _ = parser.Parse(encoded, True)
  File "../Modules/pyexpat.c", line 284, in CharacterData
  File "/home/bob/.local/lib/python3.6/site-packages/com/dtmilano/android/viewclient.py", line 2381, in CharacterData
    element = self.nodeStack[-1]
IndexError: list index out of range

When I execute the command adb shell uiautomator dump on my device a Pocophone X3 with MIUI 12 installed, I get the following result:

adb shell uiautomator dump
java.io.FileNotFoundException: /data/system/theme_config/theme_compatibility.xml: open failed: ENOENT (No such file or directory)
    at libcore.io.IoBridge.open(IoBridge.java:496)
    at java.io.FileInputStream.<init>(FileInputStream.java:159)
    at java.io.FileInputStream.<init>(FileInputStream.java:115)
    at java.io.FileReader.<init>(FileReader.java:58)
    at miui.content.res.ThemeCompatibilityLoader.getVersion(ThemeCompatibilityLoader.java:108)
    at miui.content.res.ThemeCompatibilityLoader.getConfigDocumentTree(ThemeCompatibilityLoader.java:126)
    at miui.content.res.ThemeCompatibilityLoader.loadConfig(ThemeCompatibilityLoader.java:59)
    at miui.content.res.ThemeCompatibility.<clinit>(ThemeCompatibility.java:31)
    at miui.content.res.ThemeCompatibility.isThemeEnabled(ThemeCompatibility.java:111)
    at android.content.res.MiuiResourcesImpl.<clinit>(MiuiResourcesImpl.java:41)
    at android.content.res.Resources.<init>(Resources.java:285)
    at android.content.res.MiuiResources.<init>(MiuiResources.java:49)
    at android.content.res.Resources.getSystem(Resources.java:206)
    at android.util.MiuiMultiWindowAdapter.<clinit>(MiuiMultiWindowAdapter.java:79)
    at android.view.Display.getSize(Display.java:665)
    at com.android.commands.uiautomator.DumpCommand.run(DumpCommand.java:98)
    at com.android.commands.uiautomator.Launcher.main(Launcher.java:83)
    at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
    at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:380)
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
    at libcore.io.Linux.open(Native Method)
    at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
    at libcore.io.IoBridge.open(IoBridge.java:482)
    ... 18 more
UI hierchary dumped to: /sdcard/window_dump.xml

So uiautomator throws an error, but still succeeds to dump the XML. The constructor of ViewClient calls parser.Parse(receivedXml[start_xml_index:end_xml_index + 1]) Ln 3323, but the XML contained in received cannot be parsed because it still contains parts of the exceptions: start_xml_index = receivedXml.index("<") will return the first index of '<', which is in the exception (java.io.FileInputStream.<init>).

We fixed the issue by filtering out the exception from the received variable Ln 3461:

received = re.sub(re.compile('java.io.FileNotFoundException.*<\?xml', re.DOTALL),'<?xml', received)

But it would probably be cleaner to replace the value of start_xml_index = receivedXml.index("<") with start_xml_index = receivedXml.index("<?xml").

@dtmilano What do you think? Would you be interested by a pull request?

Note that this error is similar to two issues in the scrcpy project:

These issues were fixed by just ignoring the exceptions (see https://github.com/Genymobile/scrcpy/commit/96bd2c974d0fb9765dc4d2978688bb0bbd65cc9f).

dtmilano commented 3 years ago

@Marc--Olivier good catch! Thanks for your contribution.

uiautomator dump is a source of surprises and problems. I would encourage the use of CulebraTester2-public as an alternative backend for AndroidViewClient/culebra.

Let me know if you need any help.

Marc--Olivier commented 3 years ago

@dtmilano Many thanks for having merged this pull request!

I also noticed that uiautomator dump was not always working smoothly... Many thanks for the link to https://github.com/dtmilano/CulebraTester2-public, I tried it out, but having to install an alternative back-end was not an option for what I was doing. Still, I am sure I will use this new back-end in a relatively near feature. So many thanks for providing it!