I inherited a Cordova hybrid application with an error in its manifest file. The targetSdkVersion is set to 24 which, as far as I am concerned doesn't exists yet.
The app wasn't tested on an Android 6 device so it was uploaded to Play Store with that error. Since the permission model on Android 6 has changed the app crashes on those devices.
03-12 21:10:05.991 24366-24475/? E/PluginManager﹕ Uncaught exception from plugin
java.lang.SecurityException: getDeviceId: Neither user 10111 nor current process has android.permission.READ_PHONE_STATE.
at android.os.Parcel.readException(Parcel.java:1620)
at android.os.Parcel.readException(Parcel.java:1573)
at com.android.internal.telephony.ITelephony$Stub$Proxy.getDeviceId(ITelephony.java:4207)
at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java:706)
at org.pgsqlite.SQLitePlugin.getAppKey(SQLitePlugin.java:783)
at org.pgsqlite.SQLitePlugin.executeSqlBatch(SQLitePlugin.java:347)
at org.pgsqlite.SQLitePlugin.executeAndPossiblyThrow(SQLitePlugin.java:195)
at org.pgsqlite.SQLitePlugin.execute(SQLitePlugin.java:93)
at org.apache.cordova.CordovaPlugin.execute(CordovaPlugin.java:95)
at org.apache.cordova.PluginManager.exec(PluginManager.java:130)
at org.apache.cordova.CordovaBridge.jsExec(CordovaBridge.java:59)
at org.apache.cordova.engine.SystemExposedJsApi.exec(SystemExposedJsApi.java:41)
at org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
at org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:39)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
Thanks to this blog I found that if I change the targetSdkVersion to 22 the app works as before http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en
And the documentation shows how to ask for permissions at run time: http://developer.android.com/intl/es/training/permissions/requesting.html
I have several questions regarding this issue.
Here is my manifest.xml file.
<?xml version='1.0' encoding='utf-8'?>
<manifest android:hardwareAccelerated="true" android:versionCode="10000" android:versionName="1.0" android:windowSoftInputMode="adjustResize" package="com.myorg.myapp" xmlns:android="http://schemas.android.com/apk/res/android">
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="24" />
<application android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/app_name">
<blablabla />
</application>
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
</manifest>
If I downgrade the targetSdkVersion to 22 (or 23 for that matter) and upload the app to Play Store, would the app update if I increase the version? I don't really know what would happen.
You can upload a new app built against API 22 to the Play Store and subsequently upload a build against API 23 - this will work fine. However (as @jcesarmobile points out in the comments), we're pretty sure Google won't let you downgrade to API 22 if you have an existing app in the Play Store that's been uploaded with API 23. So your choices here are to fix it for API 23 (best option) or otherwise change the package name and publish it as a new app built with API 22, then remove the old one (not ideal).
Currently there's nothing forcing you to build against API 23, and so in the short term you can continue to build and deploy against API 22. However, I would think at some point Google will require uploaded builds to use API 23, otherwise the type of malicious apps that runtime permissions are designed to combat will just continue building against API 22 and bypassing runtime permissions
How should I work with permissions on Android Marshmallow on a cordova App? Should I ask for permissions int the activity overriding the default one created by Cordova after installation?
Most (if not all) of the core plugins provided by Cordova (e.g. cordova-plugin-camera
), and some 3rd party plugins, have been updated to request the necessary runtime permissions that they use when building/running on API 23. So the first thing is to make sure the plugins in your project are up-to-date with the most recent releases. I wrote a little tool to for managing plugin updates which makes this process a bit easier: cordova-check-plugins.
From the error message you posted, it appears that the crash is due to the READ_PHONE_STATE
permission not having been requested by the plugin, which looks to be the SQLite storage plugin. Not sure if this plugin has been updated to request/step around runtime permissions, so check that first. A simple plugin update might resolve this one.
If not, or for other plugins which don't yet support requesting of API 23 runtime permissions, you have two options:
Modify the plugin source code to request the necessary runtime permissions. This will take a bit of Java surgery. The Cordova plugins make use of a re-usable Permission helper class which makes this easier. You could optionally feed back your changes to plugin authors with a pull request.
Use cordova-plugin-diagnostic to request the permissions required by the non-compliant plugins. This can be done in the Javascript layer and doesn't requiring modifying the plugin source code.
Firstly, you need to determine which permissions the plugin uses. You can look in the plugin.xml for this. If for some reason the plugin doesn't add the relevant <uses-permission/>
element to the AndroidManifest.xml, you can add it explicitly to your config.xml (note that cordova-plugin-dignostic will not add <uses-permission/>
elements for you).
Then wrap the Javascript call to the plugin with a call to cordova-diagnostic-plugin to check and request the relevant runtime permission as appropriate. Using READ_PHONE_STATE
as an example:
function requestPermission(){
cordova.plugins.diagnostic.requestRuntimePermission(function(status){
switch(status){
case cordova.plugins.diagnostic.runtimePermissionStatus.GRANTED:
console.log("Permission granted (or already granted) - call the plugin");
// call SQLite plugin
break;
case cordova.plugins.diagnostic.runtimePermissionStatus.DENIED:
console.log("Permission denied - ask again");
alert("Come on user, we really need this. I'll ask again...");
requestPermission();
break;
case cordova.plugins.diagnostic.runtimePermissionStatus.DENIED_ALWAYS:
console.log("Permission permanently denied");
alert("Well that's it, we're dead Jim");
navigator.app.exitApp();
break;
}
}, function(error){
console.error("The following error occurred: "+error);
}, cordova.plugins.diagnostic.runtimePermission.READ_PHONE_STATE);
}
requestPermission();