Deploying Platform-Signed Android Apps for Development

Developing Android applications that are intended to be pre-installed into a device ROM can be a bit of a chore. One of the strengths of the Android development environment is the ability to immediately deploy you code to a device for testing and demonstration. However, for signed apps in an Android ROM, the Android security model blocks this by default (if anyone could install signature-unmatched applications, it would render a lot of Android's security meaningless).

Attempting to do this by default will show a window like the one below. For pre-installed apps, the instructions often aren't accurate (Android Studio will fail when trying to uninstall the existing application, since the app isn't allowed to be uninstalled).

App install failing when signatures don't match

This can be frustrating. Developers shouldn't have to loose the convenient 'one-click deploy' workflow, even when developing pre-installed ROM apps. And we still want to retain an application's full platform / shared / media permissions when developing and testing those apps.

Luckily, there is a workaround. Here's one method for resolving these code-signing issues :

Assumptions :

  • Your reasonably familiar with the Linux command line (all commands below always run from the root directory)
  • Your using Android Studio 2.0 (or newer, or equivalent) with gradle
  • You have full access to the AOSP source of the ROM of the device you are developing for. (or at least, the keys within it)

Find the keys your Android app is using in the ROM

Start by finding your ROM's Android Source root directory. Then run `ls build/target/product/security`. You should see a list of two files (a *.pk8 and a *.x509.pem) for each of the keys for the various levels of permissions in Android (eight in total).

  • testkey : Default key for packages that don't specify otherwise.
  • platform : Key for packages that are part of the core platform.
  • shared : Key for packages that are shared in the home/contacts process.
  • media : Key for packages that are part of the media/download system.

Typically, one of these is the key your app is signed with. If you don't know which key your using, check your source package directory for your application's Android.mk. In my case, that's running `cat packages/apps/MyAppName/Android.mk`. In this file, you may find a line that reads someting like 'LOCAL_CERTIFICATE := platform', which would tell you the key to use.

If your Android.mk file doesn't specify a key, your probably using the testkey (it's the default). If your packages app directory contains an APK, in some cases the application may be presigned (in which case, you'll need to find the key used to sign it from the source of the APK).

Generate new ROM keys (if necessary)

If you didn't generate these keys yourself (or if someone from your company / organization did not generate these keys) then you might be using default/test ones provided by an OEM or other vendor. Now might be a good time to generate your own. If these keys are missing from the directory entirely, that's another sign to generate your own.

If necessary, you can generate your own keys by running commands like the following below (change the description as necessary to suit your needs)

`./development/tools/make_key build/target/product/security/media '/C=US/ST=Michigan/L=Grand Rapids/O=My Company/OU=My Product Name/CN=Android/emailAddress=hello@example.com'`

Note that you'll want to repeat this for each key (the above is for 'media', you'll probably want a 'platform', 'shared', and 'testkey' too to get your ROM source to compile properly). You'll also want to make sure you either don't use a password, or remember any key passwords you set.

Converting a key/certificate pair to a Java keystore

Now we have a key/certificate pair from the Android device source code. But Android Studio expects to work with a Java keystore -- not a key/certificate directly. We can work around this by creating a new Java keystore and adding our key/certificate to it, by using 'keytool-importkeypair'

  • Fetch the utility 'keytool-importkeypair' from https://github.com/getfatday/keytool-importkeypair `git clone https://github.com/getfatday/keytool-importkeypair`
  • Feed the utility your key and certificate. In our example above, this would be : `keytool-importkeypair -k device.keystore -p android -pk8 build/target/product/security/platform.pk8 -cert build/target/product/security/platform.x509.pem -alias platform`
  • Copy the resulting file `device.keystore` to a convenient location on your computer. I'll be using `~/.ssh/` for this example

Configure Android Studio to sign your app with your newly-created Java Keystore

Inside your app, open your (Module: app) build.gradle file and add a definition under android to contain your new signing configuration. Then, add or modify your build types for debug to include signing using these new keys. (See image below)

build.gradle containing the development signing configuration to overwrite pre-installed, platform-signed applications

Then, run a gradle sync (or clean and rebuild the project) and then attempt to deploy it to a connected Android device. (Shift + F10, in Android Studio)

That should be it. Your app should start up on the connected device. No more dreaded "application can't be installed" messages. And, your app should still have all of the permissions the keys specified.