How to know whether writing to stream would result in java.io.IOException: write failed: EBADF (Bad file number)
Asked Answered
B

2

0

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.

Barrera answered 19/8, 2015 at 8:0 Comment(11)
what about public static StructStat android.system.Os#fstat(FileDescriptor fd) ? i know its API 21...Penicillin
Looks like this could be a solution in general, but I need this to work down to API 14...Barrera
ok but what about File#canWrite() ? you would need to convert your Uri to File though...Penicillin
I don't understand the question. You know it's happened when you get the exception. Why it happened is another question. Android shouldn't allow it. I suspect you're writing to an invalid pfd object. Or else it's an Android bug.Brinn
@pskink: I don't think I can get a File from a Uri that starts with content:// or am I missing something`? @EJP: I want to know whether I can write to the file without modifying it. However, I only get the exception after trying to write to.Barrera
yep if it starts with content:// you cannot do that...Penicillin
BTW did you try to call ContentResolver#openOutputStream(Uri uri) directly and see if it returns null / throws an exception ?Penicillin
@pskink: Thanks for the suggestion. I just tried it. I successfully get a non null OutputStream and the same exception when I try to write to it.Barrera
seems that i run out of ideas then...Penicillin
last desperate idea: by accident i found two methods which i was not aware (thus never used them actually): Context#checkCallingUriPermission (Uri uri, int modeFlags) or Context#checkCallingOrSelfUriPermission (Uri uri, int modeFlags)Penicillin
Interesting, this looked like a promising idea. In fact both of these functions return -1 (=PERMISSION_DENIED) when called with modeFlages=Intent.FLAG_GRANT_WRITE_URI_PERMISSION on the given uri. Unfortunately they also do so for uris/files to which I can successfully write without the above exception appearing.Barrera
B
0

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.

Barrera answered 6/10, 2015 at 9:56 Comment(0)
B
0

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.

Brinn answered 7/10, 2015 at 10:39 Comment(2)
What I am trying to do here is create an intuitive user interface. I think this is a perfectly legitimate reason for trying to predict the future. Of course exceptions then still need to be handled appropriately.Barrera
There can't possibly be a legitimate reason for trying to predict the future, for the simple reason that it can't be done.Brinn

© 2022 - 2024 — McMap. All rights reserved.