Tuesday, March 31, 2015

Service Intent Must Be Explicit

Soon after releasing our new secure instant messaging app called SHADO on the Google Play Store we got a discrete number of this exception which caused the app to crash on Android Lollipop and up:

java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.android.vending.licensing
.ILicensingService }

This exception concerns the new way how service intents are started or bound on the Android API 21+. Implicit service intents are no longer supported and indeed raise an exception. In this specific case, which is very common, the problem is with the Google Play Licensing API, which, for some strange reasons, Google didn't update to explicit intents since October 2014, when the bug was reported first.

To quickly solve it, you must go with your own rebuilt version of the API, as I will explain below.

First, when you imported the API from <Android-SDK>\extras\google
\play_licensing\library in Eclipse you should have checked it to make a workspace copy of the library. If not, delete the project on Eclipse but do not delete it on the disk, otherwise you will remove it from the Android-SDK local repository, and import again the API checking for making a workspace copy of it.

Second, find the class LicenseChecker.java under the package com.google.android.vending.licensing.

Third, find the following lines of code:

boolean bindResult = mContext
  .bindService(
    new Intent(
      new String(
        Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U="))),
    this, // ServiceConnection.
    Context.BIND_AUTO_CREATE);

And replace them with the following:

boolean bindResult;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
  bindResult = mContext
    .bindService(
      new Intent(
        new String(
          Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")))
          .setPackage("com.android.vending"),
      this, // ServiceConnection.
      Context.BIND_AUTO_CREATE);
} else {
  bindResult = mContext
    .bindService(
      new Intent(
        new String(
          Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U="))),
      this, // ServiceConnection.
      Context.BIND_AUTO_CREATE);
}

At the end, Lint will alert you that LOLLIPOP is not a valid value for android.os.Build.VERSION_CODES. Don't worry. You must open the Properties window of the API project and change the Project Build Target under the Android tab to Android 5.1 to have it rebuilt without problems.

That's all. If you added the API reference to the Properties of your app project, it will import the rebuilt version of the Google Play Licensing API without pain.

Have fun!