[This post is by Ellie Powers, a product manager on the Google Play team. — Tim Bray]
Today we are releasing an update to the SDK Tools and the Eclipse plugin. Revision 17 brings a lot of new features and bug fixes in various areas such as Lint, the build system as well as the emulator.
Lint is a static checker which analyzes Android projects for a variety of issues around correctness, security, performance, usability and accessibility, checking your XML resources, bitmaps, ProGuard configuration files, source files and even compiled bytecode. It can be run from within Eclipse or from the command line.New for r17:
We’ve also made improvements to the build systems for Eclipse and Ant:
The emulator is seeing some big improvements as well:
Finally, we are also releasing an updated Support Library with the following improvements:
You can get more information about these changes in the SDK Tools Release Notes and ADT Release Notes.
[This post is a group effort by Tony Chan, Fred Chung, Brian Carlstrom, and Kenny Root. — Tim Bray]
Android 4.0 (ICS) comes with a number of enhancements that make it easier for people to bring their personal Android devices to work. In this post, we’re going to have a look at the key store functionality.
Back in Android 1.6 (Donut), a system key store was added for use by VPN. Although this was later expanded to support WiFi authentication, applications weren’t able to access it.
In the past, it was common practice for apps to maintain their own key store if they needed to authenticate a secure SSL web server, or authenticate the user to a server via a client certificate. While this works, it can present manageability issues in an enterprise environment where multiple certificates may be shared across a number of apps such as Email and Browser.
To bridge the gap in ICS, there’s a new API named KeyChain that regulates application access to the system key store and allows users to grant application access to the credentials stored there. Additionally, this API enables applications to initiate installation of credentials from X.509 certificates and PKCS#12 key stores.
The KeyChain API is rather simple. To install a key store or a certificate, you retrieve an install intent, supply the raw bytes of the credentials, and use the intent to launch a system installation dialog. If it’s a keystore, as in the example below, you’ll need provide the data in PKCS#12 format, and the user will have to know the PKCS#12 password.
byte[] keystore = . . (read from a PKCS#12 keystore) Intent installIntent = KeyChain.createInstallIntent(); installIntent.putExtra(KeyChain.EXTRA_PKCS12, keystore); startActivityForResult(installIntent, INSTALL_KEYSTORE_CODE);
The install intent launches a system dialog that prompts the user to enter the password for the keystore.
This can also be used for installing organizational CA certificates which will then be trusted by all applications to authenticate to non-public servers with certificates issued by the same CA.
In ICS, Android no longer requires a separate password to protect the system credential storage. Rather, it uses the screen lock password for this purpose, and the Android Device Administration API can be used for central policy enforcement. This means, for example, that the screen lock password can’t be removed as long as the secured credentials remain on the device.
Once the system key store is configured, the KeyChain API offers functions such as requesting a client certificate for authenticating with an SSL server. The first time an application requests access, the user is prompted with a list of available certificates and can select one to grant access to that certificate to the application. If the user chooses to allow access to a certificate, a string alias name for the certificate is returned to the application. The application can then use the alias to access the certificate in the future without further user involvement.
The code below illustrates how an application can prompt the user to select a credential alias and grant access to the application. KeyChain will remember this selection such that the same application can save the credential alias selection and have access to the same certificate in future. For example, the Email application for ICS has implemented this feature in its Server Settings screen.
KeyChain.choosePrivateKeyAlias(this, new KeyChainAliasCallback() { public void alias(String alias) { // Credential alias selected. Remember the alias selection for future use. if (alias != null) saveAlias(alias); } }, new String[] {"RSA", "DSA"}, // List of acceptable key types. null for any null, // issuer, null for any "internal.example.com", // host name of server requesting the cert, null if unavailable 443, // port of server requesting the cert, -1 if unavailable null); // alias to preselect, null if unavailable
Once an application has been granted access to the certificate, it can access the private key through the getPrivateKey() method. It is worth noting that as with any PrivateKey objects, the application should not make assumptions about the encoding. For example, on some implementations the PrivateKey object may just be an opaque representation of a key stored in a hardware key store.
Here’s a sample code snippet that demonstrates the use of private key retrieved from the key store for signing:
PrivateKey privateKey = KeyChain.getPrivateKey(context, savedAlias); if (privateKey != null) { ... Signature signature = Signature.getInstance("SHA1withRSA"); signature.initSign(privateKey); ... }
A common use of the private key is for SSL client authentication. This can be implemented by using an HttpsURLConnection with a custom X509KeyManager that returns the PrivateKey retrieved from the KeyChain API. The open source Email application for ICS uses KeyChain with an X509ExtendedKeyManager. To learn more, have a look at the source code (in SSLUtils.java).
This API provides a unified way to access the system key store credentials. If your application uses client certificates (take note: enterprise email client or web browser developers) you should definitely look into the KeyChain API for your next update!
Android applications have historically been limited to a maximum size of 50MB. This works for most apps, and smaller is usually better — every megabyte you add makes it harder for your users to download and get started. However, some types of apps, like high-quality 3D interactive games, require more local resources.
So today, we’re expanding the Android app size limit to 4GB.
The size of your APK file will still be limited to 50MB to ensure secure on-device storage, but you can now attach expansion files to your APK.
Each app can have two expansion files, each one up to 2GB, in whatever format you choose.
Android Market will host the files to save you the hassle and cost of file serving.
Users will see the total size of your app and all of the downloads before they install/purchase.
On most newer devices, when users download your app from Android Market, the expansion files will be downloaded automatically, and the refund period won’t start until the expansion files are downloaded. On older devices, your app will download the expansion files the first time it runs, via a downloader library which we’ve provided below.
While you can use the two expansion files any way you wish, we recommend that one serve as the initial download and be rarely if ever updated; the second can be smaller and serve as a “patch carrier,” getting versioned with each major release.
In order to make expansion file downloading as easy as possible for developers, we're providing sample code and libraries in the Android SDK Manager.
In the Google Market Licensing package, an updated License Verification Library (LVL). This minor update mostly adds the ability to obtain expansion file details from the licensing server.
From the Google Market APK Expansion package, the downloader service example. The library makes it relatively simple to implement a downloader service in your application that follows many of our best practices, including resuming downloads and displaying a progress notification.
Because many developers may not be used to working with one or two large files for all of their secondary content, the example code also includes support for using a Zip file as the secondary file. The Zip example implements a reasonable patching strategy that allows for the main expansion file to “patch” the APK and the patch file to “patch” both the APK and the main expansion file by searching for asset files in all three places, in the order patch->main->APK.
Expansion files have a specific naming convention and are located in a specific place for each app. As expansion files are uploaded to the publisher site, they are assigned a version code based upon the version of the APK that they are associated with. The naming convention and location are as follows:
Location: <shared-storage>/Android/obb/<package-name>/Filename: [main|patch].<expansion-version>.<package-name>.obbExample: /sdcard/Android/obb/com.example.myapp/main.5.com.example.myapp.obb
<shared-storage>/Android/obb/<package-name>/
[main|patch].<expansion-version>.<package-name>.obb
/sdcard/Android/obb/com.example.myapp/main.5.com.example.myapp.obb
Expansion files are stored in shared storage. Unlike APK files, they can be read by any application.
When the primary activity for the app is created, it should check to make sure the expansion files are available. The downloader library provides helper functions (for example the “Helpers” class in the code below) to make this easy.
boolean expansionFilesDelivered() { // get filename where main == true and version == 3 String fileName = Helpers.getExpansionAPKFileName(this, true, 3); // does the file exist with FILE_SIZE? if (!Helpers.doesFileExist(this, fileName, FILE_SIZE, false)) { return false; } return true; }
If the file does not exist, fire up the downloader service with DownloaderClientMarshaller.startDownloadServiceIfRequired(). The downloader will perform an LVL check against the server. This check will deliver the names of the files, file sizes, and the file URLs.
DownloaderClientMarshaller.startDownloadServiceIfRequired()
Once that check has been completed, it will begin downloading the files. You don’t have to use our download solution, but you might want to because we:
Include a notification UI that provides progress and estimated completion time in layouts customized for ICS and pre-ICS devices
Resume large files safely
Handle redirection with appropriate limits
Run in the background as a service
Pause and resume downloads when WiFi is not available
Enjoy! We can’t wait to see what kinds of things developers do with this! For more information about how to use expansion files with your app, read the APK Expansion Files developer guide.
[This post wasn’t actually written by anyone, but bashed out by a posse of engineering and product-management people. Heavy bashers included Dan Galpin, Ilya Firman, Andy Stadler, Michael Siliski, and Ellie Powers.]