apache / cordova-plugin-camera

Apache Cordova Plugin camera
https://cordova.apache.org/
Apache License 2.0
966 stars 1.55k forks source link

Camera plugin crashes app on some android phones when an image is taken #345

Closed hamzatrq closed 1 month ago

hamzatrq commented 6 years ago

So the plugin works on most of the devices, it works on IOS perfectly. But on android it works on some devices and on some devices once the picture is taken the user is send back to app, app crashes. Now the only thing that I am doing is storing this image in an array. Now one more thing is that if I take a picture and click on retake and then submit the second image the app does not crashes but if I submit the first image it crashes.

Here is my function:


uploadImage() {
    this.actionCtrl.create({
      title:  'Upload Image',
      buttons: [
        {
          icon: 'camera',
          text: 'Open Camera',
          handler: () => {
            const options: CameraOptions = {
              quality: 100,
              destinationType: this.camera.DestinationType.DATA_URL,
              encodingType: this.camera.EncodingType.JPEG,
              mediaType: this.camera.MediaType.PICTURE,
              sourceType: 1
            }

            this.camera.getPicture(options).then((imageData) => {
              let base64Image = 'data:image/jpeg;base64,' + imageData;
              this.images.push(base64Image);
              console.log(base64Image);
            }, (err) => {
              console.log('Error Uploading File')
            });
          }
        },
        {
          icon: 'image',
          text:  'Open Gallery',
          handler: () => {
            const options: CameraOptions = {
              quality: 100,
              destinationType: this.camera.DestinationType.DATA_URL,
              encodingType: this.camera.EncodingType.JPEG,
              mediaType: this.camera.MediaType.PICTURE,
              sourceType: 0
            }

            this.camera.getPicture(options).then((imageData) => {
              let base64Image = 'data:image/jpeg;base64,' + imageData;
              this.images.push(base64Image);
            }, (err) => {
              console.log('Error Uploading File')
            });
          }
        }
      ]
    }).present();
  }
alemit commented 4 years ago

I am using FILE_URI destination type still facing the issue with front camera and when I click on retake and then submit the second image the app does not crash

If someone can provide a sample reproduction app that produces this issue, it will help move this ticket along.

https://github.com/apache/cordova-contribute/blob/master/create-reproduction.md

You are not right about the FILE_URI. The app can crash even when using FILE_URI! It's a matter of memory management from Android. This behavior can easily be simulated in the following way: "It’s easy to reproduce/simulate the problem. To do so, you have to set “always destroy activities” to true in the developer settings of your Android phone. Doing so, each time you gonna take a photo, your app in the background gonna be killed and recreated (as cordova does when an Android phone is running on low memory)" On one of our test devices(Samsung Galaxy S5) this is also easily reproducible(without this setup) when taking photo in some kind of night portrait mode - while taking photo without light it uses some special mode which takes more time to shoot and obviously more memory. Screenshot_2020-01-19-13-33-22 1

mirko77 commented 4 years ago

I'm curious if anyone else has experimented with artificially throttling the rate at which the app allows the user to capture photos, and seen any positive impact. To prove this idea out, we added a 5 second delay between allowed photo captures, and saw a definite reduction in crashes/UI refreshes. Are we chasing ghosts?

No that does not affect it

mirko77 commented 4 years ago

Has anyone tried to use the camera preview plugin instead? https://github.com/cordova-plugin-camera-preview/cordova-plugin-camera-preview

alemit commented 4 years ago

To me looks like Capacitor will solve all this stupid problems we all have. Unfortunately the migration is a little scary for me and I still don't feel confident using it...

breautek commented 4 years ago

You are not right about the FILE_URI. The app can crash even when using FILE_URI! It's a matter of memory management from Android.

I may have caused some confusion in my past statements, but to be clear, I'm not saying FILE_URI will 100% solve the problem, but using it over DATA_URL will significantly help. Base64 explodes the memory usage by about 40%, it is an incredibly inefficient way to represent binary data. Which exacerbates memory usage issues on android and causes the activity to close when in the background. If you're using DATA_URL, then this is step 1 of solving the problem.

The next step is handling activity closures. If the activity is killed, it is up to the developer to handle resume. Which means they need to store whatever state necessary in a safe location so that they can return to that state on the resume event, should the activity be killed. Additionally, your camera results will be stored in the resume's event pendingResult. This is documented here and here. This step is necessary to support all devices.

