Wednesday, April 27, 2011

Detecting the Keystore Signature in Code

Here's a problem I'm sure many of us have faced: you've got code that runs one way when you're developing and another way when you're releasing. A few examples I've run into:

- Logging during development, but disabled for release.
- Targeting development vs. production servers.
- MapView requires an API key tied to the signing key.

Before, I just setup static parameters in code that I'd have to remember to change before making a release build. It's a pain and error prone.

I've just determined a much better (and more automatic) way - detecting the signing keystore in code. Through use of the PackageManager you can actually determine the Signature of the app that's currently running.

First, you need to get the Signature of your release keystore. Put this code into your application temporarily, then sign it with your release keystore and run it:

PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
Log.i("test", pi.signatures[0].toCharsString());

Take the output from logcat and put it into this method:

private static final Signature SIG_RELEASE = new Signature("<YOUR SIGNATURE HERE>");

public static boolean isRelease(Context context) {
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature sig : pi.signatures) {
if (sig.equals(SIG_RELEASE)) {
return true;
}
}
}
catch (Exception e) {
Log.w("Exception thrown when detecting if app is signed by a release keystore.", e);

// Return true if we can't figure it out, just to play it safe
return true;
}

return false;
}

Now you can always call isRelease() and check whether your app was signed by your release keystore. No more having to edit files to create a release version of your app - just sign it with the correct keystore and you're ready to go!

3 comments:

  1. Thank you so much for this! Was trying to figure this stuff out myself. Glad I don't have to hack my build.xml to get this functionality :-)

    ReplyDelete
  2. I just check against BuildConfig.DEBUG, mostly to stop calls to Log functions in production builds.

    Maven profiles and the replacer plugin also work wonderfully, especially when you might be testing against a local test server during development and then later you'll still be testing, but against a remote production server. You can just swap profiles.

    ReplyDelete
    Replies
    1. IIRC, when I first wrote this blog post BuildConfig.DEBUG did not exist. It may be a viable alternative now.

      Delete