leakcanary: LeakCanary can't dump heap on Android R devices when targetSdkVersion <= 28
Description
When running an app which targets SDK 28 (or probably anything lower), LeakCanary fails to dump the heap when executing on Android R device (currently tested against most recently available DP2 version)
Steps to Reproduce
- install app from https://github.com/CDRussell/LeakCanaryHeapDumpProblem
- run app on Android R device, and rotate the screen a few times to deliberately trigger a leak detection
- note in
logcat
thatLeakCanary
fails due to write permissions issues
Expected behavior: [What you expect to happen]
LeakCanary
shouldn’t have permissions issues.
Version Information
- LeakCanary version: 2.2
- Android OS version: Android R (dp2)
- Gradle version:
gradle-6.1.1
/com.android.tools.build:gradle:4.0.0-beta04
Additional Information
- Note, only happens if targetSdk is 28 or lower. targetSdk 29 works fine
- If i manually grant the
WRITE_EXTERNAL_STORAGE
permission at runtime, it works again
Stack Trace
2020-04-22 21:16:27.722 6894-6936/com.duckduckgo.myapplication D/LeakCanary: Could not dump heap
java.lang.RuntimeException: Couldn't dump heap; open("/storage/emulated/0/Download/leakcanary-com.duckduckgo.myapplication/2020-04-22_21-16-26_695.hprof") failed: Operation not permitted
at dalvik.system.VMDebug.dumpHprofData(Native Method)
at dalvik.system.VMDebug.dumpHprofData(VMDebug.java:384)
at dalvik.system.VMDebug.dumpHprofData(VMDebug.java:361)
at android.os.Debug.dumpHprofData(Debug.java:2016)
at leakcanary.internal.AndroidHeapDumper.dumpHeap(AndroidHeapDumper.kt:68)
at leakcanary.internal.HeapDumpTrigger.dumpHeap(HeapDumpTrigger.kt:156)
at leakcanary.internal.HeapDumpTrigger.checkRetainedObjects(HeapDumpTrigger.kt:146)
at leakcanary.internal.HeapDumpTrigger.access$checkRetainedObjects(HeapDumpTrigger.kt:28)
at leakcanary.internal.HeapDumpTrigger$scheduleRetainedObjectCheck$3.run(HeapDumpTrigger.kt:293)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:216)
at android.os.HandlerThread.run(HandlerThread.java:67)
2020-04-22 21:16:27.723 6894-6936/com.duckduckgo.myapplication D/LeakCanary: Failed to dump heap, will retry in 5000 ms```
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 26 (12 by maintainers)
Apologies for being non-responsive recently; been swamped. Closing sounds good to me and if I notice the same issue again on the latest Android 11 beta, I’ll comment again about it.
I’m going to close this issue as it sounds like this has been fixed in the latest android candidate release? Let me know if not.
Actually, just one thing: https://developer.android.com/reference/java/io/File#canWrite()
So, given that, it would make sense that File.canWrite() returns false given that the file wasn’t created yet… Maybe that’s not the issue at hand here?
The problem is somewhere in amongst the files created by that method. ☝️. According to those docs, there shouldn’t be a problem targeting 28, but 🤷♂️.
downloadsDirectory.canWrite() == true
. when you create that new sub-directory, the result of that is thatcanWrite() == true
. but when you create theFile
for thehprof
, it says thatcanWrite == false
. in other words, it says/storage/emulated/0/Download
is writable/storage/emulated/0/Download/leakcanary-com.duckduckgo.myapplication
is writable/storage/emulated/0/Download/leakcanary-com.duckduckgo.myapplication/2020-05-06_21-24-29_136.hprof
is not writableTry
LeakCanary.config.dumpHeapWhenDebugging = true
@consp1racy The main advantage is that if you uninstall / reinstall the hprof file is still there. In LeakCanary 1 the heap analysis was stored as a file so it survived reinstalled, but in 2 it’s stored in SQLite so it disappears anyway… Also it doesn’t count towards the app size, though not sure how exactly that matters. Also sometimes the app partition is reaching its max size. Maybe those are problems of the past? We could change LeakCanary to never store on the sdcard, maybe.
Looks like they’ve updated the docs! Here’s a screen grab from the old docs page. You’re right that it’s probably a DP2-specific issue. I’ll test it out with DP3 when I can and report back.
I just created an emulator running R and I can’t reproduce the issue, both with the provided sample as well as with LeakCanary’s sample
Logcat shows the expected log:
This means the permission is missing and LeakCanary won’t try to use the sdcard. In the example you provided, it looked like that check passed and LeakCanary had decided it could use the sdcard, and then that didn’t work.
Now, another weird thing: @CDRussell you pasted a link to https://developer.android.com/preview/behavior-changes-11 but I couldn’t find any reference to scoped storage there. A quick googling brought up this page: https://developer.android.com/preview/privacy/storage
Maybe a change of behavior between dp2 and dp3? Can you still make this crash happen?
We should always use MediaStore on R+, it has new constants for Downloads directory.
https://commonsware.com/blog/2019/12/21/scoped-storage-stories-storing-mediastore.html