@ionic-native/zip not working on API 31, open failed: EACCES (Permission denied)

439 Views Asked by At

I am working on a legacy ionic-cordova project, running my android app on API level 31 (upgraded recently). I have android:requestLegacyExternalStorage="true" and the permissions

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

on my AndroidManifest.xml file. Also checked my permissions with 'cordova-plugin-android-permissions' and also 'cordova-diagnostic-plugin' for 'READ_EXTERNAL_STORAGE' & 'WRITE_EXTERNAL_STORAGE' permissions and got granted: true,

But I still can not use the 'cordova-plugin-zip', due to EACCES (Permission denied),

this is the error I get on Android Studio:

2023-01-11 13:06:36.328 14977-22760/es.my-app.my-app E/Zip: An error occurred while unzipping.
    java.io.FileNotFoundException: /storage/emulated/0/Download/some-file.KMZ: open failed: EACCES (Permission denied)
        at libcore.io.IoBridge.open(IoBridge.java:492)
        at java.io.FileInputStream.<init>(FileInputStream.java:160)
        at java.io.FileInputStream.<init>(FileInputStream.java:115)
        at org.apache.cordova.CordovaResourceApi.openForRead(CordovaResourceApi.java:250)
        at org.apache.cordova.CordovaResourceApi.openForRead(CordovaResourceApi.java:233)
        at org.apache.cordova.Zip.unzipSync(Zip.java:84)
        at org.apache.cordova.Zip.access$000(Zip.java:23)
        at org.apache.cordova.Zip$1.run(Zip.java:39)

Previews to the update, the Zip.unzip method from '@ionic-native/zip' used to work fine to decompress my .kmz files, but now it throws an error: -1 due to permissions denied while trying to read from the External Storage.

1

There are 1 best solutions below

0
David Lopes On

Finally, I came up with an OK solution, starting from API 31, to have full access to the external storage there is a particular permission: 'MANAGE_EXTERNAL_STORAGE', in order to register my app for that permission, firstly I had to add it to the 'config.xml' file like so:

<widget xmlns:android="http://schemas.android.com/apk/res/android">
<platform name="android">
    ...
    <config-file parent="/manifest" target="AndroidManifest.xml">
        <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    </config-file>
    ...
</platform>
</widget>

it is important to add xmlns:android="http://schemas.android.com/apk/res/android" at the widget tag.

Then to request the 'MANAGE_EXTERNAL_STORAGE' permission I modified the .java file of the plugin on android studion like so:

...
public class FileChooser extends CordovaPlugin {
    @Override
    public boolean execute(String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R && !android.os.Environment.isExternalStorageManager()) {
            try {
                Intent intent = new Intent();
                intent.setAction(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                Uri uri = Uri.fromParts("package", cordova.getContext().getPackageName(), null);
                intent.setData(uri);
                cordova.getActivity().startActivity(intent);
            } catch (Exception e) {
                Intent intent = new Intent();
                intent.setAction(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
                cordova.getActivity().startActivity(intent);
            }
        }

        .... plugin code ...

    }
}

The execute function is the starting point of execution for the plugin, there, I added the code that requests permission before executing the rest of the plugin processes...

NOTE: with the newer scoped stotage restrictions for android the 'MANAGE_EXTERNAL_STORAGE' permission might make it hard to publish your app on play store, there are other, more complex, strategies to access the external storage that will not cause that trouble.