aximobile commented 4 years ago

You are not right about the FILE_URI. The app can crash even when using FILE_URI! It's a matter of memory management from Android.

I may have caused some confusion in my past statements, but to be clear, I'm not saying FILE_URI will 100% solve the problem, but using it over DATA_URL will significantly help. Base64 explodes the memory usage by about 40%, it is an incredibly inefficient way to represent binary data. Which exacerbates memory usage issues on android and causes the activity to close when in the background. If you're using DATA_URL, then this is step 1 of solving the problem.

The next step is handling activity closures. If the activity is killed, it is up to the developer to handle resume. Which means they need to store whatever state necessary in a safe location so that they can return to that state on the resume event, should the activity be killed. Additionally, your camera results will be stored in the resume's event pendingResult. This is documented here and here. This step is necessary to support all devices.

How can we get to the 'resume' event if the app simply restarts ? I would love to handle any app destroy but we just dont get there :(

'Older' Android devices seem to have zero issues because of the fact that images have lower quality on these devices and therefore use less memory.

ReallySmallSoftware commented 4 years ago

I'm reaching a bit here - but I see that the error reported in issue #554 is on line 803 of CameraLauncher. I have been trying to debug a problem with resuming after the activity has been destroyed and hit the same error on that line.

My investigations suggest that the use of applicationId is the problem. If the activity has been destroyed and is recreated then applicationId will not be set because it gets set in the execute() method. If I fiddle with the code locally and ensure it gets set within onActivityResult then that problem goes away.

You can recreate this 100% is you set allowEdit to true and on the device change the developer settings to destroy activities.

sirhaplo commented 4 years ago

My investigations suggest that the use of applicationId is the problem. If the activity has been destroyed and is recreated then applicationId will not be set because it gets set in the execute() method. If I fiddle with the code locally and ensure it gets set within onActivityResult then that problem goes away.

You can recreate this 100% is you set allowEdit to true and on the device change the developer settings to destroy activities.

But this referer to the "allowEdit" branch of the if. If not in allowEdit applicationId is not used. With allowEdit the app crash ( maybe for the applicationId as you reporti ) but without the app restart anyway.

ReallySmallSoftware commented 4 years ago

@sirhaplo That is true, but your own stacktrace in #554 does have allowEdit true, and a lot of the comments in this issue don't give actual examples of the settings that are being used.

My feeling is that we are dealing with multiple issues: Some failure to handle the resume event, possibly some of this issue and maybe others.

The app restart (or at least activity restart) is expected behaviour and the issue I found is a concrete example of when the app totally crashes out and it isn't possible to capture the resume event. For example, the comment by @aximobile suggests the resume event is never fired and this could cover that particular situation but we just don't know what options @aximobile was using.

nabwill commented 4 years ago

to simulate https://github.com/peterpeterparker/ionic-zurich-cordova-android-quirks

to handle https://github.com/peterpeterparker/ionic-zurich-cordova-android-quirks/tree/quirks

reference: https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html#lifecycle-guide

nabwill commented 4 years ago

I disable default camera of motorola and install Open Camera and no more restarts.

https://play.google.com/store/apps/details?id=net.sourceforge.opencamera

I`m really thinking to use a customize camera based in openCamera - https://sourceforge.net/projects/opencamera/

mirko77 commented 4 years ago

I disable default camera of motorola and install Open Camera and no more restarts.

https://play.google.com/store/apps/details?id=net.sourceforge.opencamera

I`m really thinking to use a customize camera based in openCamera - https://sourceforge.net/projects/opencamera/

We ended up telling our users to install Open Camera if they face issues with our app, as Cordova does not provide any consistent workaround. @nabwill it would be great to have a Cordova plugin based on Open Camera, like the barcode scanner one. So a stripped-down version of Open Camera is launched instead of whatever camera the users have installed, and that would prevent crashes.

This is also interesting https://github.com/cordova-plugin-camera-preview/cordova-plugin-camera-preview

breautek commented 4 years ago

it would be great to have a Cordova plugin based on Open Camera, like the barcode scanner one. So a stripped-down version of Open Camera is launched instead of whatever camera the users have installed, and that would prevent crashes.

The problem with this solution is if your app accesses the camera APIs directly, which I suspect is the case if we use any camera library, then your app needs "dangerous"[1] permissions, and this will go against the recommendations of Google for most users (who simply needs to open a camera to snap a picture and/or access the public image library).

For most of these users, Google wants the app to use something they call Intents, which allows apps to use another already installed app, to do a particular task, in this case... the privacy-sensitive task. This however, does pose a problem if user chooses to use a camera app that is buggy.

[1] Camera API permissions are considered dangerous because they grant the app the ability to start video recording or snapping pictures without necessary their consent because you can use the API without a user gesture. This leads to huge privacy concerns, and Google will take this seriously.

With that being said, that option is still on the table for someone to build a plugin around Open Camera, or a similar camera library. I just don't think it's an option for Apache to provide.

mirko77 commented 4 years ago

it would be great to have a Cordova plugin based on Open Camera, like the barcode scanner one. So a stripped-down version of Open Camera is launched instead of whatever camera the users have installed, and that would prevent crashes.

The problem with this solution is if your app accesses the camera APIs directly, which I suspect is the case if we use any camera library, then your app needs "dangerous"[1] permissions, and this will go against the recommendations of Google for most users (who simply needs to open a camera to snap a picture and/or access the public image library).

For most of these users, Google wants the app to use something they call Intents, which allows apps to use another already installed app, to do a particular task, in this case... the privacy-sensitive task. This however, does pose a problem if user chooses to use a camera app that is buggy.

[1] Camera API permissions are considered dangerous because they grant the app the ability to start video recording or snapping pictures without necessary their consent because you can use the API without a user gesture. This leads to huge privacy concerns, and Google will take this seriously.

With that being said, that option is still on the table for someone to build a plugin around Open Camera, or a similar camera library. I just don't think it's an option for Apache to provide.

I am not sure I understand... If I install Open Camera on the side, I will have to accept these "dangerous" permissions anyway, I assume a camera app will use the camera APIs directly? How is that different from providing the same functionality but embedded in a Cordova app? My app needs to take pictures, so the user must give permissions to do so.

For example, if I am developing a native app that takes pictures, I will write native code that uses the camera APIs, I will not call a third-party camera app using an intent. If I take a picture on Instagram, it does not open my camera app, that functionality is embedded. I have not seen any weird permissions when I installed Instagram.

breautek commented 4 years ago

If I install Open Camera on the side, I will have to accept these "dangerous" permissions anyway, I assume a camera app will use the camera APIs directly? How is that different from providing the same functionality but embedded in a Cordova app? My app needs to take pictures, so the user must give permissions to do so.

The difference is that your app itself is not using these dangerous permissions. A third-party app that is already vetted by Google consumes that task for your app.

For example, if I am developing a native app that takes pictures, I will write native code that uses the camera APIs, I will not call a third-party camera app using an intent. If I take a picture on Instagram, it does not open my camera app, that functionality is embedded. I have not seen any weird permissions when I installed Instagram.

Instagram is using the Camera permission. And probably does so, so they can implement filters and other camera-related functionality on top of the camera API. ie. they are not just snapping photos.

This is google's recommendations for when you should use an intent, or if you should use the camera APIs directly: https://developer.android.com/guide/topics/media/camera#considerations

Like I said, it all depends on your use case. In your case, using the camera APIs directly may be an better option for your app.

But I would not agree on changing apache's plugin to use the camera APIs and require apps to use the camera permissions. I would defer to the community to build a third-party plugin for use cases that requires the use of camera APIs.

mirko77 commented 4 years ago

If I install Open Camera on the side, I will have to accept these "dangerous" permissions anyway, I assume a camera app will use the camera APIs directly? How is that different from providing the same functionality but embedded in a Cordova app? My app needs to take pictures, so the user must give permissions to do so.

The difference is that your app itself is not using these dangerous permissions. A third-party app that is already vetted by Google consumes that task for your app.

For example, if I am developing a native app that takes pictures, I will write native code that uses the camera APIs, I will not call a third-party camera app using an intent. If I take a picture on Instagram, it does not open my camera app, that functionality is embedded. I have not seen any weird permissions when I installed Instagram.

Instagram is using the Camera permission. And probably does so, so they can implement filters and other camera-related functionality on top of the camera API. ie. they are not just snapping photos.

This is google's recommendations for when you should use an intent, or if you should use the camera APIs directly: https://developer.android.com/guide/topics/media/camera#considerations

Like I said, it all depends on your use case. In your case, using the camera APIs directly may be an better option for your app.

But I would not agree on changing apache's plugin to use the camera APIs and require apps to use the camera permissions. I would defer to the community to build a third-party plugin for use cases that requires the use of camera APIs.

Given the increasing number of crashes on more and more devices, I do not see any other viable solution.

nabwill commented 4 years ago

Could someone say the reason for the application not to restart with the open camera? I think we could analyze the callback routine.

nabwill commented 4 years ago

To resolve the issues that multiple users have reported. I had to implement my cordova-camera plugin and I was successful. In my tests there were no more errors with mobiles with low memory, because now the call is being made directly to the camera and no longer to a third camera application.

1) In the ionic application I sent the code "uuid" + .jpg and the application folder where the photo will be saved.

