Support for SD Card on Android 6.0 (Marshmallow)

December 7, 2015

Since Android 4.4 (KitKat) Google keeps making significant changes in how apps can access the removable SD card. In KitKat apps cannot write to folders on SD card. In Android 5.x (Lollipop) there is a new way to write to the SD card, albeit with poor user experience and bad performance.

Android 6.0 (Marshmallow) officially does not introduce new restrictions in SD card access, but it breaks the current API semantics in a subtle way. To delete a file /storage/35A5-BE41/foo/bar.txt on SD card, we are supposed to use code like this

DocumentFile fooDir = DocumentFile.fromTreeUri(context, fooTreeUri);
DocumentFile barFile = fooDir.findFile("bar.txt");
boolean deleted = barFile.delete(); // => true
boolean exist = (new File("/storage/35A5-BE41/foo/bar.txt")).exists(); // => true !!

DocumentFile.delete() is supposed to return true if the file was deleted, otherwise false. That’s what its javadoc says and on a par with what normal File.delete() returns. But if we check for the existence of the file right after seeing deleted == true we’d see it still exists. It’s not the code. If we watch the SD card filesystem we’d indeed see bar.txt really is still there.

What happened? On Marshmallow DocumentFile.delete() is asynchronous. It returns success (true) immediately but the file was only enqueued for deletion. bar.txt is truly gone only after about 10 seconds. That’s when the parent folder contains nothing else, only this one file. If there is a lot of files there the delay can be much longer. This behavior is clearly a breakage of the official DocumentFile.delete() contract.

I have a workaround for Dropsync, OneSync, Autosync for Google Drive, and Autosync for Box. The beta versions are available in Play Store beta channel if you can’t wait. The workaround should work quite well, but I want to work on it some more to minimize the negative impact on sync speed.

Besides this issue Marshmallow also breaks local file change monitoring. Instant upload feature uses FileObserver API class to monitor changes in selected folders. The system notifies registered apps when files are modified, deleted or added. This does not work anymore on Marshmallow. Apps don’t receive any notifications. Not for changes in folders on SD card, not even for changes in folders in device internal storage. There is a bug report filed against Android 6.0 preview. It was closed when 6.0 final was released but obviously without the bug being fixed. Until it’s fixed, Instant Upload in my sync apps does not work and unfortunately it seems there is nothing I can do about it. This is a system level bug which apps cannot workaround.