How to know whether writing to stream would result

2019-06-13 16:37发布

问题:

I know there are a number of posts here on the java.io.IOException: write failed: EBADF (Bad file number) exception, but non of them seems to answer my particular question:

Suppose my activity is called with Intent.ACTION_VIEW and I got a Uri via Uri uri = intent.getData() that starts with content:// from which I read some data (for example a pdf file). Now I want to find out whether I can also write to that Uri to decide whether a "save" button should be shown to the user, or just a "save as" button.

Suppose further that I can successfully open first a ParcelFileDescriptor and finally a FileOutputStream as in

ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());

such that fileOutputStream != null.

Depending on the Uri it can now happen that if I try to write to fileOutputStream I get the exception:

Exception=java.io.IOException: write failed: EBADF (Bad file number)

I would like to know in advance whether this will happen without actually touching/changing the file. One would think that it should be possible to find out whether I can write to a given Uri or not before trying.

How can I achieve that?

Additional observations:

I suppose that the above happens when I don't have permission to write to that particular file/uri, but then why does Android let me open a FileOutputStream in the first place?

For testing I use attachments in emails received with Kaiten mail on an ICS device. If I my app opens after I click on "save" in Kaiten mail uri matches content://media/external/file/[0-9]* and everything works, if I however clicked on "open" uri matches content://com.kaitenmail.attachmentprovider/[-0-9a-f]*/[0-9]*/VIEW and I run into the above error.

回答1:

The correct way to test whether any resource is available is to try to use it, and handle the exceptions or errors that result when you can't.

Anything else amounts to fortune-telling. You might

  • test the wrong thing
  • test the right thing but get an answer that is correct for the time of the test but not for the time of the actual use. This can work two ways, both bad: the test says you can't, but later you could: or the test says you can, but later you can't.

Don't try to predict the future. Coping with the present is difficult enough.



回答2:

There are apparently two methods:

  1. One can call

    Context.checkCallingUriPermission(Uri uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    

    to check whether the calling process is allowed to write to a given Uri.

    For the cases I could check, on API level < 19 this seems to result in PackageManager.PERMISSION_DENIED whenever the writing to an output stream pointing to uri fails and in PackageManager.PERMISSION_GRANTED in all other cases.

    For API level >= 19 it however yields PackageManager.PERMISSION_DENIED even if one has previously taken a persisted write permission with getContentResolver().takePersistableUriPermission(Uri uri, int takeFlags). In that case however one can use

    context.getContentResolver().getPersistedUriPermissions()
    

    to get a list of all previously taken permissions and then look through them so see if one has permission to write to a given Uri.

  2. If one got he Uri via an Intent intent one can check its flags via intent.getFlags() and see whether Intent.FLAG_GRANT_WRITE_URI_PERMISSION is set. This also seems to be a way to "predict the future".

Obviously, neither of the above methods can be an excuse to not properly handle exceptions that can occur while writing to the stream.