2) In the PluginResult object: setKeepCallback (true);

3) In the onActivityResult method, I check if the file was saved and return the request status to the front end.

I would really like to understand why cordova-camera-plugin does not call the api directly.

I would like to suggest that the developer also has the option of using the direct call to the camera

Privacy issues can be included in the app's privacy policy and that's it.

Do you agree with my point of view?

mirko77 commented 4 years ago

To resolve the issues that multiple users have reported. I had to implement my cordova-camera plugin and I was successful. In my tests there were no more errors with mobiles with low memory, because now the call is being made directly to the camera and no longer to a third camera application.

  1. In the ionic application I sent the code "uuid" + .jpg and the application folder where the photo will be saved.
  2. In the PluginResult object: setKeepCallback (true);
  3. In the onActivityResult method, I check if the file was saved and return the request status to the front end.

I would really like to understand why cordova-camera-plugin does not call the api directly.

I would like to suggest that the developer also has the option of using the direct call to the camera

Privacy issues can be included in the app's privacy policy and that's it.

Do you agree with my point of view?

Is your custom plugin a public repo? I'd like to have a look.

ghwrivas commented 3 years ago

We decided not to use this plugin and build our own web component to take photos. It is based on the WebRTC API. Here is an example how to use it: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos

mirko77 commented 3 years ago

