appium/appium-uiautomator2-server

App crash : 'Resource leak' and 'HardwareBuffer.close not called ' issue while using getScreenshotAs method of appium android driver

Ganesha-Deshmukh opened this issue · 3 comments

Problem statement
I'm doing the UI automation using the appium-uiautomator2-server-v4.27.0.apk apk's Or appium-uiautomator2-server-v7.0.14.apk.
I'm calling the interface "((TakesScreenshot)session).getScreenshotAs(OutputType.FILE)" using the appium Android driver session.
After calling this method, I have observed following exception in logcat
Log
W System : A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
W System : java.lang.Throwable: Explicit termination method 'HardwareBuffer.close' not called
W System : at dalvik.system.CloseGuard.openWithCallSite(CloseGuard.java:288)
W System : at dalvik.system.CloseGuard.open(CloseGuard.java:257)
W System : at android.hardware.HardwareBuffer.(HardwareBuffer.java:272)

Overall impact
Its start crashing the application after sometimes.
Primary Root cause analysis
After looking into the [appium-uiautomator2-server apk source code I observed that the method "takeDeviceScreenshot" of a class ScreenshotHelper get called.
After doing debug on it, its observed that the condition "if (metrics.densityDpi != DENSITY_DEFAULT && Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) " is not met because the express 'metrics.densityDpi != DENSITY_DEFAULT' value is false.
So it invokes the method "screenshot = automation.takeScreenshot();" .After calling this method it take the screenshot but throws exception of resource leak shown as above.

Display metrics: DisplayMetrics{density=1.0, width=1744, height=720, scaledDensity=1.0, xdpi=167.013, ydpi=167.779}
Log value print for the expression "metrics.densityDpi != DENSITY_DEFAULT && Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1" are as follows:
metrics.densityDpi: 160
DENSITY_DEFAULT: 160
Build.VERSION.SDK_INT: 33
Build.VERSION_CODES.LOLLIPOP_MR1: 22

Observation
On commenting the expression "metrics.densityDpi != DENSITY_DEFAULT " no issue occurred
Overall, the call of "automation.executeShellCommand("screencap -p") " is working on android 8,10,12,13 but the call "automation.takeScreenshot()" is not working on Android 12 and 13.

Note
Issue occurred on the device which is using Android Automotive OS of android version 12 and 13. Same issue is no occurring for android version 8 and 10. Difference between these 2 is only metrics.densityDpi.

Thanks for the investigation @Ganesha-Deshmukh

The above automation.takeScreenshot() method belongs to the UiAutomator API and we don't have any control over it.
I have double checked that the returned Bitmap instance is recycled after it is used, otherwise there is nothing else to close in this API from the client side.

Please report the issue to Google.

Thank you for your quick response @mykola-mokhnach
I have reported this issue to google and sharing its tracker id
https://issuetracker.google.com/issues/351092760

It will be helpful if you provide the information about the need of expression "metrics.densityDpi != DENSITY_DEFAULT" in statement "if (metrics.densityDpi != DENSITY_DEFAULT && Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {"
in method "takeDeviceScreenshot(Class outputType)" of class "ScreenshotHelper"
There is one comment but not sure does this expression of android 5 .

The original reason why the screencap workaround has been added was another bug in the UiAutomator where the retrieved bitmap was wrong for non-default DPIs. See #248 for more details.

The main path still uses the UiAutomator call because it's more performant in comparison to the screencap workaround, e.g. enabling it by default would slow down customer scenarios.