4

From the Google Play Console, I can see that this exception is only happening on devices with Android 8.0+.

android.os.FileUriExposedException: 
at android.os.StrictMode.onFileUriExposed (StrictMode.java:1975)
at android.net.Uri.checkFileUriExposed (Uri.java:2355)
at android.content.Intent.prepareToLeaveProcess (Intent.java:9975)
at android.content.Intent.prepareToLeaveProcess (Intent.java:9950)
at android.content.Intent.prepareToLeaveProcess (Intent.java:9929)
at android.app.Instrumentation.execStartActivity (Instrumentation.java:1622)
at android.app.Activity.startActivityForResult (Activity.java:4748)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult (BaseFragmentActivityJB.java:10)
at android.support.v4.app.FragmentActivity.startActivityForResult (FragmentActivity.java)
at android.support.v4.app.ActivityCompatJB.finishAffinity (ActivityCompatJB.java)
or                                         .startActivityForResult (ActivityCompatJB.java)
at android.support.v4.app.ActivityCompat.a (ActivityCompat.java:6)
at android.support.v4.app.FragmentActivity.a (FragmentActivity.java:8)
at android.support.v4.app.FragmentActivity$HostCallbacks.a (FragmentActivity.java:2)
at android.support.v4.app.Fragment.a (Fragment.java:38)
at android.support.v4.app.Fragment.a (Fragment.java:1)
at de.test.testapp.FileManagerFragment.onClick (FileManagerFragment.java:113)
at android.view.View.performClick (View.java:6291)
at android.view.View$PerformClick.run (View.java:24931)
at android.os.Handler.handleCallback (Handler.java:808)
at android.os.Handler.dispatchMessage (Handler.java:101)
at android.os.Looper.loop (Looper.java:166)
at android.app.ActivityThread.main (ActivityThread.java:7390)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:245)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:926)

the code:

Intent intent = new Intent(Intent.ACTION_VIEW);
ArrayList<Uri> uri = new ArrayList<Uri>();
for (int i=0; i<checkedItems.size(); i++) {
    if (checkedItems.valueAt(i)) {
        intent.setDataAndType(Uri.fromFile(new File(projectDir, filename), "text/plain");
    }
}
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Open RINEX File"));

Manifest:

<?xml version="1.0" encoding="utf-8"?>

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- To access Google+ APIs: -->
<uses-permission android:name="android.permission.INTERNET" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="de.test.testapp"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_providers_paths" />
    </provider>
</application>

file_providers_paths.xml

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

Why does this work on Android 7 but not on Android 8 anymore? What behaviour changes have happened? I am calling this within a fragment.

Armai
  • 162
  • 1
  • 1
  • 10
  • "Unfortunately I do not have a Android 8 device to test here" -- test on an emulator. Also, are you sure that the error that you are seeing comes from this version of the code? – CommonsWare Feb 23 '18 at 12:53
  • Please provide file_providers_path.xml file – Jaimin Thakkar Feb 23 '18 at 13:01
  • `new File(projectDir,listView.getAdapter().getItem(checkedItems.keyAt(i)).toString()))` ??? We do not know to which path that evaluates. Please tell full path of that file. PYou are supposed to post code that is readable. – greenapps Feb 23 '18 at 13:50
  • @Jaimin Thakkar: I added that file content to my post. – Armai Feb 23 '18 at 16:16
  • @greenapps: Sorry for that. Please assume that the file path is correct as this is used in many other aspects within the app and no issues can be noticed there. – Armai Feb 23 '18 at 16:18
  • No. That file path should correspond with that from the provider. So please let us check that. – greenapps Feb 23 '18 at 16:30
  • Hey everyone. I deeply have to apologice since the code I posted here originally was not the right one causing the exception. that one was for sharing the file for general purpose, which was working well. The real code causing the exception was the one for opening the file as a text file, sharing it among text reading apps only. I just edited my post and included the right code (@greenapps: I also made the code more readable as you adviced it (filename)). Please review the new code! – Armai Feb 23 '18 at 16:38
  • I see no full file path. Moreover you checked already a very bad answer. And to me it looks as if it is still the same code. – greenapps Feb 23 '18 at 16:57

3 Answers3

15

Try adding this inside onCreate.

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();
Jay Rathod
  • 11,131
  • 6
  • 34
  • 58
5

If you have an app that shares files with other apps using a Uri, you may have encountered this error on API 24+.

This error occurs when you try to share a file:// Uri in an Intent broadcast to share data with other apps. Using file:// Uri’s are discouraged in this scenario because it makes some assumptions about the destination app. For one thing, we assume that the destination app has READ_EXTERNAL_PERMISSION which may not be the case. If the destination app does not have READ_EXTERNAL_PERMISSION, this may result in unexpected behaviour at best or at worst, result in a crash.

As of Android N, in order to work around this issue, you need to use the FileProvider API.

Step 1: Manifest Entry

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

Step 2: Create XML file res/xml/provider_paths.xml

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

Step 3: Code changes

File file = ...;
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Old Approach
install.setDataAndType(Uri.fromFile(file), mimeType);
// End Old approach
// New Approach
Uri apkURI = FileProvider.getUriForFile(
                         context, 
                         context.getApplicationContext()
                         .getPackageName() + ".provider", file);
install.setDataAndType(apkURI, mimeType);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// End New Approach
context.startActivity(install);
Pankaj Talaviya
  • 3,328
  • 28
  • 31
0

This is cause from android 8 on wards whatever image you take programmatically won't be stored to the gallery, hence you cannot access the gallery files or its URI's, If implementing any image capturing functionality you need to implement and store the captured image to your resources folder and from there you will get the URI. Thats why we do

android:resource="@xml/file_providers_paths"

Actually its good from the security point of view for the user.

Elathan
  • 31
  • 1
  • 6
  • I am not dealing with image files here at all and that line is already included in the manifest file, did you notice that? – Armai Feb 23 '18 at 16:39
  • I am not generalizing it to only image files be it creating text file, I have tried to answer your question Why does this work on Android 7 but not on Android 8 anymore? What behaviour changes have happened? Also I have faced this scenario and have dealt with it without having to write the above code. – Elathan Feb 26 '18 at 07:08