4

Apparently, something in the application calls the method from different threads (both main and a binder thread) which causes an internal ANR. It happens quite frequently and I don't have an idea as to where it happens because I cannot reproduce it on emulators or the test devices that I have got.

What the app does: It is an app locker application, which draws a full screen lock view on application overlay and asks for a password (pattern), which also supports fingerprint unlock mechanism. To successfully listen the fingerprint, we also have to use a transparent activity to make the app go "foreground", and then wait for the fingerprint input, as otherwise, fingerprint mechanism does not listen and it doesn't have a callback for it either. Maybe that is the cause, because the activity starts and finishes frequently, on every locked app.

ANR is reported under two matters:

Broadcast of Intent { act=android.intent.action.SCREEN_ON flg=0x50200010 (has extras) } Broadcast of Intent { act=android.intent.action.SCREEN_OFF flg=0x50200010 (has extras) }

Regarding to this, the app has a foreground service that always runs, because it needs to lock apps at any time as a requirement, and it listens to these events from a dynamic broadcast receiver. However, there are no blocking calls or synchronized calls in any of them, just a start-stop mechanism for the continuous worker thread that checks the current foreground app.

Full ANR report

Related ANR report (related section, reported from Android 10 device, API 29):

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x71773f98 self=0x7f95a7d000
  | sysTid=28841 nice=-4 cgrp=default sched=0/0 handle=0x7f96febee8
  | state=S schedstat=( 151471598466 24930775143 453988 ) utm=9534 stm=5612 core=3 HZ=100
  | stack=0x7fddb4d000-0x7fddb4f000 stackSize=8192KB
  | held mutexes=
  at android.hardware.display.DisplayManagerGlobal.getDisplayInfo (DisplayManagerGlobal.java:177)
- waiting to lock <0x038dbdd7> (a java.lang.Object) held by thread 6
  at android.view.Display.updateDisplayInfoLocked (Display.java:1214)
  at android.view.Display.updateDisplayInfoLocked (Display.java:1209)
  at android.view.Display.getState (Display.java:1174)
- locked <0x053b6aeb> (a android.view.Display)
  at android.view.ViewRootImpl$1.onDisplayChanged (ViewRootImpl.java:1601)
  at android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate.handleMessage (DisplayManagerGlobal.java:1417)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:237)
  at android.app.ActivityThread.main (ActivityThread.java:7811)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1068)

"Binder:28841_6" prio=5 tid=6 Runnable
  | group="main" sCount=0 dsCount=0 flags=0 obj=0x132c19b0 self=0x7f08d9a400
  | sysTid=29512 nice=0 cgrp=default sched=0/0 handle=0x7f0133ed50
  | state=R schedstat=( 673333745225 29381013522 76199 ) utm=67111 stm=221 core=7 HZ=100
  | stack=0x7f01248000-0x7f0124a000 stackSize=991KB
  | held mutexes= "mutator lock"(shared held)
  at android.os.MessageQueue.enqueueMessage (MessageQueue.java:581)
- locked <0x0306e956> (a android.os.MessageQueue)
  at android.os.Handler.enqueueMessage (Handler.java:754)
  at android.os.Handler.sendMessageAtTime (Handler.java:703)
  at android.os.Handler.sendMessageDelayed (Handler.java:673)
  at android.os.Handler.sendMessage (Handler.java:611)
  at android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate.sendDisplayEvent (DisplayManagerGlobal.java:1403)
  at android.hardware.display.DisplayManagerGlobal.handleDisplayEvent (DisplayManagerGlobal.java:408)
- locked <0x038dbdd7> (a java.lang.Object)
  at android.hardware.display.DisplayManagerGlobal.access$100 (DisplayManagerGlobal.java:70)
  at android.hardware.display.DisplayManagerGlobal$DisplayManagerCallback.onDisplayEvent (DisplayManagerGlobal.java:1354)
  at android.hardware.display.IDisplayManagerCallback$Stub.onTransact (IDisplayManagerCallback.java:119)
  at android.os.Binder.execTransactInternal (Binder.java:1021)
  at android.os.Binder.execTransact (Binder.java:994)

