androidkotlinpermissionssecurityexception

Kotlin: java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority xxx


My app includes the classic "Share This App" menu option, which takes two screenshots and send them by mail along with a promotional text.

It was working fine before migrating from Java to Kotlin, but now in Kotlin it started to throw an exception.

The weird thing is that even the exception (which can be seen in logcat) the process is working fine, the app is not crashing (at least not in a visible way) and the share process is done, for example, if you choose email the email is received along with the images.

Manifest.xml

<provider
    android:name=".helpers.GenericFileProvider"
    android:authorities="${applicationId}.GenericFileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

GenericFileProvider:

package com.xxx.xxx.helpers

import androidx.core.content.FileProvider

class GenericFileProvider : FileProvider()

provider_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="cache" path="."/>
</paths>

shareFile:

@JvmStatic
fun shareFile(uris: ArrayList<Uri?>?, fileType: String?) {
    try {
        val share = Intent(Intent.ACTION_SEND_MULTIPLE)
        share.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
        share.type = fileType
        val strSubject = getStringResourceByName("mainmenu_sharethisapp_subject")
        share.putExtra(Intent.EXTRA_SUBJECT, strSubject)
        share.putParcelableArrayListExtra(Intent.EXTRA_TEXT, uris)
        share.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
        activity!!.get()!!.startActivity(
            Intent.createChooser(
                share,
                getStringResourceByName("mainmenu_sharethisapp_sharethoughttitle")
            )
        )
    } catch (ex: Exception) {
        Toast.makeText(activity!!.get(), ex.message.toString(), Toast.LENGTH_LONG).show()
    }
}

onImageProcessingFinished:

fun onImageProcessingFinished(ipr: ImageProcessingResult, activity: Activity) {
    val uris = ArrayList<Uri?>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(activity,  activity.packageName + ".GenericFileProvider", file)
        if (uri!=null) uris.add(uri)
    }
    AWDrawerMenu.listener = activity as IActionListeners
    AWDrawerMenu.activity = WeakReference(activity)
    AWDrawerMenu.shareFile(uris, "image/jpg")
}

Exception:

2022-06-16 22:01:08.427 11357-11378/com.xxx.xxx E/DatabaseUtils: Writing exception to parcel
    java.lang.SecurityException: Permission Denial: reading com.xxx.xxx.helpers.GenericFileProvider uri content://com.xxx.xxx.GenericFileProvider/cache/xxx.jpg from pid=6474, uid=1000 requires the provider be exported, or grantUriPermission()
        at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:820)
        at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:684)
        at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:674)
        at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:548)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:327)
        at android.os.Binder.execTransactInternal(Binder.java:1154)
        at android.os.Binder.execTransact(Binder.java:1123)

Edit 1:

If I replace the onImageProcessingFinished method by this next

fun onImageProcessingFinished(ipr: ImageProcessingResult, activity: Activity) {
    val uris = ArrayList<Uri?>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(activity,  activity.packageName + ".helpers.GenericFileProvider", file)
        if (uri!=null) uris.add(uri)
    }
    AWDrawerMenu.listener = activity as IActionListeners
    AWDrawerMenu.activity = WeakReference(activity)
    AWDrawerMenu.shareFile(uris, "image/jpg")
}

adding the .helpers before GenericFileProvider (which in the end is the complete package name for the GenericFileProvider), then the following exception is thrown (an in this case the app DO crashes):

