Wrestling Google API keys

Google uses API keys to track and bill your usage of their APIs. While creating and using those keys is generally well documented and even partly automated you are in for some surprises.

This post focuses on the speed bumps you’ll encounter especially when creating an Android app using Google Maps.

Surprises in the App’s API Key Infrastructure

When you add a Google Maps activity to an app the AndroidManifest.xml file will contain this element:

<!-- ... -->
  <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="YOUR_API_KEY"/>       
<!-- ... -->

While the “Get API Key” documentation initially tells you to just replace YOUR_API_KEY with the actual API key that’s not a good idea – surprise #1.

A few paragraphs later it tells you to paste your key into some google_maps_api.xml file instead where the interesting element looks like this:

<!-- ... -->
 <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">
        ADD_API_KEY_HERE
    </string>
<!-- ... -->

Replacing ADD_API_KEY_HERE with the API key is better but still not good enough – surprise #2.

The problem is in both cases you’ll come away with the idea of using a single API key. But you’ll need at least two because an app’s debug and release build types use different certificates which makes two API keys mandatory as they are issued for a combination of package name and certificate finger print.

The good news: your project contains two google_maps_api.xml files:

  • one in …\app\src\debug\res\values
  • one in …\app\src\release\res\values

Android Studio will automatically supply you with the correct google_maps_api.xml file when switching between debug and release build types. Also the respective file’s value will replace the one in AndroidManifest.xml. So just copy the respective API key into each google_maps_api.xml file and both release and debug variants of your app can use Google Maps.

An easy way to check this is to open AndroidManifest.xml and use the Merged Manifest tab. That will show the file’s content after merging and considers the currently selected build type. When you select the respective attribute you can even see where its value came from.

By the way: one reason for failed authentication is using the wrong package name. It’s determined by the applicationId in build.gradle – the package attribute in AndroidManifest.xml only determines the project’s structure.

Surprises with Caching

Once an app successfully authenticates with an API key the result is cached. That means even if you for example provide a wrong key afterwards your app will only break after you uninstalled and reinstalled it – surprise #3.

How to Keep Your API Keys Private and Find More Surprises

Google’s solution requires you to store their API keys in the code base. That’s not what you want to do for example in an open source project.

Gradle to the rescue: it can read values from files outside the project and make them available for example in AndroidManifest.xml.

In the app’s build.gradle file add lines like these:

...
    defaultConfig {
        applicationId "de.dbremes...."
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1"
        Properties props = new Properties()
        props.load(new FileInputStream("$project.rootDir/../../MyApp/MyApp.properties"))
        manifestPlaceholders = [apiKey: "${props.getProperty("debug_api_key")}"]
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            Properties props = new Properties()
            props.load(new FileInputStream("$project.rootDir/../../MyApp/MyApp.properties"))
            manifestPlaceholders = [apiKey: "${props.getProperty("release_api_key")}"]
        }
    }
...

This example requires a file named MyApp.properties in a MyApp directory relative to your project’s directory. Don’t worry about getting the directory structure wrong: once you sync Gradle it will inform you if it can’t find the file. The file’s content should look like this:

debug_api_key=AIzaSy...7J0g
release_api_key=AIzaSy...tCmc

Then change your AndroidManifest.xml file like this:

<!-- ... -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${apiKey}" />
<!-- ... -->

Finally delete both google_maps_api.xml files.

When building the app Gradle will replace the variable in AndroidManifest.xml with the value for the respective build type. And the Merged Manifest tab of AndroidManifest.xml will be helpful gain.

In my case I blindly copied code from a previous project that required quotes around the value. The logcat output complaining about the authorization failure showed them but it took me a while to realize they weren’t supposed to be there:

... E/Google Maps Android API﹕ Authorization failure. Please see developers.google.com/maps/documentation/android-api/start for how to correctly set up the map.
... E/Google Maps Android API: In the Google Developer Console (https://console.developers.google.com)
  Ensure that the "Google Maps Android API v2" is enabled.
  Ensure that the following Android Key exists:                                                                               	
    API Key: "AIzaSy...Q4"                                                                               	
    Android Application (<cert_fingerprint>;<package_name>): 
    CC:6E:...:62:41;de.dbremes....

The logcat output will show the actual values sent to Google’s servers. So an Ctrl+C here followed by a Ctrl+F, Ctrl+V in the Google Developer Console makes it easy to check the values match – unless you fall in a similar trap like me.

But this post just has to end with another surprise. When you change an API key in the external file Gradle won’t pick up that change and even switching between build types and recompiling the project won’t help – surprise #4. The solution is to close and reopen Android Studio.

Additional Resources

Advertisements
This entry was posted in Android and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s