48

I'm trying to make Camera App to store the output to my internal storage. I also understand that third party apps are not able to access the Internal Storage of my application BUT we are able to do so by exposing the internal directory through FileProvider. I have followed the guide here:

I specify my AndroidManifest.xml in the following way:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.stackoverflow.test.camerawithfileprovider" >

    <application>
        <activity
            ...
        </activity>

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.stackoverflow.test.camerawithfileprovider.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true" >
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_provider_path" />
        </provider>
    </application>

</manifest>

I created a xml file named file_provider_path.xml in /res/xml/:

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

This is how I create and call the Camera Intent:

private static final int CAMERA_REQUEST_CODE = 5000;
private static final String CAMERA_FP_AUTHORITY = "com.stackoverflow.test.camerawithfileprovider.fileprovider";

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

Context context = MainActivity.this;
File imagePath = new File(context.getFilesDir(), "public");
if (!imagePath.exists()) imagePath.mkdirs();
File newFile = new File(imagePath, "tmp.jpg");

Uri imageUri = FileProvider.getUriForFile(context, CAMERA_FP_AUTHORITY, newFile);
//context.grantUriPermission("com.google.android.GoogleCamera", imageUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.d("YouQi", "Image URI Passing to Camera: " + imageUri.toString());

intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, CAMERA_REQUEST_CODE);

When I run the application, I can get the below Logcat result:

    11-11 20:07:39.581 18303-18303/com.stackoverflow.test.camerawithfileprovider D/YouQi: Image URI Passing to Camera: content://com.stackoverflow.test.camerawithfileprovider.fileprovider/image_capture/tmp.jpg

Right at the moment when the camera closes, I hit the below Exception:

com.google.android.GoogleCamera E/AndroidRuntime: FATAL EXCEPTION: main
com.google.android.GoogleCamera E/AndroidRuntime: Process: com.google.android.GoogleCamera, PID: 19420
com.google.android.GoogleCamera E/AndroidRuntime: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{42e44f70 19420:com.google.android.GoogleCamera/u0a83} (pid=19420, uid=10083) that is not exported from uid 10312
com.google.android.GoogleCamera E/AndroidRuntime:     at android.os.Parcel.readException(Parcel.java:1465)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.os.Parcel.readException(Parcel.java:1419)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2882)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.app.ActivityThread.acquireProvider(ActivityThread.java:4544)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2274)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1425)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:906)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.content.ContentResolver.openOutputStream(ContentResolver.java:669)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.content.ContentResolver.openOutputStream(ContentResolver.java:645)
com.google.android.GoogleCamera E/AndroidRuntime:     at com.android.camera.PhotoModule.onCaptureDone(PhotoModule.java:1281)
com.google.android.GoogleCamera E/AndroidRuntime:     at com.android.camera.PhotoModule$8.onClick(PhotoModule.java:593)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.view.View.performClick(View.java:4445)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.view.View$PerformClick.run(View.java:18446)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.os.Handler.handleCallback(Handler.java:733)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:95)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:136)
com.google.android.GoogleCamera E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5146)
com.google.android.GoogleCamera E/AndroidRuntime:     at java.lang.reflect.Method.invokeNative(Native Method)
com.google.android.GoogleCamera E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:515)
com.google.android.GoogleCamera E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:732)
com.google.android.GoogleCamera E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:566)
com.google.android.GoogleCamera E/AndroidRuntime:     at dalvik.system.NativeStart.main(Native Method)

Can anyone help to explain why the problem happen? I have created a sample project of my problem: http://www.axzae.com/downloads/CameraWithFileProvider.zip

Anyway, the app will work if I explicitly specify:

context.grantUriPermission(
    "com.google.android.GoogleCamera",
    imageUri,
    Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION
);

But this will only work for nexus/cyanogen devices. Samsung camera app might use a different package name.

Community
  • 1
  • 1
You Qi
  • 8,353
  • 8
  • 50
  • 68

2 Answers2

107
  1. Please do not try to put exported="true" as pointed out by pskink, your app will crash the moment it loads. FileProvider is not meant to work in this state.

  2. Tried intent.addFlags solution by CommonsWare. Not working. probably will only works with ACTION_SEND kind of intent.

  3. I found the answer in this post. Apparently the manual granting way is the only solution. But we can always loop thru the list of candidate packages and grantPermission to all of them.

    List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
    
Community
  • 1
  • 1
You Qi
  • 8,353
  • 8
  • 50
  • 68
  • 5
    Very satisfying hack :) – Laurent Meyer Jun 23 '16 at 13:11
  • 29
    `addFlags()` works on Android 5.0+. On older devices, your approach is required. See https://github.com/commonsguy/cw-omnibus/tree/master/Camera/FileProvider for a sample. – CommonsWare Jul 18 '16 at 10:33
  • 2
    I have a doubt here. I am sorry for my very less knowledge of Android, but I am confused. Before launching the camera, I am setting the directory as "Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES"). Do I still need to use FileProvider OR is it only used for images stored in private app area ? – AndroidGuy Jul 26 '16 at 06:45
  • @CommonsWare, your approach works as expected thanks. Also, I think that if you use intent.seClipData(ClipData.newRawUri(null, uri)); should also work. – Amilcar Andrade Oct 04 '16 at 15:35
  • Surprisingly, `intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);` worked for me on Android SDK 18 (version 4.3 Jellybean). – Rock Lee Oct 07 '16 at 19:41
  • I also found that addFlags did not work on API 4.4 or below in this case for me. The "hack" specified in the answer here worked! – zaifrun Oct 10 '16 at 13:41
  • I had to do this only when trying to share with the gmail app. Other apps worked witthout this (Twitter, Facebook, Instagram, SMS). Anyone knows the reason behind such behavior? – Tooroop Oct 10 '16 at 21:49
  • @CommonsWare Will this solution work for Android 5.0+ versions also? – Sreekanth Karumanaghat May 29 '17 at 05:53
  • @SreekanthKarumanaghat: As I wrote in [an earlier comment](https://stackoverflow.com/questions/33650632/fileprovider-not-working-with-camera/33652695?noredirect=1#comment64274394_33652695), `addFlags()` is sufficient for Android 5.0+. – CommonsWare May 29 '17 at 11:13
  • As @CommonsWare mentioned addFlags() works for Android 5.0+ and explicit URI permission works for versions below 5.0. One thing to remember If you are using the image capture intent as the question states, then you mostly expect a URI for the image captured. Be Aware that the data is null for versions below Android 6.0 if you set MediaStore.EXTRA_OUTPUT. You are safe to use the URI you passed to the intent after checking the result is OK. So make sure to have a reference to the file URI for versions below Android 6 – Raaja SN Apr 12 '18 at 19:29
  • Also note that [Google's tutorial on how to take photos](https://developer.android.com/training/camera/photobasics) doesn't use `addFlags()` at all so it's probably not needed any longer for recent Android versions. – Andreas Jan 23 '19 at 14:16
0

I had a similar issue but later on I find out to have a typo in my code. The typo was in AndroidManifest.xml, precisely into the tag at the line "authorities".

For me,

android:authorities="com.stackoverflow.test.camerawithfileprovider.fileprovider"

doesn't work (when I call the camera instance, the app crashes) but if i delete "file" and replace that string as following:

android:authorities="com.stackoverflow.test.camerawithfileprovider.provider"

my app runs smoothly (the app takes the photo, stores it, and gives back a preview to the activity in the client app).

Does your app also have the same issue ?