Cordova - Android Camera Permissions - Granted but DENIED_ALWAYS?

Versions: Quasar 2.16.9, Cordova 12.0.0, Cordova-Android 13.0.0, Cordova-IOS 7.1.1, Cordova Plugin Diagnostic 7.1.4.

I am having a frustrating time integrating a QR code scanner into an app. Everything works fine on iOS, but on Android, I am seeing odd behaviour. Having spent several hours with console.log debugging, and capturing thousands of lines of adb logs, I am stumped.

What I know:

  • On a fresh install, when my component mounts, I call cordova.plugins.diagnostic.getCameraAuthorizationStatus(). As expected, it returns NOT_REQUESTED.
  • If I see NOT_REQUESTED, then I pop up an explanation for the user about why we need camera access. Once they acknowledge, I call cordova.plugins.diagnostic.requestCameraAuthorization (with ‘external storage’ = false). This pops up the camera permission dialog. I say “allow while using the app”.
  • In the adb logs, I can see that this appears to work. Below is an extract of what I think are the relevant log entries:
08-28 08:10:11.981  9553  9553 I chromium: [INFO:CONSOLE(401)] "[QRCodeScan] Beginning requestCameraPermissions()", source: https://localhost/js/21.js (401)
...
08-28 08:10:11.983  9553  9708 V Diagnostic: Get authorisation status for android.permission.CAMERA
08-28 08:10:11.984  9553  9708 V Diagnostic: Get authorisation status for android.permission.READ_MEDIA_IMAGES
08-28 08:10:11.985  9553  9708 V Diagnostic: Get authorisation status for android.permission.READ_MEDIA_VIDEO
08-28 08:10:11.986  9553  9708 D Diagnostic: Requesting permission for android.permission.CAMERA
08-28 08:10:11.986  9553  9708 D Diagnostic: Requesting permission for android.permission.READ_MEDIA_IMAGES
08-28 08:10:11.986  9553  9708 D Diagnostic: Requesting permission for android.permission.READ_MEDIA_VIDEO
08-28 08:10:11.986  9553  9708 V Diagnostic: Requesting permissions
...
08-28 08:10:13.805  9553  9553 V Diagnostic: Received result for permissions request id=746214
...
08-28 08:10:13.815  9553  9553 V Diagnostic: Authorisation for CAMERA is GRANTED
08-28 08:10:13.817  9553  9553 V Diagnostic: Authorisation for READ_MEDIA_IMAGES is DENIED_ALWAYS
08-28 08:10:13.818  9553  9553 V Diagnostic: Authorisation for READ_MEDIA_VIDEO is DENIED_ALWAYS
...
08-28 08:10:13.853  9553  9553 I chromium: [INFO:CONSOLE(404)] "[QRCodeScan] Result from requestCameraAuthorization:  DENIED_ALWAYS", source: https://localhost/js/21.js (404)

It seems that Cordova Diagnostics seems that CAMERA permission was GRANTED, and yet when the result is returned to my app it has changed to DENIED_ALWAYS. What have I missed?

Incidentally, this is an update to an existing app, which worked fine targeting SDK 33, but we’re now targeting SDK 34. I don’t think anything changed in the permissions model between 33 and 34 and so I think my manifest is correct. In particular, I have in config.xml:

    <platform name="android">
        <allow-intent href="market:*" />
        <preference name="AndroidXEnabled" value="true" />
        <edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
            <application android:debuggable="true" />
        </edit-config>
        <config-file mode="merge" parent="/manifest" target="AndroidManifest.xml">
            <uses-permission android:name="android.permission.INTERNET" />
            <uses-permission android:name="android.permission.CAMERA" />
            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
            <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
            <uses-feature android:name="android.hardware.camera" />
            <uses-feature android:name="android.hardware.camera.autofocus" />
            <uses-feature android:name="android.hardware.location" />
        </config-file>

… and I have confirmed that those permissions are making their way through to the platforms/android/app/src/main/AndroidManifest.xml file as well.

Some more information here. I added some debugging logging to the Cordova diagnostics plugin.

So getCameraAuthorizationStatus is receiving this back from `Diagnostic.instance._getPermissionsAuthorizationStatus()":

 Diagnostic_Camera: {"CAMERA":"GRANTED","READ_MEDIA_IMAGES":"DENIED
_ALWAYS","READ_MEDIA_VIDEO":"DENIED_ALWAYS"}

But then somewhere between receiving that and handing off the JSONObject to callbackContext.success(statuses);, we lose the fact that there are in fact 3 different permissions being returned, and somehow my callback is only getting DENIED_ALWAYS even thoughg CAMERA was granted.

I tracied callbackContext.success() through to a call to Cordova Webview’s sendPluginResult but I do not know how that is implemented. It seems to be ‘losing’ th at information though.

Update: A previous developer apparently was passing the “external storage” flag not as a boolean, but as an object, the first key of which was a boolean set to false. This apparently did not have the desired effect, because it resulted in requestCameraAuthorization and getCameraAuthorizationStatus requesting not only CAMERA permission, but also READ_MEDIA/WRITE_MEDIA. However, the callback parameter that requestCameraAuthorization and getCameraAuthorizationStatus uses is only a single string – so if it requests multiple permissions at once, it can only report ONE of those values back to the application. That, seemingly, is not the first value in the set.

This seems to me to be a bug in the Diagnostics module. If you request multiple permissions, there must be some way for multiple responses to be provided back to the application callback - but at present that doesn’t seem to work. Maybe most people don’t run into it because if they are asking for all 3 permissions, they actually only care whether all succeed or fail, not if they get camera but not media?

GitHub issue opened: Requesting camera AND media access permissions together results in ambiguous return value · Issue #519 · dpa99c/cordova-diagnostic-plugin · GitHub