We decided not to use this plugin and build our own web component to take photos. It is based on the WebRTC API. Here is an example how to use it: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos

Looks good. Any experience on Xiaomi devices?

ghwrivas commented 3 years ago

Hi, I don't have experience in that device you comment, our app only will used in a specific device (samsung)

thirdwheel commented 3 years ago

Same was observed on a Nokia 2.1 running Android 10. User needed his phone so not currently able to test.

aximobile commented 3 years ago

@sirhaplo That is true, but your own stacktrace in #554 does have allowEdit true, and a lot of the comments in this issue don't give actual examples of the settings that are being used.

My feeling is that we are dealing with multiple issues: Some failure to handle the resume event, possibly some of this issue and maybe others.

The app restart (or at least activity restart) is expected behaviour and the issue I found is a concrete example of when the app totally crashes out and it isn't possible to capture the resume event. For example, the comment by @aximobile suggests the resume event is never fired and this could cover that particular situation but we just don't know what options @aximobile was using.

@ReallySmallSoftware apologies for my late answer. I did include the options in the ticket I created: https://github.com/apache/cordova-plugin-camera/issues/554

zyhzx123e commented 3 years ago

Tried background mode enable b4 open camera and disable it after done takenPic, the app is now ok , will not restart

cyptus commented 3 years ago

Using cordova-plugin-background-mode. Enable before camera and then disable after camera is back. It fixed the problem.

after banging my head against some walls for over 2 days... thanks god this works. using FILE_URI does not solve the random crashs - it maybe reduces the chance to get one.

5hee75 commented 3 years ago

You can recreate this 100% is you set allowEdit to true and on the device change the developer settings to destroy activities.

Thank you so much! I'm actually using the Capacitor Camera plugin and am seeing similar weird crashes, as described here, and you just saved me days of troubleshooting. This reproduces the behavior exactly!

nabwill commented 3 years ago

