SimpleStorage
SimpleStorage copied to clipboard
MediaStore lost access to files after uninstalling the app or clearing its data
Library version: 1.5.1 OS version: [Android 12] Device model: [Emulator Pixel 5]
Describe the bug I'm creating a txt file in Downloads folder with this code:
fun writeToConfig() {
val file = MediaStoreCompat.createDownload(
context,
FileDescription("config-test.txt", "", "text/plain"),
CreateMode.REUSE,
)
file?.openOutputStream(true).use {
it?.write("HelloWorld\n".toByteArray())
}
}
And I'm reading the file with this approach:
fun readConfig() {
val file = MediaStoreCompat.fromFileName(context, MediaType.DOWNLOADS, "config-test.txt")
file?.openInputStream()?.use {
val text = it.readBytes().toString(Charsets.UTF_8)
logD(text)
}
}
Everything works fine. But after I uninstall the app and reinstall it, when I call the readConfig() function, the file variable is null. and strange thing is that when i call the writeToConfig() function again, instead of reusing the same file it creates a new file with this name: "config-test (1).txt".
So after I saw this, I tried to retest the code and I deleted the files (""config-test.txt" and "config-test (1).txt"). After force stopping and relaunching the app I got an Exception: android.database.sqlite.SQLiteConstraintException.
After this exception you cant work with this code anymore and the only way to fix this crash is that you need to wipe data from your device settings.
So we have two bugs in here
To Reproduce
- Use
MediaStoreCompatto write/read a file in Downloads folder. - Unistall the app
- Install the app
- (Bug 1 appears)
- Go to Downloads folder and manually delete the file.
- Force stop the app
- launch the app again.
- (Bug 2 --> Crash)
Stacktrace
FATAL EXCEPTION: main
Process: app.source.getcontact, PID: 8835
java.lang.RuntimeException: Unable to create application com.test.myapp.DebugApp: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6730)
at android.app.ActivityThread.access$1500(ActivityThread.java:247)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2053)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:178)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:557)
at android.content.ContentResolver.insert(ContentResolver.java:2193)
at android.content.ContentResolver.insert(ContentResolver.java:2155)
at com.anggrayudi.storage.media.MediaStoreCompat.createMedia(MediaStoreCompat.kt:143)
at com.anggrayudi.storage.media.MediaStoreCompat.createDownload(MediaStoreCompat.kt:34)
at com.test.myapp.ConfigCache.writeToConfig(ConfigCacheImpl.kt:37)
at com.test.myapp.DebugApp.onCreate(DebugApp.kt:7)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1211)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6725)
at android.app.ActivityThread.access$1500(ActivityThread.java:247)Â
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2053)Â
at android.os.Handler.dispatchMessage(Handler.java:106)Â
at android.os.Looper.loopOnce(Looper.java:201)Â
at android.os.Looper.loop(Looper.java:288)Â
at android.app.ActivityThread.main(ActivityThread.java:7839)Â
at java.lang.reflect.Method.invoke(Native Method)Â
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)Â
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)Â
I'm also able to reproduce these two issues. But, if you try to use another name, e.g. another-config-test.txt, then the error will be disappear. These are OS level issues, not the library. I presume the OS is trying to insert the same file name into SAF database, but the previous name still exists in the database even though the actual file was deleted, hence the unique constraint is not satisfied.
FYI, if you uninstall the app, then all of your files created with MediaStore will be unbound from your app. I don't recommend anyone to use MediaStore if you want to read the files again in the future. Use DocumentFile with storage access instead. MediaStore is only appropriate for write and forget cases, for example saving screenshots and receipts. Additionally, to avoid this crash on your app, generate unique file name for every file creation. You can append random string, integer, or timestamp to your file name.
This issue can't be fixed from my side, but I'll leave it open until Android team resolve it. I'll raise this issue on Google Issue Tracker and will put the link here soon.
I'm trying to implement a ConfigHelper (a configuration file that holds list of key value pairs) that survives from uninstalling app.
As described https://developer.android.com/training/data-storage I can only use MediaStore (SharedPreferences doesn't survive from uninstall and SAF always ask from user to choose a file). So I cannot use DocumentFile because I cannot ask from user to select a file that I need to do some configurations for the app. Actually this ConfigHelper is more like SharedPreferences with the ability of surviving from uninstall.
I didn't find any article about ConfigHelper, can you post the link here @TurKurT656?
@anggrayudi Its my own custom class that behaves like SharedPrefrences. I've named it ConfigHelper its not a public library or something :). Sorry if I confused you
I encountered the same issue, as discussed in https://github.com/lolo-io/OneList/issues/41#issuecomment-1370501337 . The problem is that DocumentFile and DocumentFileHelper never allow to get access to any file whatsoever. I actually reused a helper function that @anggrayudi you mentioned elsewhere, I guess you will recognize what I mean if you see my implementation:
https://github.com/lrq3000/OneList/blob/master/app/src/main/java/com/lolo/io/onelist/util/Utils.kt#L110
This helper function tries to use a DocumentFile, and if it fails, it uses a MediaStore to create or reuse a file. I noticed that in practice, it always uses a DocumentFile. All my attempts at using a DocumentFile manually just miserably failed:
https://github.com/lrq3000/OneList/blob/master/app/src/main/java/com/lolo/io/onelist/dialogs/StorageDialog.kt#L74
I will ask another related question elsewhere, but the culprit is that it appears that all the DocumentFile and DocumentFileCompat functions are now broken. So this issue needs fixing.
ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION can solve your problem from api 30 and above
Your app will be refused from Play Store if you use this permission.
23 mars 2023 06:13:50 Nguyá»…n Minh Khoa @.***>:
ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION can solve your problem from api 30 and above
— Reply to this email directly, view it on GitHub[https://github.com/anggrayudi/SimpleStorage/issues/103#issuecomment-1480613368], or unsubscribe[https://github.com/notifications/unsubscribe-auth/AAIRFXSZ4P4TEXHCIXONITDW5PLY5ANCNFSM6AAAAAARZKBZP4]. You are receiving this because you commented.[Image de pistage][https://github.com/notifications/beacon/AAIRFXQ3DFP2MEADZ3KJPKTW5PLY5A5CNFSM6AAAAAARZKBZP6WGG33NNVSW45C7OR4XAZNMJFZXG5LFINXW23LFNZ2KUY3PNVWWK3TUL5UWJTSYIBO7Q.gif]