Android : Fix URI Restrictions 🔥


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://
URIsCreates 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:
- 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.
- 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.
- 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.
- 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.
- 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…
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.