I'm not exactly sure what causes this, as there are no stack traces to the app itself, in any threads. And I can see that main thread is blocked due to waiting to lock <0x038dbdd7>, since the binder thread already locked it: locked <0x038dbdd7> but did not release it.

Any opinions on this matter? I appreciate any help, thank you very much.

Furkan Yurdakul
  • 2,801
  • 1
  • 15
  • 37
  • Seems like a weird race condition in Android itself. Do you have any other information provided by the ANR report? I'm not familiar with the reports themselves but logcats or just more details could potentially help. – JensV Apr 13 '20 at 08:25
  • @JensV Unfortunately, no. The stack trace is provided from Developer Console, from the tab ANR's and crashes. I cannot reproduce the ANR as well, locally. – Furkan Yurdakul Apr 13 '20 at 08:28
  • @JensV Actually, if it helps, the cause seem to be " Broadcast of Intent { act=android.intent.action.SCREEN_OFF flg=0x50200010 (has extras) }", which I am adding as an edit to the stack trace now in the question. – Furkan Yurdakul Apr 13 '20 at 08:31
  • @JensV Added a bit of extra information to the question. – Furkan Yurdakul Apr 13 '20 at 08:34
  • Something is definitely weird... Since thread 6 has state `Runnable` and should theoretically get assigned some time to finish sending that message. I presume you don't have any information on other running threads. A wild guess is that the system was so busy that Thread 6 just didn't get any time to finish but it seems a bit unlikely. – JensV Apr 13 '20 at 08:39
  • @JensV The report seems to have almost 20 threads, but 15 of them are in "Waiting" or "TimedWaiting" state. The others are also in "Native" state, but I'm not exactly sure if they affect the main thread. There aren't also any other common locked objects that affects main thread itself even in a relative way, so I'm stumped. – Furkan Yurdakul Apr 13 '20 at 08:47
  • Can you provide info on the threads that are `Running` or `Runnable`? Are these executing any code that you control and/or are doing expensive work? – JensV Apr 13 '20 at 08:49
  • You could also post the whole ANR Report to pastebin or similar. (It'd be a bit much to include in the question I'm guessing) – JensV Apr 13 '20 at 08:50
  • @JensV Added the pastebin link under "Full ANR report" up above the stack trace. I can also tell that the "Native" state threads, the app does not explicitly create them, but I'm not sure about the included libraries. – Furkan Yurdakul Apr 13 '20 at 08:58
  • Which BroadcastReceivers and other event handlers are you registering? It might be caused by registering from a service which is stopped later and not unregistering or something causing the sendMessage to get stuck – JensV Apr 13 '20 at 09:36
  • For some reason the chat link disappeared, so if you have written anything there, I wasn't able to see it. Just mentioning here. – Furkan Yurdakul Apr 16 '20 at 11:59

1 Answers1

2

Well, I've found the problem. It is caused because of an internal memory leak under Android's WindowManagerGlobal class. Here is what it caused it:

The app is using the overlay permission, and doing so we were asking the users to give permission, and automatically detect it if the user gives one. After this, we've also been creating a foreground service that runs in between 2 seconds of interval in a loop, never stopping, also checking the permission in between. Since I've encountered some bugs regarding to Settings.canDrawOverlays(Context) method, I've decided to look up for an answer, and found this answer which lead to the huge memory leak.

You see, calling the drawing an invisible overlay part creates a leak. It adds the view root into the memory, but in between, fails to add the overlay because of a SecurityException and the view never gets removed. Since that code creates a new view each time to check the permission, new view roots, new displays and new listeners to the call onDisplayChanged. After debugging, I was finally able to see this and caught the memory leak. Removing that code resolved the issue entirely, no ANRs relating to it are visible now.

I've also tried to remove the view calling WindowManager.removeView(view) if addView fails, but regardless the failed view to be added remained in the listener and memory. So, I'd say this approach should be avoided in any case and another alternative should be found.

Furkan Yurdakul
  • 2,801
  • 1
  • 15
  • 37