FileProvider crash - npe attempting to invoke XmlResourceParser on a null String

Android

Android Problem Overview


This is a part of my manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.asd"
    android:versionCode="118"
    android:versionName="118" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

   
    <application
        android:name="com.example.asd.AsdApplication"
        android:allowBackup="true"
        android:allowTaskReparenting="true"
        android:theme="@style/AsdTheme" >
        ...
        
        <provider
            android:name="com.example.asd.database.hq.ContentProviderDB"
            android:authorities="ourContentProviderAuthorities" >
        </provider>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.asd.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>

       ...
    </application>

</manifest>

This is the filepaths file in raw/xml/filepaths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="media"/>
</paths>

I download a video from internet and save it to internal storage this way:

public static boolean saveInputStreamToInternalStorageFile(Context context, String filename, byte[] dataToWrite, Context ctx) {
	FileOutputStream fos;
	try {
		fos = new FileOutputStream(context.getFilesDir() + File.separator + filename);
	
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(dataToWrite);
		oos.close();
		return true;
	} catch (FileNotFoundException e) {
		e.printStackTrace();
		return false;
	} catch (IOException e) {
		e.printStackTrace();
		return false;
	}
}

I try to use it like so:

private void playVideoFromDeviceWithWorkaround(String fileName) {
	
	File newFile = new File(getFilesDir(), fileName);
	Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile);
	
	try {
		vvVideoFullscreen.setVideoURI(contentUri);
		showMediaControls = true;
		playVideo();
	} catch (Exception e) {
		playVideoFromNetwork();
	}

}

At this line:

Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile); 

I get the following error:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:560)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:534)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:376)

Android Solutions


Solution 1 - Android

The problem was that in Manifest I had this line:

android:authorities="com.example.asd.fileprovider"

and when calling getUriForFile I was passing:

Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.example.asd", newFile); 

So changed from "com.example.asd" to "com.example.asd.fileprovider" and it worked

Solution 2 - Android

You can do this without hardcoding the package name with an additional benefit of being able to run multiple variants on the same device (think release and debug with applicationIdSuffix, see these issues):

Based on FileProvider.java:560

final ProviderInfo info = context.getPackageManager()
        .resolveContentProvider(authority, PackageManager.GET_META_DATA);
final XmlResourceParser in = info.loadXmlMetaData( //560
        context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);

you were using the wrong authority and it didn't find the ContentProvider (info == null).

Change your manifest to (${applicationId} will be replaced by Manifest Merger)

android:authorities="${applicationId}.share"

and

Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".share", result);

The .share suffix is optional, in case you have a real ContentProvider which is better to have the package name as the authority.

Solution 3 - Android

In my case, I got the error because the

BuildConfig.APPLICATION_ID

was being imported from

import android.support.v4.BuildConfig;

So the string it returned was "android.support.v4" instead of my project package name. Check out the import file is from your import project.Buildconfig and not another. Example:

import com.example.yourProjectName.BuildConfig;

Finally, in <provider> tag in Manifest I have android:authorities="${applicationId}" to always get my project package name as the authority

<manifest>
   ..
   ..
   <application>
    ..
    ..
       <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/ruta_fileprovider" />
        </provider>

    </application>

</manifest>

Solution 4 - Android

First, be sure that you provider android:authorities does not conflict with your other providers. Besides that you may choose any name for the last part of its name: "provider", "fileprovider" etc., but app crashes when there are more than one android:authorities listed, while documentation states that it allows multiple values listed.

file:// scheme is now not allowed to be attached with Intent on targetSdkVersion >= 24 (Android N 7.0), only content:// is always passed for all devices (Android 5, 6 and 7). But we encountered that Xiaomi breaks this Google convention and sends file://, hence data.getData().getAuthority() gives empty string.

final String uriScheme = currentUri.getScheme();
if ("content".equals(uriScheme)) {
    // getting full file path (works with some providers, i.e. Gallery)
    path = FileUtils.getPath(getContext(), currentUri);
    if (path != null) {
         currentFile = new File(path);
    }
} else if ("file".equals(uriScheme)) {
    // in a rare case we received file:// in currentUri, we need to:
    // 1. create new File variable from currentUri that looks like "file:///storage/emulated/0/download/50044382b.jpg"
    // 2. generate a proper content:// Uri for it
    currentFile = new File(currentUri.getPath());
    String authority = data.getData().getAuthority();
    if (authority != null && !authority.isEmpty()) {
        currentUri = FileProvider.getUriForFile(getActivity(), authority, currentFile);
    }
} else {
    // throw exception
}

Also, the bug when FileProvider.getUriForFile() resulted in crash java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example/files/attachments/image.jpg was fixed in Android Support Library v24.2.0. The problem was that FileProvider.java did not see external-path folders.

Solution 5 - Android

If you're building your AUTHORITY at runtime using BuildConfig make sure you use the full class name including your package name.

Bad:

final String AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";

Good:

final String AUTHORITY = com.mycompany.myapp.BuildConfig.APPLICATION_ID + ".provider";

Solution 6 - Android

Following worked for me.

mUri = FileProvider.getUriForFile(this,
                    BuildConfig.APPLICATION_ID + ".provider",
                    fileObject);

Solution 7 - Android

Here is what i did to fix the issue. I gave fully qualified name in android:name. It works in android 6,7,8

    <provider android:authorities="${applicationId}.opener.provider" 
        android:exported="false" android:grantUriPermissions="true" 
        android:name="io.github.pwlin.cordova.plugins.fileopener2.FileProvider">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
    </provider>

Solution 8 - Android

You should try it:

  Context context = PostAdapter.this.activity;
                    StringBuilder stringBuilder2 = new StringBuilder();
                    stringBuilder2.append(PostAdapter.this.activity.getPackageName());
                    stringBuilder2.append(".provider");
                    Uri uri;
                    uri = FileProvider.getUriForFile(context,stringBuilder2.toString(), newFile);

Solution 9 - Android

This is what you need to do:

Uri fileURI = FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".fileprovider", file);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKaloyan RoussevView Question on Stackoverflow
Solution 1 - AndroidKaloyan RoussevView Answer on Stackoverflow
Solution 2 - AndroidTWiStErRobView Answer on Stackoverflow
Solution 3 - Androidjan4coView Answer on Stackoverflow
Solution 4 - AndroidsoshialView Answer on Stackoverflow
Solution 5 - AndroidVahid AmiriView Answer on Stackoverflow
Solution 6 - AndroidJoseView Answer on Stackoverflow
Solution 7 - AndroidArup NayakView Answer on Stackoverflow
Solution 8 - AndroidChirag PatelView Answer on Stackoverflow
Solution 9 - AndroidMr TView Answer on Stackoverflow