The Play Services Problem with Cordova Applications

Simon MacDonald
PhoneGap
Published in
2 min readMay 11, 2018

Well at least I’m affectionally calling it the “Play Services Problem”. It’s not an issue with the Play Services library per say but rather the way in which plugins are including the library in a Apache Cordova Android application.

The Problem

You see if you have Plugin A that includes the ad’s portion of Play Services via the framework tag in the plugin’s plugin.xml file:

<framework src=”com.google.android.gms:play-services-ads:12+” />

Then Plugin B includes the auth portion of Play Services again via the framework tag:

<framework src=”com.google.android.gms:play-services-auth:15.0.0" />

You compile your application and part of that process creates an project.properties file that looks like this:

target=android-27
android.library.reference.1=CordovaLib
android.library.reference.2=app
cordova.system.library.1=com.google.android.gms:play-services-ads:12+
cordova.system.library.2=com.google.android.gms:play-services-auth:15.0.0

So far so good but when your app is running and you call upon something that depends upon the auth portion of Play Services your app will crash with a stack trace that includes a super class or missing class error with com.google.android.gms.internal as the base package. This is because the auth package is expecting a newer version of Play Services.

In some cases you may even run into a compile error but you can fix those before your app gets deployed so it is not quite as serious.

The Fix

Plugin authors should update their plugins to depend on cordova 7.1.0 or greater and cordova-android 6.3.0 or greater. The best way to do this is to add or update the plugins engines tag.

<engines>
<engine name=”cordova” version=”>=7.1.0"/>
<engine name=”cordova-android” version=”>=6.3.0"/>
</engines>

Then authors will be able to use variables in the framework tag. So Plugin A becomes:

<preference name="PLAY_SERVICES_VERSION" default="12+"/>
<framework src=”com.google.android.gms:play-services-ads:$PLAY_SERVICES_VERSION” />

and Plugin B:

<preference name="PLAY_SERVICES_VERSION" default="15.0.0"/>
<framework src=”com.google.android.gms:play-services-auth:$PLAY_SERVICES_VERSION” />

But wait, how does this fix the problem? The two plugins still use different versions of Play Services.

Well now that the version of Play Services are controlled by a preference it is user settable. So now when you add each plugin to your application you can modify the version of Play Services used by each plugin in your apps config.xml.

<plugin name=”pluginA” spec=”npm”>
<variable name=”PLAY_SERVICES_VERSION” value=”15.0.0" />
</plugin>
<plugin name=”pluginB” spec=”npm”>
<variable name=”PLAY_SERVICES_VERSION” value=”15.0.0" />
</plugin>

Now each plugin uses the same version of Play Services and the project.properties file will look like:

target=android-27
android.library.reference.1=CordovaLib
android.library.reference.2=app
cordova.system.library.1=com.google.android.gms:play-services-ads:15.0.0
cordova.system.library.2=com.google.android.gms:play-services-auth:15.0.0

Next Steps

If you run into a plugin that has this issue please create an issue in the plugin’s repo and refer them to this pull request for an example on how to fix the issue. As an added bonus in the comments of the issue please tag me on github by including @macdonst so I can potentially help out with the PR.

--

--

Simon MacDonald
PhoneGap

Father, Software Engineer, Comic Enthusiast, Coffee Lover and Developer 🥑 for @AdobeIO