ACTION_OPEN_DOCUMENT: May Not Be Read-Write

While I think that the death of external storage in Android Q
is a good move overall, and while I think that
the Storage Access Framework is OK for basic scenarios,
there will be ���growing pains���.



One of those is the realization that not all document providers play fair.



For example, you might want to write to a user-supplied document. So, you
use ACTION_OPEN_DOCUMENT to request that the user choose a document. You
take the Uri that you get and eventually you call openOutputStream()
on a ContentResolver, passing in that Uri. You then try to write content
out to that stream, to be able to store that content in the user-chosen document.



That might not work.



For example, if the user chose the ���Audio��� category in the Storage Access Framework
UI, you will crash on your openOutputStream() call, with an IllegalArgumentException and
an error message of ���Media is read-only���.



The tactical reason for the crash is that MediaDocumentsProvider ��� the implementation
behind that ���Audio��� option ��� universally rejects write access:



@Override
public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
throws FileNotFoundException {
final Uri target = getUriForDocumentId(docId);

if (!"r".equals(mode)) {
throw new IllegalArgumentException("Media is read-only");
}

// Delegate to real provider
final long token = Binder.clearCallingIdentity();
try {
return getContext().getContentResolver().openFileDescriptor(target, mode);
} finally {
Binder.restoreCallingIdentity(token);
}
}


And, in their document metadata, they correctly indicate that the document is not writable
(i.e., they do not have FLAG_SUPPORTS_WRITE in the document���s flags).



The strategic problem, though, is that we have no means of telling ACTION_OPEN_DOCUMENT
that we want a Uri that we can write to
. As a result, ACTION_OPEN_DOCUMENT will happily let
the user choose a piece of content that we cannot use. This gives the user a poor
experience, as they will now get some cryptic error message from the app about not
being able to write to that location.



This is a framework problem, so we are stuck with this implementation for the time
being. With luck, and with your help, perhaps this gets addressed in the Android R timeframe,
so that in several years, this problem will be behind us. Simply put:
we need some
way to stipulate whether we need read-write access to the content in ACTION_OPEN_DOCUMENT,
with the Storage Access Framework UI limiting the user to read-write documents.
The DocumentsContract.Document API already has FLAG_SUPPORTS_WRITE, so the
Storage Access Framework UI has the information necessary to do the filtering.



In the short term, though, we are stuck with a Uri that may or may not work
for writing.



Ideally, we would use DocumentFile to determine if we can write to a document.
Unfortunately, it does not correctly examine the flags,
so its canWrite() method returns true for a document in which we do not have
write access. Instead, you need around 100 lines of code to check to see
whether the flags for this document contain FLAG_SUPPORTS_WRITE, based on the
the DocumentsContractApi19 implementation of canWrite().



One way or another, if you intend to write to a Uri that you get from
ACTION_OPEN_DOCUMENT, confirm that you actually can write there first, before
trying to use the Uri.

 •  0 comments  •  flag
Share on Twitter
Published on August 06, 2019 09:31
No comments have been added yet.