Error Code 20 on Android with Plugin v7.0.0
Bug Report
Problem
I'm seeing error code 20 returned as a result of calls to navigator.camera.getPicture on Android. It's happening pretty regularly with between 2 and 15 reports a day. I'm using cordova-plugin-camera version 7.0.0 with cordova-android 12.0.0
What is expected to happen?
User should be able to use their device camera to take a photo and the image data should be returned to the getPicture success callback.
What does actually happen?
The getPicture error callback is called with error code 20.
Information
This issue was previously reported in #826
I had previously tried using the jalios fork to fix this problem before cordova-plugin-camera version 7.0.0 was released but the jalios fork also resulted in error code 20 reports.
Code
// after deviceready event
let Camera = window.Camera,
options = {
quality: 75,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: false,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 1024,
targetHeight: 1024,
correctOrientation: true
};
navigator.camera.getPicture(
() => {
console.log('success');
},
(error) => {
console.log('error: ' + JSON.stringify(error));
},
options
);
Environment, Platform, Device
On Android devices using cordova-plugin-camera version 7.0.0 and cordova-android 12.0.0
The error has occurred with the following devices (this is not an exhaustive list):
-- Android 12 -- Nokia 5.4 ITEL A60 Samsung Galaxy A21s Oppo A16s Moto G Stylus 5G
-- Android 11 -- Samsung Galaxy A50 Samsung Galaxy A10
-- Android 10 -- Moto G7 Plus Moto G7 Power Moto G7 Play Samsung Galaxy Note9 Samsung Galaxy S9+
-- Android 8.1.0 -- Samsung Galaxy J5
Same here on devices with Android 13, getting the same error:20.
OK, I think I found it. Looking at the source code inside CameraLauncher.java we have public static final int PERMISSION_DENIED_ERROR = 20;. So I did a little investigation and stumbled upon these 2 permissions that are used in this plugin:
https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE Note: Starting in API level 33, this permission has no effect. If your app accesses other apps' media files, request one or more of these permissions instead: READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO. Learn more about the storage permissions that are associated with media files.
https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE. If your app is on a device that runs API level 19 or higher, you don't need to declare this permission to read and write files in your application-specific directories returned by Context.getExternalFilesDir(String) and Context.getExternalCacheDir()
As quick fix - I replaced all READ_EXTERNAL_STORAGE with READ_MEDIA_IMAGES and removed completely WRITE_EXTERNAL_STORAGE everywhere in the code (CameraLauncher.java) and voila - it works.
Todo - make sure it still works on older Android
I'm on an older plugin version and prepared a hotfix for the 5.0.x branche.
https://github.com/apache/cordova-plugin-camera/compare/5.0.x...bstmedia:cordova-plugin-camera:5.0.x
In case anyone else needs this as well. I tested this with Android 5, 9 and 13 and it seems to work fine.
I forked the plugin and added some debug code to get more details. The updated error reports I'm seeing show that Error code 20 happens on Android 7, 8 and 9 when READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions are requested and the READ_EXTERNAL_STORAGE permission is denied.