appium / appium-uiautomator2-server

Appium UiAutomator/UiObject2-based server for Android UI automation. This module is used by appium-uiautomator2-driver component
Apache License 2.0
318 stars 226 forks source link

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

Open Ganesha-Deshmukh opened 5 days ago

Ganesha-Deshmukh commented 5 days ago

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.

mykola-mokhnach commented 5 days ago

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.

Ganesha-Deshmukh commented 5 days ago

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 .

mykola-mokhnach commented 5 days ago

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 https://github.com/appium/appium-uiautomator2-server/pull/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.