I need help with this problem since I'm new to Android.
My app support JellyBean (16) up to Oreo (26).
I have an UploadService that requires openInputStream() to upload data because the new behavior in Nougat.
This code works fine in Marshmallow and below, but always give me SecurityException crash on Nougat. And it crashes on the line where openInputStream() is called with error:
java.lang.SecurityException: Permission Denial: reading com.miui.gallery.provider.GalleryOpenProvider uri content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FIMG_20171008_182834.jpg from pid=30846, uid=10195 requires the provider be exported, or grantUriPermission()
The file uri could be from various app (gallery, camera, etc). I've narrowed down the problem to uri that comes from ACTION_GET_CONTENT intent (anything that comes from camera intent or MediaRecorder works fine).
I think it's because the uri lost its permission when passed into the service, but adding Intent.FLAG_GRANT_WRITE_URI_PERMISSION and Intent.FLAG_GRANT_READ_URI_PERMISSION doesn't help.
Also tried adding FLAG_GRANT_PERSISTABLE_URI_PERMISSION flag, but it still crashes and getContentResolver().takePersistableUriPermission() causes another SecurityException crash saying the said uri hasn't been granted persistable uri...
UploadService.java
//.......... code to prepare for upload
if ( contentResolver != null && schemeContentFile ) {
mMime = UtilMedia.getMime(this, uri);
try {
InputStream is = contentResolver.openInputStream(uri);
byte[] mBytes = getBytes(is);
Bundle fileDetail = UtilMedia.getFileDetailFromUri(this, uri);
Log.d("AndroidRuntime", TAG + " " + mMime + " " + UtilToString.bundleToString(fileDetail) + " imageFile " + mFile);
currTitle = fileDetail.getString(OpenableColumns.DISPLAY_NAME, "");
MediaType type = MediaType.parse(mMime);
requestFile = RequestBody.create(type, mBytes);
} catch ( IOException e ) {
e.printStackTrace();
} catch ( SecurityException e ) {
e.printStackTrace();
}
}
//............continue to upload
Thank You in advance.
EDIT (Additional Info)
In case this is important. The activity calling the service is calling finish() after it sends all the required data to the service, letting user to use the app, while the upload resumed in the background (with notification to tell user). And also, the upload works based on queue, and user can choose to upload multiple files in the activity. The first file always gets uploaded, but the files after always return with the crash.
I finally managed to fix this. Apparently it is because the permission for the given uri is only valid as long as the receiving activity is active. So, sending the uri to a background service (upload service) will result in SecurityException as expected, unless the uri is a persistable uri (ie. from ACTION_OPEN_DOCUMENT).
So my solution is to copy the file to a file my app created and use FileProvider.getUriForFile() function to get the uri and send it instead to the background service and delete the copy when my service finished uploading. This works fine even after the calling activity has finished.
The following code is working fine in my device (android 7):
public void pickImage(View view) {
try {
Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
photoPickerIntent.setType("image/*");
photoPickerIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(photoPickerIntent, RC_PICK_IMAGE);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
}
onActivityResult:
Uri uri = data.getData();
InputStream in = getContentResolver().openInputStream(uri);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With