javaandroidkotlinokhttpphoto-picker

Getting error "open failed: EFAULT (Bad address)" when okhttp tries to send choosed from PhotoPicker image


This error occurs ONLY when an image was choosed from Photo Picker on android 13 (on versions without Photo Picker all works!).

What does code:

User picks image from Photo Picker.

Get image's uri and save file in body, there all is working too:

File imgFile = FilesHelper.INSTANCE.getFileFromUri(this, imgUri); 
postBody.setPhoto(imgFile);
@Throws(Exception::class)
fun getFileFromUri(context: Context, uri: Uri): File? {
    var path: String? = null
    if ("content".equals(uri.scheme, ignoreCase = true)) {
        path = getDataColumn(context, uri, null, null)
    } else if ("file".equals(uri.scheme, ignoreCase = true)) {
        path = uri.path
    }
    return if (!path.isNullOrEmpty()) File(path) else null
}

private fun getDataColumn(context: Context, uri: Uri, 
                          selection: String?, selectionArgs: Array<String?>?): String? {
    var cursor: Cursor? = null
    val column = MediaStore.Images.Media.DATA
    val projection = arrayOf(column)
    try {
        cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
        if (cursor != null && cursor.moveToFirst()) {
            val column_index = cursor.getColumnIndexOrThrow(column)
            return cursor.getString(column_index)
        }
    } finally {
        cursor?.close()
    }
    return null
}

But when body is sending as multipart,

final Map<String, RequestBody> multipart = new HashMap<>(2);
multipart.put("product",
            RequestBody.create(MediaType.parse("application/json"),
            BaseApplication.getGson().toJson(postBody)));
multipart.put("image\"; filename=\"%1.jpg\"",
            RequestBody.create(MediaType.parse("image/jpeg"),
            postBody.getPhoto())); // type File
....  
serverApi.sendBody(multipart); 
....

a warning was shown in logCat:

* /sdcard/.transforms/synthetic/picker/0/com.android.providers.media.photopicker/media/1000000019.jpg: open failed: EFAULT (Bad address)*

and then retrofit/okhttp throws exception.

Retrofit version "com.squareup.retrofit2:retrofit:2.9.0"

Permissions in AndroidManifest, which are given before picking image:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--Android 13 permissions-->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>

And android:requestLegacyExternalStorage="true" in <application> too.

Asking permissions:

if (VERSION.SDK_INT >= 33) arrayOf(Manifest.permission.READ_MEDIA_IMAGES) else arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)

Can I get and send that image without using MANAGE_EXTERNAL_STORAGE permission? Because application distributes via Google Play and is not files manager or something like that. Maybe I should copy file in app's internal files dir or what?


Solution

  • Well, found solution: grant read uri permission

      private void onImagePicked(Uri imgUri){
        if(imgUri!=null) {
          try {
    
            grantUriPermission(getPackageName(),imgUri,Intent.FLAG_GRANT_READ_URI_PERMISSION);
    
            File imgFile = FilesHelper.INSTANCE.getFileFromUri(this, imgUri);
            postBody.setPhoto(imgFile);
          } catch (Exception e) {
            ...
          }
        }
      }