Brian Long Consultancy & Training Services
Ltd.
January 2012
To install any Android application it must be signed with a certificate related to a key in a keystore. If you were unaware that your Android applications had been signed that would be because the default behaviour of Oxygene for Java is to have the Android SDK tools automatically sign each application with the Android debug key.
The first time you use the Android SDK tool chain by building an application an
Android debug key is created in a keystore in %USERPROFILE%\.android\debug.keystore
.
When apkbuilder is invoked as part of the Android build cycle it signs the app using
the key in this debug keystore. It does this because the Signing page in the project
options defaults to have the Sign app with default Android debug key
option
selected in the Signing
options list.
If you
instead selected No signing
from the list of Signing
options then apkbuilder would be passed
a -u
switch telling it not to sign the package with the debug keystore,
leaving you with an unsigned application that could not be installed.
Why all this information about certificates in keystores? Well,
some time very soon we will need to get some intimate details from the keystore
used to sign our app, give them to Google and get another piece of information back
(called a Map API key). That Map API key will need to be compiled into our app for
the MapView
control to operate correctly at runtime.
We could just use the Android debug keystore to generate this MAP API key, but some forward thinking might suggest this is not such a good idea.
If the goal is to build an app that you might at some point want to publish on the Android Market you should be aware that such an app must be signed with a custom key. The Android debug key is insufficient - an app signed with the Android debug key will not be allowed onto the Android Market.
So it makes sense to take the step of creating yourself a self-signed certificate/key/keystore up front for all your published apps*, so that when you get the Google Map API key based on it, that Map API key will always be usable in your app. Otherwise at some later point when you decide to switch from the default Android debug keystore to a custom keystore, you'll need to get a new Map API key based on the custom keystore and locate and replace all occurrences of the old Map API key with the new one.
*When you publish apps on the Android Market it's your key that identifies you as the publisher. If you use the same key for all your apps, then they will all be associated with the same developer - you. So when you create a custom key for a published app it's really important to keep hold of it so you can use it for future apps and updates to existing apps.
Ok, so assuming you agree that making a new keystore containing a key and self-signed
certificate is a good idea, we'll look at how to do so. However if you want to use
the Android debug key for the time being then that's also just fine. It saves you
a step whilst testing with the MapView
control. You simply need to
reference the Android debug keystore instead of the custom one when generating a
Map API key in a later step, which you can now skip
to.
The Java utility keytool.exe is used to create and manage certificates and keys
in keystores. You may have the Java 6 or Java 7 JDK installed - I have the Java
7 JDK installed in C:\Program Files\Java\jdk1.7.0_02
and I've added
C:\Program Files\Java\jdk1.7.0_02\bin
to my Windows PATH
- you should also add your JDK bin directory to the PATH
for convenience.
keytool appears pretty much identical between the two JDK versions but, just in
case, the JDK 6 and JDK 7 documentation for keytool is available here and here respectively. We'll need to use the -genkeypair
switch (or -genkey
swtch in versions prior to JDK 6), which generates
a public key and associated private key, wraps them in a self-signed X.509 v3 certificate
stored as a single-element certificate chain, and stores the certificate chain and
private key in a new keystore file. You can see the arguments we can pass when using
-genkeypair
by running this command:
C:\Oxygene\Android\com.blong.googleapi>keytool -genkeypair -help keytool -genkeypair [OPTION]... Generates a key pair Options: -alias <alias> alias name of the entry to process -keyalg <keyalg> key algorithm name -keysize <keysize> key bit size -sigalg <sigalg> signature algorithm name -destalias <destalias> destination alias -dname <dname> distinguished name -startdate <startdate> certificate validity start date/time -ext <value> X.509 extension -validity <valDays> validity number of days -keypass <arg> key password -keystore <keystore> keystore name -storepass <arg> keystore password -storetype <storetype> keystore type -providername <providername> provider name -providerclass <providerclass> provider class name -providerarg <arg> provider argument -providerpath <pathlist> provider classpath -v verbose output -protected password through protected mechanism Use "keytool -help" for all available commands
We'll need to specify:
So a full call to keytool, including the -v
switch for verbose output could look
like this:
C:\Oxygene\Android\com.blong.googleapi>keytool -genkeypair -alias googleappkey -dname "CN=Acme Ltd.,O=Acme,C=UK" -storepass googleapp -keypass googleapp -keystore Properties\googleapp.keystore -validity 10000 -keysize 1024 -keyalg DSA -v Generating 1,024 bit DSA key pair and self-signed certificate (SHA1withDSA) with a validity of 10,00 0 days for: CN=Acme Ltd., O=Acme, C=UK New certificate (self-signed): [ [ Version: V3 Subject: CN=Acme Ltd., O=Acme, C=UK Signature Algorithm: SHA1withDSA, OID = 1.2.840.10040.4.3 Key: Sun DSA Public Key Parameters:DSA p: fd7f5381 1d751229 52df4a9c 2eece4e7 f611b752 3cef4400 c31e3f80 b6512669 455d4022 51fb593d 8d58fabf c5f5ba30 f6cb9b55 6cd7813b 801d346f f26660b7 6b9950a5 a49f9fe8 047b1022 c24fbba9 d7feb7c6 1bf83b57 e7c6a8a6 150f04fb 83f6d3c5 1ec30235 54135a16 9132f675 f3ae2b61 d72aeff2 2203199d d14801c7 q: 9760508f 15230bcc b292b982 a2eb840b f0581cf5 g: f7e1a085 d69b3dde cbbcab5c 36b857b9 7994afbb fa3aea82 f9574c0b 3d078267 5159578e bad4594f e6710710 8180b449 167123e8 4c281613 b7cf0932 8cc8a6e1 3c167a8b 547c8d28 e0a3ae1e 2bb3a675 916ea37f 0bfa2135 62f1fb62 7a01243b cca4f1be a8519089 a883dfe1 5ae59f06 928b665e 807b5525 64014c3b fecf492a y: a358cf37 b42c5958 e45c6682 c26ed729 d7c3f431 be2343cb 7322a2aa b4b95e56 bf28b179 8ecb064a 9d22aa8a 7309243b b8684eac 749055aa 6e8a9bc0 f3cbadae 31349de6 6ac63a44 b6ee43a9 77516966 b9917e9d 26cf3064 8a379c24 add42a49 1925085c 5c44de69 64d999ea 76e5a39c 8d9d92d2 b46f6358 32e791fe 6d09c063 Validity: [From: Mon Jan 16 21:06:47 GMT 2012, To: Fri Jun 03 22:06:47 BST 2039] Issuer: CN=Acme Ltd., O=Acme, C=UK SerialNumber: [ 4f6ea8e0] Certificate Extensions: 1 [1]: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 97 75 66 82 37 CB 0F 82 64 0F 72 A7 B2 BB 22 24 .uf.7...d.r..."$ 0010: 4D 72 06 B6 Mr.. ] ] ] Algorithm: [SHA1withDSA] Signature: 0000: 30 2C 02 14 71 52 DF 4D 6F FD A2 24 72 60 DB BC 0,..qR.Mo..$r`.. 0010: B0 D4 4F 3F 62 81 A1 3E 02 14 37 97 E5 99 39 3D ..O?b..>..7...9= 0020: 41 5A 67 78 BA 20 A3 F2 4E FE CA 66 77 15 AZgx. ..N..fw. ] [Storing Properties\googleapp.keystore]
Note: having created a custom keystore, using your own comapny name etc., you'll have no need to do so again unless you want to develop apps that have a different published developer listed.
As you can see from the command-line, the keystore is emitted into the project's
Properties
directory. If you want, you can now add the keystore to
the project so it shows up in Solution Explorer - right-click on the Properties
folder in the Solution Explorer, choose Add
, Existing Item...
,
locate googleapp.keystore
in the Properties
directory
and press Add
.
In order to ensure your app is now signed with this self-signed custom key you will
need to go back to the Signing page of the project properties, select Sign app
with custom key
from the Signing
options list and fill in
the details as per the command-line that created the keystore. During the build
process this alternate signing process is done by the Java jarsigner.exe tool (documented
for JDK 6 and JDK 7 here and here respectively).
Note: to ensure this custom key is used to sign the app in both
Debug mode and Release mode you should use the Configuration
dropdown
and choose All Configurations
before saving the changes to the project
options.
You'll perhaps notice that there are additional options available on the Signing
page over and above what was passed on the keytool command-line: Digest algorithm
and Signature algorithm
. Additionally, if you are using the RTM initial
release of Oxygene for Java, or an old trial download, you may not even see these
options available to you - that's because they were added in the Spring 2012 release,
which should be available to you if you've purchased Oxygene for Java.
What's this all about then? Well, when a key is used to sign an Android package, jarsigner uses a digest algorithm when digesting the unsigned entries of the package. If unspecified, JDK 6's jarsigner will default to using a SHA1 digest algorithm, whereas JDK 7's jarsigner defaults to using SHA-256. It would appear from what I've seen so far that Android really only recognises an SHA1 digest algorithm so it is important to be able to specify this, in case JDK 7 is installed. Of course if you're using JDK 6 there's little worry about.
The signature algorithm identifies the signature algorithm used to create the key in the certificate. Usually the default value works fine in JDK 6 and in JDK 7 but advanced users may need to select a different value, for example if using specially created custom keys.
Note: you may observe above and below that there have been various
changes between JDK 6 and JDK 7 that have the potential to upset the Android development
process (and there are others that are outside this article's remit). Fortunately,
as these have been identified the issues have been reported and Oxygene for Java
has been updated to protect against the installed JDK version.
If you are using the RTM release of Oxygene for Java or the trial version and happen
to have JDK 7 installed, then you should read the signing footnote
that explains how to overcome the problems introduced by JDK 7.
At this point you could make use of the MapView
control and successfully
build and deploy an app to a device. However the MapView
will not render
a map until we do one more thing. The MapView
control requires a
Google Map API key that has been logged against your Google account in order to
operate correctly so here we'll see what's involved in getting one.
The information we require is the MD5 fingerprint of the certificate in your keystore. We can use keytool again to get this information:
C:\Oxygene\Android\com.blong.googleapi>keytool -v -list -alias googleappkey -keystore "Properties\googleapp.keystore" -storepass googleapp Alias name: googleappkey Creation date: 16-Jan-2012 Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Acme Ltd., O=Acme, C=UK Issuer: CN=Acme Ltd., O=Acme, C=UK Serial number: 4f6ea8e0 Valid from: Mon Jan 16 21:06:47 GMT 2012 until: Fri Jun 03 22:06:47 BST 2039 Certificate fingerprints: MD5: 96:6D:83:D9:57:EA:27:D8:A3:28:2F:D8:08:F4:1F:EB SHA1: 0F:08:FB:6D:86:59:60:8B:3C:F1:56:F2:50:F9:C0:F2:02:2A:D4:40 SHA256: 11:3B:33:05:42:3F:A0:C1:59:28:28:FD:A7:F9:1D:C4:C4:1F:6E:37:1F:F3:5A:C7:99:59:AA:37:DE:0D:44:72 Signature algorithm name: SHA1withDSA Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 97 75 66 82 37 CB 0F 82 64 0F 72 A7 B2 BB 22 24 .uf.7...d.r..."$ 0010: 4D 72 06 B6 Mr.. ] ]
Note: in the command above the -v
switch is used to
get verbose information displayed. Without this switch keytool will simply display
a single fingerprint. However, whilst with the JDK 6 version of keytool defaults
to displaying the MD5 fingerprint, which is what we want, JDK 7's jarsigner now
defaults to showing the SHA1 fingerprint. To cover all bases, just use the -v
switch and take the MD5 fingerprint from the list of certificate fingerprints shown.
Having got the MD5 fingerprint of your certificate you then need to visit the Android Maps API Key Signup page. There you can agree to Google's Maps API terms and conditions, plug in your MD5 fingerprint and be presented with your Map API key. For the key that I created with the command-line above I am given an Android Maps API key of:
0OUa1B6eBarw7xYPoFo8dYfMnSbS3gXuBOTpdOg
This value needs to be given to a MapView
control, either in the layout file it is
defined in with the android:apiKey
attribute, or you can pass it to the
MapView
constructor if creating the MapView
control in code.
Now at last.... at long, long, last we can use the MapView
control!
Let's add a new activity to the template Android project to show the Google map.
Right-click on the project node in the Solution Explorer and choose Add
,
New Item...
, choose Activity
from the list, call the source
file GoogleMapActivity.pas
and press Add
.
Let's set up a simple map UI in the layout file
res\layout\googlemapactivity.xml
:
Notice the use of our key-specific Map API key in the MapView
's android:apiKey
attribute.
On the code side, in GoogleMapActivity.pas
, we should change our activity's
ancestor class from the default Activity
class to MapActivity
,
which means we also need to add com.google.android.maps
to the uses
clause. MapActivity
adds in some lifecycle management support and deals
with setup and teardown of MapView
's required services.
Note: the documentation for the MapView
, MapActivity
,
and their associated classes can be found on the Google APIs Add-On reference page.
MapActivity
defines one abstract method we have to override: the protected
isRouteDisplayed()
method. If we are using the map
view to display any kind of route then we should return true
, otherwise
we return false
. This method is just used by Google to collate statistical
information (for accounting purposes) on the usage of the map control.
Another method we are typically expected to override is isLocationDisplayed()
, however since this is not
an abstract method the choice is really ours. This method needs a Boolean
returned indicating if we are displaying the user's current location as ascertained
by any kind of sensor, such as a GPS device.
So the most basic MapView
activity template looks like this:
Before we can test the app and see the results of our work there are still a couple of things left to do. Not least of which is the small matter of ensuring the new map activity can be invoked - currently the template application's main activity just displays numbers when you click the button.
To make the main activity work with the map activity do the following:
Count
variableButtonOnClick
to say:
To tidy things up, in res\values\strings.xml
, remove the definition
of the string my_button_text_2
, then update the app_name
string and add a new string definition as follows:
And finally we can use these strings in the Android manifest by changing the new
automatically added GoogleMapActivity activity declaration line in Properties\AndroidManifest.xml
from this:
to this:
And now we're ready to build!
Go back to the top of this page
Go back to start of this article