'Cordova 10 and Android SDK 30 : cannot access local file ("Not allowed to load local resource: file:///storage/emulated/0/Android/data...")
I'm trying to update my cordova application to use SDK 30 instead of 29, and I have a problem I cannot resolve.
Apparently, the default value of setAllowAccessFile of the webview changed from true to false between Android SDK 29 and 30. Problem is, in my cordova app, I don't have access to the WebView API, and thus need to change this value with the config.xml file. But I cannot find any option that fits.
Or at least, I think the problem comes from here.
App is an Angular app packaged for Android with Cordova 10. I've tried to switch from Android 8.1.0 to Android 10, to add the new MANAGE_EXTERNAL_STORAGE permission, and a few other stuff I was pretty sure wouldn't work but, well... But nothing changed anything.
If you have any idea of how to do that, or where else the problem could come from, I'd gladly take your advice.
Thanks a lot.
config.xml :
<?xml version='1.0.0' encoding='utf-8'?>
<widget xmlns:android="http://schemas.android.com/apk/res/android" id="my.package.name" version="1.0.6" xmlns="http://www.w3.org/ns/widgets">
<name>My package name</name>
<description>
</description>
<author email="[email protected]" href="https://my.website">
My website
</author>
<content src="index.html"/>
<plugin name="cordova-plugin-whitelist" spec="1"/>
<plugin name="cordova-plugin-wkwebview-engine" spec="https://github.com/driftyco/cordova-plugin-wkwebview-engine.git"/>
<plugin name="cordova-plugin-wkwebview-engine" spec="1.2.1"/>
<plugin name="cordova-plugin-device" spec="~1.1.5"/>
<plugin name="cordova-plugin-statusbar" spec="~2.2.2"/>
<plugin name="cordova-plugin-splashscreen" spec="~4.0.2"/>
<plugin name="cordova-plugin-file" spec="~6.0.0"/>
<plugin name="cordova-plugin-zip" spec="~3.1.0"/>
<plugin name="cordova-plugin-app-version" spec="~0.1.9"/>
<plugin name="cordova-plugin-network-information" spec="~3.0.0"/>
<plugin name="cordova-plugin-save-image" spec="0.2.5"/>
<plugin name="cordova-plugin-customurlscheme" spec="https://github.com/EddyVerbruggen/Custom-URL-scheme.git">
<variable name="URL_SCHEME" value="my.package.name" />
</plugin>
<plugin name="cordova-plugin-intent" spec="https://github.com/napolitano/cordova-plugin-intent.git" />
<feature name="StatusBar">
<param name="ios-package" value="CDVStatusBar" onload="true"/>
</feature>
<preference name="Fullscreen" value="true" />
<preference name="StatusBarOverlaysWebView" value="false"/>
<preference name="DisallowOverscroll" value="true"/>
<preference name="SplashScreen" value="screen"/>
<preference name="SplashScreenDelay" value="3000"/>
<preference name="SplashMaintainAspectRatio" value="true"/>
<preference name="SplashScreenBackgroundColor" value="white"/>
<preference name="android-minSdkVersion" value="21"/>
<preference name="android-targetSdkVersion" value="30"/>
<preference name="Orientation" value="landscape"/>
<access origin="*"/>
<allow-navigation href="http://localhost:8080/*"/>
<feature name="CDVWKWebViewEngine">
<param name="ios-package" value="CDVWKWebViewEngine"/>
</feature>
<preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine"/>
<allow-intent href="http://*/*"/>
<allow-intent href="https://*/*"/>
<allow-intent href="tel:*"/>
<allow-intent href="sms:*"/>
<allow-intent href="mailto:*"/>
<allow-intent href="geo:*"/>
<platform name="android">
<edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
<application android:allowBackup="false"/>
</edit-config>
<edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
<application android:fullBackupContent="false"/>
</edit-config>
<edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
<application android:largeHeap="true" />
</edit-config>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<preference name="AndroidLaunchMode" value="singleTask"/>
<allow-intent href="market:*"/>
<intent-filter>
<action name="android.intent.action.SEND"/>
<action name="android.intent.action.SEND_MULTIPLE"/>
<category name="android.intent.category.DEFAULT"/>
<data mimeType="*/*"/>
</intent-filter>
<intent-filter label="@string/launcher_name">
<action name="android.intent.action.MAIN" />
<category name="android.intent.category.LAUNCHER" />
</intent-filter>
</platform>
<platform name="ios">
<allow-intent href="itms:*"/>
<allow-intent href="itms-apps:*"/>
<preference name="WKWebViewOnly" value="true" />
<feature name="CDVWKWebViewEngine">
<param name="ios-package" value="CDVWKWebViewEngine" />
</feature>
<preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" />
</platform>
<engine name="ios" spec="~5.1.1"/>
<engine name="android" spec="~10.0.0"/>
</widget>
Solution 1:[1]
We dealt with Android 11 storage / security stuff fairly recently in our Cordova app. Here are a few things that we found -- that may or may not be what's going on in your app. More info can be found in my answer here (How to get documents in an android directory that PhoneGap will see -- which I try to keep updated):
Stay away from
MANAGE_EXTERNAL_STORAGE
permission if you can help it. Google is reserving that permission for apps that need to scan the filesystem (I'm guessing virus scanners, etc.) -- other apps will get rejected from the play store.I'm not sure how else to say it, but Android 6.x added an extra set of authorization permissions that your app needs to ask for at runtime (as opposed to in the
AndroidManifext.xml
file). I'm currently using the cordova.plugins.diagnostic plugin, with theANDROIDX_VERSION
flag set to1.0.0
. Here's the relevant code:// request read access to the external storage if we don't have it cordova.plugins.diagnostic.getExternalStorageAuthorizationStatus(function (status) { if (status === cordova.plugins.diagnostic.permissionStatus.GRANTED) { console.log("External storage use is authorized"); } else { cordova.plugins.diagnostic.requestExternalStorageAuthorization(function (result) { console.log("Authorization request for external storage use was " + (result === cordova.plugins.diagnostic.permissionStatus.GRANTED ? "granted" : "denied")); }, function (error) { console.error(error); }); } }, function (error) { console.error("The following error occurred: " + error); }); // request runtime permissions if needed cordova.plugins.diagnostic.getPermissionsAuthorizationStatus(function(statuses){ for (var permission in statuses){ switch(statuses[permission]){ case cordova.plugins.diagnostic.permissionStatus.GRANTED: console.log("Permission granted to use "+permission); break; case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED: console.log("Permission to use "+permission+" has not been requested yet; asking now"); cordova.plugins.diagnostic.requestRuntimePermission(function(status){ console.log("Runtime permission request result: " + status.toString()); }, function(error){ console.error("The following error occurred: "+error); }, permission); break; case cordova.plugins.diagnostic.permissionStatus.DENIED_ONCE: console.log("Permission denied to use "+permission+" - ask again?"); break; case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS: console.log("Permission permanently denied to use "+permission+" - guess we won't be using it then!"); break; } } }, function(error){ console.error("The following error occurred: "+error); },[ cordova.plugins.diagnostic.permission.WRITE_EXTERNAL_STORAGE, cordova.plugins.diagnostic.permission.READ_EXTERNAL_STORAGE ]);
Note that there are other plugins that can ask for the appropriate runtime permissions / authorization. The diagnostic one is the first I came across when I first ran into the issue.
Hope this helps. Our app is on GitHub if you need to compare notes: https://github.com/adapt-it/adapt-it-mobile
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | eb1 |