In fact, the Capacitor Camera solve crashs in hibrid apps. This plugin (capacitor camera) is recommended to use for mobile phones with low memory. But, is needed disable a crop function to android android SDK > 29.

ynagasundeep commented 3 years ago

Oh god, this issue still stays.....

On Thu, 5 Aug, 2021, 20:12 nabwill, @.***> wrote:

In fact, the Capacitor Camera solve crashs in hibrid apps. This plugin (capacitor camera) is recommended to use for mobile phones with low memory. But, is needed disable a crop function to android SDK > 30.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/apache/cordova-plugin-camera/issues/345#issuecomment-893515570, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFJPJYLPGC7FNUYVZUQ5GI3T3KPMBANCNFSM4FVZJL3A . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&utm_campaign=notification-email .

mcrio commented 3 years ago

Starting seeing this issue on some Xiaomi Redmi Note 5G phones recently. We never used Redmis and MIUI before.

Examining the debug logs the app is killed when the camera app opens, before the picture is even taken:

W/cr_ChildProcessConn: onServiceDisconnected (crash or killed by oom): pid=9006 bindings:W  S state:3 counts:0,0,0,1,
E/chromium: [ERROR:aw_browser_terminator.cc(123)] Renderer process (9006) crash detected (code -1).
E/chromium: [ERROR:aw_browser_terminator.cc(89)] Render process (9006) kill (OOM or update) wasn't handed by all associated webviews, killing application.

Updating my old Ionic 3, Android 6.4.0 app to Android 10 seems to have resolved it but as it's a big jump and I'm not yet comfortable taking it without good testing. I haven't also done extensive testing, maybe the problem just appeared less.

Implementing the solution by not allowing the app to sleep in background mode seems to have helped so far. App is not crashing anymore on the Redmi Note but I need to examine if that introduced misbehavior for other phones and android versions. BackgroundMode plugin

The Android behavior with Intents and killing apps by default is very weird. Especially with Xiaomi MIUI which kills the main app although there is enough RAM. At least I'd love to see a configuration option that would allow specifying different scenarios like keeping the main app in memory and if there is a problem with RAM to kill the activity activated as an Intent, or kill both, etc.

mirko77 commented 3 years ago

@mcrio we have positive results with this foreground plugin, which we forked due to some crashes. It helps mitigate the issue, however not perfect. A lot of work is going towards other approaches like Camera Preview but unfortunately, it is too buggy for the time being.

sithwarrior commented 3 years ago

@mcrio We have bought a Xiaomi Redmi Note 5G, exactly to test this, as we see the phone crashing in our Android Crash Analytics, but we cant make the phone crash in our test, how are you reproducing the crashes?

Unfortunately we cant update to Cordova Android 10, as the Push Notification Plugin, dosen't work with that version yet.

mirko77 commented 3 years ago

@sithwarrior try to take like 50 photos in a row, it should crash at some point. It could happen after the first one, or after 50, it is completely random.

mcrio commented 3 years ago

@sithwarrior I was able to reproduce this all the time on Cordova Android 6, 7... Didn't get back to further testing with 10 as I have other troubles with it. Running old Ionic 3 app.

The problem seems to be the following:

@mirko77 suggested to use the Foreground plugin. It requires Cordova Android 7 and I need to stay with 6.4 because of some weird compiling issues with my project and Cordova Android 7 that I don't have time to resolve now.

I sticked to the BackgroundPlugin but it's crashing very often.

Here is my fork that prevents the plugin from crashing: https://github.com/mcrio/cordova-plugin-background-mode but I am not sure if that introduces some other side effects like memory leaks as I don't have any experience with Android Native and Cordova Plugin development.

mcrio commented 3 years ago

...according to the logs the app seems to be killed at the point the Camera opens, before the picture is even taken so I'm taking away the assumption that the main app dies because it runs out of memory when processing the image (Redmi Note 5G has a 48MPx cam)

mirko77 commented 3 years ago

@mcrio memory does not have anything to do with this issue, it is the way background tasks are dealt with in some roms. A more aggressive approach like on Xiaomi (but also the latest Samsung, Huawei etc..) kills the app when it goes to the background. It is not limited to photo taking, any background task can be killed at random if it is not a system app or a popular app the manufacturer decided to whitelist. More info https://dontkillmyapp.com/

breautek commented 1 month ago

Stale