expo: expo-image-picker fails with exception when launching camera async

READ THIS FIRST, PLEASE!

Hello! Thanks for reporting an issue. Please make sure you’re posting this in the right place:

  1. If this is an issue with Expo CLI, our command line tool, please post it in the https://github.com/expo/expo-cli repo instead.
  2. If this is a feature request, please vote or post it at https://expo.canny.io instead.
  3. If you are unable to create a reproducible Snack example that demonstrates the bug/issue, please first post in our forums at https://forums.expo.io/ before opening an issue here! (Unless the bug/issue pertains to a standalone, non-ejected build)

Thanks for helping us make Expo better!

Environment

Expo CLI 2.11.7 environment info: System: OS: macOS 10.14.3 Shell: 3.2.57 - /bin/bash Binaries: Node: 10.15.3 - ~/.nvm/versions/node/v10.15.3/bin/node Yarn: 1.13.0 - /usr/local/bin/yarn npm: 6.4.1 - ~/.nvm/versions/node/v10.15.3/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman IDEs: Android Studio: 3.3 AI-182.5107.16.33.5314842 Xcode: 10.1/10B61 - /usr/bin/xcodebuild npmPackages: react: 16.6.3 => 16.6.3 react-native: 0.58.6 => 0.58.6 react-navigation: ^3.3.2 => 3.3.2

This is a bare Expo app created with expo init --template bare-minimum and which has the expo-image-picker module added as per https://github.com/expo/expo/tree/master/packages/expo-image-picker

I am running on a Samsung Galaxy S5 (SM-G930F) running Android 8.0.0 Oreo, API Level 26.

Steps to Reproduce

Here’s the code that launches the ImagePicker:

import * as ImagePicker from "expo-image-picker";
import PropTypes from "prop-types";
import { Component } from "react";

class TakePhoto extends Component {
  // Permissions.CAMERA and Permissions.CAMERA_ROLL have already been secured at this point
  componentDidMount() {
    const { onSuccess, onError, onCancel, quality } = this.props;

    ImagePicker.launchCameraAsync({
      allowsEditing: true,
      aspect: [1, 1],
      quality: quality,
      base64: true,
    })
      .then(res => {
        if (!res.cancelled) {
          const file = `data:image/jpeg;base64,${res.base64}`;
          onSuccess(file);
        } else {
          onCancel();
        }
      })
      .catch(error => onError(error));
  }

  render() {
    return null;
  }
}

TakePhoto.propTypes = {
  onSuccess: PropTypes.func.isRequired,
  onError: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
  quality: function(props, propName, componentName) {
    const quality = props[propName];
    if (typeof quality !== "number" || quality <= 0 || quality > 1) {
      return new Error(
        `Invalid prop ${propName} supplied to ${componentName} must be a number in (0, 1].  Validation failed.`,
      );
    }
  },
};

TakePhoto.defaultProps = {
  quality: 0.5,
};

export default TakePhoto;

(Write your steps here:)

  1. Tap the button that mounts the TakePhoto Component.
  2. TakePhoto launches the camera async.
  3. The app instantly crashes with the exception given below.

Expected Behavior

expo-image-picker should NOT crash when we use it to take a picture with the device camera.

Actual Behavior

App crashes with the following exception:

03-11 23:27:52.537  2257  2257 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.bidvidclientmobile/com.bidvidclientmobile.MainActivity}: android.os.FileUriExposedException: file:///data/user/0/com.bidvidclientmobile/cache/ImagePicker/458e779d-3481-4e7d-8e85-caca047b2d41.jpg exposed beyond app through ClipData.Item.getUri()
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3790)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3830)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1746)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:105)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:164)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6944)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: Caused by: android.os.FileUriExposedException: file:///data/user/0/com.bidvidclientmobile/cache/ImagePicker/458e779d-3481-4e7d-8e85-caca047b2d41.jpg exposed beyond app through ClipData.Item.getUri()
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.os.StrictMode.onFileUriExposed(StrictMode.java:1958)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.net.Uri.checkFileUriExposed(Uri.java:2356)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.content.ClipData.prepareToLeaveProcess(ClipData.java:944)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.content.Intent.prepareToLeaveProcess(Intent.java:10480)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.content.Intent.prepareToLeaveProcess(Intent.java:10465)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.Instrumentation.execStartActivity(Instrumentation.java:1616)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.Activity.startActivityForResult(Activity.java:4564)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.Activity.startActivityForResult(Activity.java:4522)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at expo.modules.imagepicker.ImagePickerModule.startActivityOnResult(ImagePickerModule.java:755)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at expo.modules.imagepicker.ImagePickerModule.launchCameraWithPermissionsGranted(ImagePickerModule.java:201)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at expo.modules.imagepicker.ImagePickerModule.access$000(ImagePickerModule.java:60)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at expo.modules.imagepicker.ImagePickerModule$1.onPermissionsResult(ImagePickerModule.java:156)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at expo.modules.permissions.PermissionsService$1.onPermissionResult(PermissionsService.java:56)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at expo.adapters.react.services.UIManagerModuleWrapper$5.onRequestPermissionsResult(UIManagerModuleWrapper.java:217)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at com.facebook.react.ReactActivityDelegate$1.invoke(ReactActivityDelegate.java:201)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at com.facebook.react.ReactActivityDelegate.onResume(ReactActivityDelegate.java:111)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at com.facebook.react.ReactActivity.onResume(ReactActivity.java:64)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1361)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.Activity.performResume(Activity.java:7352)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3765)
03-11 23:27:52.537  2257  2257 E AndroidRuntime: 	... 8 more

