Android : Fix URI Restrictions 🔥

Romman SabbirRomman Sabbir
4 min read

When working with files in Android, we often come across content:// URIs from file pickers, downloads, or external storage. However, WebView, third-party libraries, or some APIs might not accept content:// URIs directly, which can cause errors such as:

java.lang.IllegalArgumentException: Uri lacks 'file' scheme

Android enforces strict scoped storage policies, so directly converting content:// to [file://](file://) is no longer allowed. Instead, we use a secure method to create a temporary file and provide access through FileProvider.

The Problem: Why Can't We Use content:// Directly?

Modern Android security policies prevent apps from accessing another app's files directly. If we try to use a content:// URI in WebView or share it externally, we'll often run into:

  • SecurityException (Permission Denied)

  • IllegalArgumentException (URI Lacks 'File' Scheme)

  • File Not Found Exception

To safely access and use content:// URIs, we convert them into a secure [file://](file://) URI using a FileProvider.

The Solution: Convert content:// to a Secure File URI

→ Step 1: Add FileProvider to AndroidManifest.xml

First, register FileProvider inside <application>:

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

The ${applicationId}.fileProvider should match our app's package.

→ Step 2: Define Secure File Paths

Create a new XML file res/xml/provider_paths.xml to specify which directories can be accessed:

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

This ensures only files in cache or external storage can be shared securely.

→ Step 3: Convert content:// to a Secure URI

Now, use this Kotlin function to safely access any Uri:

class FailedToEnableAccessForURI(override val message: String = "Failed to provide access.") : Exception()

fun Uri.grantAppAccess(context: Context): Uri {
    return try {
        // Get the file extension from the original Uri
        val fileExtension = context.contentResolver.getType(this)?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) }
            ?: "tmp" // Default to "tmp" if the extension cannot be determined

        // Open an input stream for the URI (reads the content of the file)
        val inputStream = context.contentResolver.openInputStream(this)
            ?: throw FailedToEnableAccessForURI("Input stream is null.")

        // Create a temporary file in the app's cache directory with the correct extension
        val tempFile = File(context.cacheDir, "upload_${System.currentTimeMillis()}.$fileExtension")

        // Write the input stream's data to the temporary file
        FileOutputStream(tempFile).use { output ->
            inputStream.copyTo(output)
        }

        // Close the input stream after usage
        inputStream.close()

        // Generate a URI for the temp file using FileProvider
        FileProvider.getUriForFile(context, "${context.packageName}.fileProvider", tempFile)

    } catch (e: Exception) {
        throw FailedToEnableAccessForURI().apply {
            this.stackTrace = e.stackTrace
        }
    }
}

→ Step 4: How to Use It?

Use this function whenever you need a secure file URI:

val secureUri = selectedUri.grantAppAccess(context)
webView.loadUrl(secureUri.toString()) // ✅ No more URI access issues!

Why This Works?

  • Bypasses restrictions on content:// URIs

  • Creates a temporary file for easy access

  • Works with WebView, File Uploads, and Third-party APIs

  • Follows Android security policies (Scoped Storage, FileProvider)

What to Keep in Mind if the App is Multi-Modular or Each Module Has Its Own FileProvider

If our app is multi-modular, or each module has its own FileProvider, we need to be careful about URI authority conflicts. Here’s what we need to consider:

  1. Each Module Should Have a Unique Authority

If each module registers its own FileProvider, the authority must be unique.
For example:

  • Main app (com.myapp.main) → "com.myapp.main.fileprovider"

  • Module A (com.myapp.modulea) → "com.myapp.modulea.fileprovider"

  • Module B (com.myapp.moduleb) → "com.myapp.moduleb.fileprovider"

If two modules use the same authority, the app might crash or fail to resolve URIs.

  1. Use the Correct Authority When Generating URIs

If we are calling FileProvider.getUriForFile(), we must use the correct authority for the module handling the file:

FileProvider.getUriForFile(context, "com.myapp.modulea.fileprovider", file)

Do not hardcode the main app’s authority unless the module explicitly shares its FileProvider.

  1. Grant URI Permissions Correctly

When sharing a file URI between modules, we must grant temporary access permissions:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)

Otherwise, the receiving module won’t be able to read the file.

  1. Consider a Single Centralized FileProvider

Instead of multiple FileProviders, we can define one FileProvider in the main app and let all modules use it:

  • Declare FileProvider in the Main App (com.myapp.main.fileprovider)

  • All modules use com.myapp.main.fileprovider instead of their own

This avoids confusion, duplicate authorities, and makes URI access easier across the app.

  1. Debugging Authority Issues

If we get an error like "Couldn't find meta-data for provider", check:

  • Does the module have its own FileProvider?

  • Are we using the correct authority?

  • Does provider_paths.xml exist in the correct module?

TL;DR - Best Practices for Multi-Modular Apps

  • Use unique authorities for each module’s FileProvider

  • Use the correct authority when generating URIs

  • Grant read/write permissions when sharing URIs

  • Consider a single centralized FileProvider to simplify access

With these best practices, we can safely access Uri files in Android without security restrictions—even in multi-modular apps!


That’s for today. Happy Coding…

0
Subscribe to my newsletter

Read articles from Romman Sabbir directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Romman Sabbir
Romman Sabbir

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.