2022-06-16 21:48:10.203 10694-10694/com.xxx.xxx E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.xxx.xxx, PID: 10694
    java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority com.xxx.xxx.helpers.GenericFileProvider
        at androidx.core.content.FileProvider.parsePathStrategy(FileProvider.java:662)
        at androidx.core.content.FileProvider.getPathStrategy(FileProvider.java:635)
        at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:441)
        at com.xxx.xxx.component.drawer.AWDrawer$Companion.onImageProcessingFinished(AWDrawer.kt:294)
        at com.xxx.xxx.activities.main.MainActivity.onImageProcessingFinished(MainActivity.kt:448)
        at com.xxx.xxx.activities.main.MainActivity.onShareThisApp$lambda-6(MainActivity.kt:443)
        at com.xxx.xxx.activities.main.MainActivity.$r8$lambda$c-xNLWLGog-3ah1DvHN4y6HCHsw(Unknown Source:0)
        at com.xxx.xxx.activities.main.MainActivity$$ExternalSyntheticLambda3.onComplete(Unknown Source:4)
        at com.xxx.xxx.helpers.TaskRunner.executeAsync$lambda-1$lambda-0(TaskRunner.kt:24)
        at com.xxx.xxx.helpers.TaskRunner.$r8$lambda$ItjAcdIefpXj05LbTtje15JKSPM(Unknown Source:0)
        at com.xxx.xxx.helpers.TaskRunner$$ExternalSyntheticLambda0.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Solution

  • I ended finding out I was having the exact same problem as described here:

    Permission Denial while sharing file with FileProvider

    And luckily the exact same solution worked for me.

    I had to replace this:

    @JvmStatic
    fun shareFile(uris: ArrayList<Uri?>?, fileType: String?) {
        try {
            val share = Intent(Intent.ACTION_SEND_MULTIPLE)
            share.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
            share.type = fileType
            val strSubject = getStringResourceByName("mainmenu_sharethisapp_subject")
            share.putExtra(Intent.EXTRA_SUBJECT, strSubject)
            share.putParcelableArrayListExtra(Intent.EXTRA_TEXT, uris)
            share.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
            activity!!.get()!!.startActivity(
                Intent.createChooser(
                    share,
                    getStringResourceByName("mainmenu_sharethisapp_sharethoughttitle")
                )
            )
            activity!!.get()!!.startActivity(share)
        } catch (ex: Exception) {
            Toast.makeText(activity!!.get(), ex.message.toString(), Toast.LENGTH_LONG).show()
        }
    }
    

    by this:

    @JvmStatic
    fun shareFile(uris: ArrayList<Uri?>?, fileType: String?) {
        try {
            val share = Intent(Intent.ACTION_SEND_MULTIPLE)
            share.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
            share.type = fileType
            val strSubject = getStringResourceByName("mainmenu_sharethisapp_subject")
            share.putExtra(Intent.EXTRA_SUBJECT, strSubject)
            share.putParcelableArrayListExtra(Intent.EXTRA_TEXT, uris)
            share.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
            /*activity!!.get()!!.startActivity(
                Intent.createChooser(
                    share,
                    getStringResourceByName("mainmenu_sharethisapp_sharethoughttitle")
                )
            )*/
            activity!!.get()!!.startActivity(share)
        } catch (ex: Exception) {
            Toast.makeText(activity!!.get(), ex.message.toString(), Toast.LENGTH_LONG).show()
        }
    }
    

    The chooser looks a little bit different (don't know why), but it works.

    Edit 1:

    OK, the final working code:

    @JvmStatic
    fun shareFile(uris: ArrayList<Uri?>?, fileType: String?) {
        try {
            val strSubject = getStringResourceByName("mainmenu_sharethisapp_subject")
            val strMessage = getStringResourceByName("mainmenu_sharethisapp_message")
            val intent = Intent()
            intent.type = fileType
            intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
            intent.putExtra(Intent.EXTRA_SUBJECT, strSubject)
            intent.putExtra(Intent.EXTRA_TEXT, strMessage)
    
            if (uris!!.size == 0) {
                return
            } else if (uris.size == 1) {
                val uri = uris[0]!!
                intent.action = Intent.ACTION_SEND
                intent.putExtra(Intent.EXTRA_STREAM, uri)
                intent.clipData = ClipData.newRawUri("", uri)
            } else {
                intent.action = Intent.ACTION_SEND_MULTIPLE
                intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
                val clipData = ClipData.newRawUri("", uris[0])
                for (i in 1 until uris.size) {
                    val uri = uris[i]!!
                    clipData.addItem(ClipData.Item(uri))
                }
                intent.clipData = clipData
            }
    
            activity!!.get()!!.startActivity(
                Intent.createChooser(
                    intent, getStringResourceByName("mainmenu_sharethisapp_sharethoughttitle")
                )
            )
        } catch (ex: Exception) {
            Toast.makeText(activity!!.get(), ex.message.toString(), Toast.LENGTH_LONG).show()
        }
    }