Reproducible Demo

I can’t create a snack because this is an expo bare app.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 2
  • Comments: 31 (22 by maintainers)

Most upvoted comments

I’ve provided PR with fix for that, but if you need changes immediately you can go apply it manually 😉

@bbarthec are you sure that your pull request #3743 fixes the issue? I’ve copied the changes into my bare app and I still get the FileUriExposedException.

In order to fix it I’ve had to replace ExpFileUtils.uriFromFile with ExpFileUtils.contentUriFromFile in launchCameraWithPermissionsGranted:

-mCameraCaptureURI = ExpFileUtils.uriFromFile(imageFile);
+mCameraCaptureURI = ExpFileUtils.contentUriFromFile(imageFile, getApplication(null));

The change goes into this line: https://github.com/expo/expo/blob/master/packages/expo-image-picker/android/src/main/java/expo/modules/imagepicker/ImagePickerModule.java#L301

After doing this I got this Exception: 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.

To fix it, in ExpFileUtils.contentUriFromFile I’ve had to change the name of the provider authority to match the one you put in the AndroidManifest.xml in #3743:

-return FileProvider.getUriForFile(application,application.getPackageName() + ".provider", file);
+return FileProvider.getUriForFile(application,application.getPackageName() + ".ImagePickerFileProvider", file);

The names need to match - see https://stackoverflow.com/a/30896464/4034572.

The change goes into this line: https://github.com/expo/expo/blob/master/packages/expo-image-picker/android/src/main/java/expo/modules/imagepicker/ImagePickerModule.java#L807

With this 2 changes it does work. I can now call ImagePicker.launchCameraAsync without the app crashing.

If you want I can open a pull request (would be my first open source contribution 😃 Just let me know.

Hi @cruzach @bbarthec @brentvatne @Szymon20000 @sjchmiela - I realize you are busy so I wanted to help out by creating a test project that repros this crash reliably. That test project is here:

https://github.com/joelgetaction/ExpoImagePickerCameraCrash

You can just download that and run it on an Android emulator (e.g. Nexus 5 API 26) and it will crash every single time in the emulator. You do NOT need an actual Android device to test this.

If somebody has time, could they take a look at this and figure out a fix? 😃 I’ve tried but my Android skills are not strong enough for this.

I’m happy to help however I can, just let me know.

Thanks!

@AlbertVilaCalvo I wish I could, but there’s a lot that changed in the react-native-unimodules that is already affecting this module codebase on master and in other core things. Releasing this module right now and updating it in your project would require a lot of adjustments that aren’t documented anywhere yet, so please wait until next release 😅 Sorry for that inconvenience 😞

Hi @bbarthec , this issue is NOT a duplicate of #3702 . #3702 was about using the image picker to select an existing image from the gallery (and getting a class cast exception) whereas this issue is about using the camera to take a new photo (and getting some kind of file system privacy/sharing exception). Yes both issues occur in the same file ImagePickerModule.java but they are different bugs caused by different things and occur in different usages on different lines and different methods within the file. #3702 was about launchImageLibraryAsync whereas this issue is about launchCameraAsync.

I was the one that submitted the PR to fix #3702 (thanks for approving that by the way 😃 ) but that PR does NOT fix this issue (since this issue still occurs even using the code that includes the fix in that PR). This issue is caused by a totally different section of code.

I’m not sure how to fix this issue here - I tried some fixes on the internet but they didn’t help. Maybe it’s because when expo-image-picker was separated from the classic expo app the API level was boosted to 24 or higher?

So this issue is not a duplicate and please don’t close it as one. Sorry for being so verbose here, I just wanted to make sure the difference between the two issues was clear. 😃

I’m happy to help in any way and I have an Android device I can test any fixes on if that helps.

Any idea how to fix this @bbarthec or @brentvatne or @cruzach ? Thanks!

Tried with Android 8 SDK 26 and Android 9 SDK 28 on various emulated devices + Samsung S3 Hardware device. No difference. 😦

Additional information: react-native 0.58.6 react-native-unimodules 0.2.0 expo-image-picker 4.0.0

buildToolsVersion = "28.0.3"
minSdkVersion = 21
compileSdkVersion = 28
targetSdkVersion = 27
supportLibVersion = "28.0.0"

Picking images from Media Library works.

Edit:

I found the problem. When having rn-fetch-blob installed and linked it doesn’t work. I don’t know why and I won’t dig further. Happily I don’t need that package.

@bbarthec thank you!!! 😃 I tested your fix on a Samsung Galaxy S5 Android 8.0.0 API 26 and it works and doesn’t crash. I can take photos using the camera without issue now.

I really appreciate you helping with this - I know you’re busy and it means a lot to me that you’d fix this so quickly. Thanks again and take care!