It’s also a good idea to include the EGL library in order to set up a GL context. Use -lEGL in the Android.mk

file and include EGL/egl.h and EGL/eglext.h in your code.

Native Audio with OpenSL ES

The native audio API in Android is based on the OpenSL ES 1.0.1 from Khronos Group. This API provides a way

to perform native audio operations that is generally faster than the Java alternatives. If your application requires

very low-latency audio, use this API. To include the API, use the following flag in your Android.mk file:

LOCAL_LDLIBS += -lOpenSLES

Use the following include statements in your code to utilize both the standard OpenSL ES API and the

Android-specific extensions:

278 Part III: Pushing the Limits

#include <SLES/OpenSLES.h>

#include <SLES/OpenSLES_Platform.h>

#include <SLES/OpenSLES_Android.h>

#include <SLES/OpenSLES_AndroidConfiguration.h>

Porting a Native Library to Android

Often you will have the source code for an existing, usually open-source, library that you want to integrate into

your application. However, doing so can be difficult because these libraries are often built around the traditional

build system (usually a GNU makefile generated by the tools Autoconf and Automake) for Linux or other

UNIX operating systems. You could copy all the required source files and write a new Android.mk file from

scratch that configures the build and includes the right headers. However, this approach can prove very time-

consuming and error-prone, but luckily there’s a way around this issue.

The Android NDK provides the capability to generate a stand-alone toolchain that works in the same way as

the standard toolchain on your computer. By referring to this toolchain when building the library, you can

use the build system the source code is configured for and build a binary library that you can include in your

application. This process makes it easy to port an existing native library written in C/C++ and reduces the

complexity of your builds as well.

To build the stand-alone toolchain, run the following command:

$NDK/build/tools/make-standalone-toolchain.sh --platform=android-5

--install-dir=<install path>

This command creates a new stand-alone toolchain for the ARM ABI and Android API level 5. If you don’t

provide the install-dir parameter, a compressed archive with the toolchain is placed in your system’s

temp directory.

Building a Native Library

The library for the example in this section is the audio codec library called Opus. This is a free, open-source audio

codec that provides an excellent result for interactive speech as well as streaming music over the Internet. This

library uses technology from both the SILK codec from Skype and the CELT codec from Xiph.org. I don’t go into

the details of how Opus works; instead, I focus on the fact that you can encode and decode between a large

audio frame recorded in PCM format and a much smaller Opus packet.

You can download the source code and read more about the Opus codec at http://opus-

codec.org.

Even though this example uses the Opus audio codec, the same method applies to most other native libraries

that support Linux. Simply download your library and perform the same operations shown in this section.

Chapter 14: Native Code and JNI 279

After you download and extract the library, you need to configure your environment before building.

$ export PATH=$TOOLCHAINS/arm-linux-androideabi-4.6/bin/:${PATH}

$ export CC=arm-linux-androideabi-gcc

$ export CXX=arm-linux-androideabi-g++

These commands prefix your path so that the binaries in your toolchain have precedence over the default ones

for your operating system. It will also set two environment variables used by the build systems to determine

which compiler architecture to use.

Install all your stand-alone toolchains in a common directory and assign an environment variable. This

way, you can easily refer to a specific toolchain.

This command generates the file Makefile used for compiling and installing the library at the specified

position (note that <project path> requires an absolute path).

opus-1.0.2 $ ./configure --prefix=<project path>/jni/libopus --host=

arm-eabi

The preceding command needs to be executed in the directory where you extracted the Opus source code and

in the same terminal where you configured your environment. It sets up the build parameters for your sources.

The host parameter tells the configure script which CPU is to be targeted. The prefix parameter decides where

the compiled binaries and header files are installed. Use the directory of your Android application as a prefix if

that is where you wish the compiled library to be placed. Note that if the jni and libopus directories are

missing, they will be created.

Before you can continue and build the library, you probably need to manually edit the generated makefile. In a

normal UNIX environment, you can have several versions of the same library installed, which are identified by an

internal version number as well as their filename (for instance, libXYZ.1.2.3.so). The Android NDK doesn’t

support this version information, so before you build the library, you need to tell the build system to avoid

applying any version information. You do so by editing the file Makefile that was generated by the configure

script. Open the file in a text editor and find the line that appears as follows:

libopus_la_LDFLAGS = -no-undefined -version-info 3:0:3

Now remove the –version-info argument and replace it with the argument –avoid-version, which

tells the build system not to add this information in the compiled result. The result will appear as follows:

libopus_la_LDFLAGS = -no-undefined –avoid-version

Now you’re ready to compile and install your library in your project’s jni directory:

opus-1.0.2 $ make ; make install

280 Part III: Pushing the Limits

The preceding line is actually two commands, make and make install. The first command compiles the

code according to the configuration set up by the configure script. make install copies all the resulting

binaries and other files to the installation directory. You have now successfully installed the compiled Opus as

an ARM binary in the jni directory of your project. You’re ready to configure the NDK makefiles and implement

the JNI bridge to call the library.

Static Library Module

The library you just built consists of the actual library binary named libopus.so as well as a number of C

header files. The header files are required in order to call the functions inside the library itself. When building

your native code with the NDK, be sure to define a separate module for the native library. This is why the output

path was <project path>jni/libopus and not simply <project path>/jni. The Android.mk file

you place in the libopus directory looks like this:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := libopus

LOCAL_SRC_FILES := libopus.so

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include

include $(PREBUILT_SHARED_LIBRARY)

The makefile configuration described in the preceding examples defines the module as a shared library and

exports the include files found in the include directory. Because no sources need to be compiled for this

module (you already did that in the previous section), you specify only the libopus.so file as the source files

to be included.

JNI Module for Native Library

It’s time to create a new directory named opus_jni next to the libopus directory. This is where your JNI

wrapper module for the native library is implemented. Start with the Android.mk file that will import your

module.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := opus_jni

LOCAL_SRC_FILES := opus_jni.c

LOCAL_LDLIBS := -llog

LOCAL_SHARED_LIBRARIES := libopus

include $(BUILD_SHARED_LIBRARY)

$(call import-module,libopus)

This Android.mk file tells the NDK to include the static local library libopus in your build, just as was done

in the native-activity sample shown in “Android NDK Build Scripts,” earlier in this chapter. The next step is to

define the Java class that will provide the native methods.

The following class loads the library named opus_jni, and for each new instance, a new encoder and a new

decoder are initialized. You then have two public methods, encodePcm() and decodeOpus(), that do the

actual codec work. Finally, there is a destroy() method that frees up the memory used by the codec. You

now use the javah tool, as shown earlier, to generate the header file for these functions. After you have the

header file, you can start implementing the JNI code.

Chapter 14: Native Code and JNI 281

public class OpusCodec {

{

System.loadLibrary(“opus_jni”);

}

public OpusCodec(int sampleRate, int channels) {

initOpusEncoder(sampleRate, channels);

initOpusDecoder(sampleRate, channels);

}

private native void initOpusDecoder(int sampleRate, int channels);

public native int decodeOpus(byte[] encodedData,

int encodedLength,

short[] pcmOutput,

int pcmLength);

private native void initOpusEncoder(int sampleRate, int channels);

public native int encodePcm(short[] pcmData,

int pcmLength,

byte[] encodedData,

int encodedLength);

public native void destroy();

}

Here is the entire JNI code, piece by piece, starting with the header files and definitions:

#include <opus/opus.h>

#include <opus/opus_defines.h>

#include <opus/opus_types.h>

#include <opus/opus_multistream.h>

#include <android/log.h>

#include <string.h>

#include <stdlib.h>

#include <time.h>

#include “com_aptl_opus_OpusCodecTest_OpusCodec.h”

#define LOG_TAG “OpusTest”

#define LOGD(LOG_TAG, ...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,

__VA_ARGS__)

#define LOGV(LOG_TAG, ...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_

TAG,

__VA_ARGS__)

#define LOGE(LOG_TAG, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,

__VA_ARGS__)

OpusDecoder* gOpusDecoder;

OpusEncoder* gOpusEncoder;

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {

return JNI_VERSION_1_6;

}

282 Part III: Pushing the Limits

As shown here, the locally generated header files for the JNI functions and the headers for the Opus codec

that are located in the libopus module are included. The two global variables, OpusDecoder and

OpusEncoder, are the references to the decoder and encoder objects in the Opus library.

You also define the shorter log functions as well as the default JNI_OnLoad function to tell the system that it

supports the correct JNI version.

/*

* Class: com_aptl_opus_OpusCodecTest_OpusCodec

* Method: initOpusDecoder

* Signature: (II)I

*/

JNIEXPORT void JNICALL Java_com_aptl_opus_OpusCodecTest_OpusCodec_

initOpusDecoder

(JNIEnv *env, jobject obj, jint sampleRate, jint channels) {

int error;

gOpusDecoder = opus_decoder_create(sampleRate, channels, &error);

LOGD(LOG_TAG, “Decoder initialized: %d”, error);

}

/*

* Class: com_aptl_opus_OpusCodecTest_OpusCodec

* Method: initOpusEncoder

* Signature: (II)I

*/

JNIEXPORT void JNICALL Java_com_aptl_opus_OpusCodecTest_OpusCodec_

initOpusEncoder

(JNIEnv *env, jobject obj, jint sampleRate, jint channels) {

int error;

gOpusEncoder = opus_encoder_create(sampleRate, channels,

OPUS_APPLICATION_VOIP, &error);

}

The two init functions call the respective create function from the Opus library. In this example, you

provide the encoder with a parameter telling it to optimize its processing for VoIP applications.

/*

* Class: com_aptl_opus_OpusCodecTest_OpusCodec

* Method: decodeOpus

* Signature: ([BI[BIZ)I

*/

JNIEXPORT jint JNICALL Java_com_aptl_opus_OpusCodecTest_OpusCodec_

decodeOpus

(JNIEnv *env, jobject obj, jbyteArray in, jint inLength, jshortArray

out,

jint outLength) {

opus_int32 encodedBytes;

jbyte* jniIn;

jshort* jniOut;

if(in != NULL) {

Chapter 14: Native Code and JNI 283

jniIn = (*env)->GetByteArrayElements(env, in, NULL);

jniOut = (*env)->GetShortArrayElements(env, out, NULL);

encodedBytes = opus_decode(gOpusDecoder,

jniIn,

inLength,

jniOut,

outLength, 0);

(*env)->ReleaseByteArrayElements(env, in, jniIn, JNI_ABORT);

(*env)->ReleaseShortArrayElements(env, out, jniOut, 0);

} else {

jniOut = (*env)->GetShortArrayElements(env, out, NULL);

encodedBytes = opus_decode(gOpusDecoder,

NULL,

0,

jniOut,

(*env)->GetArrayLength(env, out), 1);

}

return encodedBytes;

}

The decode function allows the input to the opus_decode function to be null, which handles situations

where you’re missing an incoming packet of Opus data because of network latency and want the decoder to

handle the situation. The Opus library is designed to handle such cases and will generate a PCM package to fill

out missing audio.

/*

* Class: com_aptl_opus_OpusCodecTest_OpusCodec

* Method: encodePcm

* Signature: ([BI[BI)I

*/

JNIEXPORT jint JNICALL Java_com_aptl_opus_OpusCodecTest_OpusCodec_

encodePcm

(JNIEnv *env, jobject obj, jshortArray in, jint inLength, jbyteArray

out,

jint outLength) {

opus_int32 encodedBytes;

jshort* jniIn;

jbyte* jniOut;

jniIn = (*env)->GetShortArrayElements(env, in, NULL);

jniOut = (*env)->GetByteArrayElements(env, out, NULL);

encodedBytes = opus_encode(gOpusEncoder,

jniIn,

inLength,

jniOut,

outLength);

284 Part III: Pushing the Limits

if(encodedBytes == OPUS_BAD_ARG) {

LOGE(LOG_TAG, “OPUS_BAD_ARG”);

} else if(encodedBytes == OPUS_BUFFER_TOO_SMALL) {

LOGE(LOG_TAG, “OPUS_BUFFER_TOO_SMALL”);

} else if(encodedBytes == OPUS_INTERNAL_ERROR) {

LOGE(LOG_TAG, “OPUS_INTERNAL_ERROR”);

} else if(encodedBytes == OPUS_INVALID_PACKET) {

LOGE(LOG_TAG, “OPUS_INVALID_PACKET”);

} else if(encodedBytes == OPUS_UNIMPLEMENTED) {

LOGE(LOG_TAG, “OPUS_UNIMPLEMENTED”);

} else if(encodedBytes == OPUS_INVALID_STATE) {

LOGE(LOG_TAG, “OPUS_INVALID_STATE”);

} else if(encodedBytes == OPUS_ALLOC_FAIL) {

LOGE(LOG_TAG, “OPUS_ALLOC_FAIL”);

}

(*env)->ReleaseShortArrayElements(env, in, jniIn, JNI_ABORT);

(*env)->ReleaseByteArrayElements(env, out, jniOut, 0);

return encodedBytes;

}

The encode function takes PCM data represented as an array of short and encodes. The result ends up in the

byte array that the Java side can work with.

The destroy function is the last part in this file. It’s important to call this native method from your Java code as

follows once you’re done with the OpusCodec instance; otherwise, you’ll leak memory.

/*

* Class: com_aptl_opus_OpusCodecTest_OpusCodec

* Method: destroy

* Signature: ()V

*/

JNIEXPORT void JNICALL Java_com_aptl_opus_OpusCodecTest_OpusCodec_destroy

(JNIEnv *env, jobject obj) {

opus_decoder_destroy(gOpusDecoder);

opus_encoder_destroy(gOpusEncoder);

}

You now have a fully functioning JNI wrapper for the Opus codec optimized for VoIP applications. With just

some small modifications, you can craft this implementation to your own needs and build your own VoIP

application or adapt it for streaming music.

Summary

In this chapter, I introduced you to the Android NDK and how you can use it to write and build native code for

your Android applications. You can choose to write your entire application in native, which is many times the

case when it comes to 3D games where there’s an existing 3D engine available, or you can combine it with Java

Chapter 14: Native Code and JNI 285

code and write native C code where further optimization is needed. Also, thousands of open-source libraries

are on the Internet that you may find useful, and with the techniques shown in this chapter, you can easily port

them to Android and create a simple JNI wrapper to access them from Java.

This chapter certainly isn’t a comprehensive discussion of all things regarding the NDK and JNI. However, for

further details, please refer to the official documentation in the Android NDK as well as the JNI specifications

from Oracle, and check out “Further Resources,” below.

Further Resources

Android Developers Site

“JNI Tips” specific to Android development: http://developer.android.com/training/

articles/perf-jni.html

Oracle Websites

Oracle. “Java SE Documentation.” Here you can find information on JNI Documentation: http://docs.

oracle.com/javase/7/docs/technotes/guides/jni/index.html

Oracle. “Java Native Interface Documentation.” Here you can find specification for JNI: http://docs.

oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html

Chapter 15

The Hidden Android APIs

Android developers know that tons of features in the platform aren’t accessible through the official and public

APIs. For instance, although Android has classes for sending SMS (SmsManager and SmsMessage), there is no

official API for receiving them. Nevertheless, you can find several applications on the Google Play Store site that

provide full-featured SMS clients, and many apps are built around incoming SMS. Although a simple Google

search will give the necessary information for how to receive SMS in an Android application, you’ll come upon

many other cases where the hidden platform APIs in Android could be useful in applications.

In this chapter, I explain how and where to find hidden APIs. I also describe different methods for accessing

them and how to do so in a safe and secure way.

Most of the hidden Android APIs are also protected by permissions that have a protectionLevel of

signature or system (refer to Chapter 12). Although you can’t use these APIs in regular applications you

publish on the Google Play Store, you can still use them in applications you write for a custom firmware. Doing

so allows you to access these APIs without changing the Android platform. (See Chapter 16 for more on how to

build a custom firmware.)

Official and Hidden APIs

The official APIs are all the classes, interfaces, methods, and constants found in the SDK documentation.

Although these APIs are usually enough for most applications, at times you’ll want to access something more,

but don’t know how to find them in the official APIs.

When Google publishes the Android SDK, it contains a JAR file (android.jar) that you use as a reference

when compiling your code. You can find this file via <sdk root>/platforms/android-<API level>/.

The file contains only empty classes, meaning that it includes only the public and protected declarations and all

code within the methods has been scrubbed away. This JAR file is generated as part of the SDK when you build

the platform.

The scrubbed android.jar file is generated when the SDK is built by inspecting each source file and

excluding every field (such as constants), method, and class with the JavaDoc annotation @hide (see Figure 15-1).

This means that these symbols are still accessible to the implementation running on the device but they aren’t

visible at compile time.

288 Part III: Pushing the Limits

Figure 15-1 The source code for the hidden class CountryDetector . Note the @hide annotation for the JavaDoc .

Some APIs in Android are hidden automatically, without the @hide annotation. You can usually find these APIs

in the package com.android.internal, which is never part of the android.jar but does contain lots of

code for internal use in Android. You can find some of the other hidden APIs in the system applications. These APIs

usually have ContentProvider information for the system providers that isn’t included in the official SDK.

Discovering Hidden APIs

The easiest way to find the hidden APIs is to search for them in the Android source code. The source code for the

Android platform is vast, but fortunately several online sites have indexed the code and made it searchable. One

such site is AndroidXRef (http://androidxref.com), which allows you to search all the open source code

for all officially released Android versions (see Figure 15-2).

Chapter 15: The Hidden Android APIs 289

Figure 15-2 The Search dialog box at AndroidXRef

Another way to look for hidden APIs is through the View Source link on the Android API reference site (see

Figure 15-3). This site doesn’t provide a search method like AndroidXRef does, but it’s easier to access directly

from the official API documentation.

Although searching is good if you know where and what to look for, finding the code that does what you need

can be difficult. You can find most of the hidden APIs in the frameworks project. All of the APIs in the android

package can be found here as well as most of the APIs in com.android.internal.

Often the hidden API you’re looking for is part of a class that is public. For instance, the WifiManager has

several public, unhidden, methods but also a number of useful hidden methods and fields. In other cases, the

class is hidden from the public API, such as the CountryDetector class shown earlier in Figure 15-1.

290 Part III: Pushing the Limits

Figure 15-3 The View Source link for Settings .Global on the API reference site

Safely Calling Hidden APIs

Constant fields, such as broadcast actions or provider Uri, are major parts of the useful hidden APIs. You can

copy these fields into your own code and use them just as they are. The easiest way to do so is to copy the

entire class (for instance, directly from AndroidXRef) and place it in your project. If these hidden APIs have been

changed in the different Android API levels, you can keep a copy of each version in its own package. This way,

you can use a hidden API and still target multiple Android versions.

For most situations in which you need to use a hidden API that consists of constants (such as

broadcast actions), I recommend copying the hidden constants from the Android source code.

For APIs that require compile-time linking—that is, interfaces, classes, and methods—you have two choices. You

can compile your application with a modified SDK that contains a JAR file with all the classes and interfaces you

need. The other solution is to use the Reflection API in Java to dynamically look up the classes and methods you

want to call. Each method has its pros and cons, and your choice will depend on the situation.

Chapter 15: The Hidden Android APIs 291

Modifying the SDK will effectively bind your code to the device you use when generating the modified

android.jar (see the section “Extracting Hidden APIs from Device”) and could result in your code crashing

on other devices if you’re not careful. However, there’s no performance penalty with this solution. Using the

Reflections API allows you to target multiple Android versions but can penalize performance because it requires

a runtime lookup of classes and methods. I discuss both these approaches in the following sections.

Extracting Hidden APIs from a Device

To do compile-time linking to the hidden APIs, you first need to extract and process the libraries from a device.

You can extract these libraries either from an emulator instance or from a device because they’ll be used only

for compiling your code. Because this work requires a number of files from a device, I recommend that you

create an empty working directory. Since you may want to perform this task for multiple API versions, you can

create one working directory per API level.

$ adb pull /system/framework .

pull: building file list...

pull: <files pulled from device>

63 files pulled. 0 files skipped.

4084 KB/s (35028810 bytes in 8.374s)

Run the preceding command from your working directory, and you’ll see that it pulls all the files from the

directory /system/framework on the device. Once the extraction is completed, you can list the files, and the

output should look somewhat like the following (it may vary depending on the device’s manufacturer and the

version of Android you’re using):

$ ls

am.jar ext.jar

am.odex ext.odex

android.policy.jar framework-res.apk

android.policy.odex framework.jar

android.test.runner.jar framework.odex

android.test.runner.odex ime.jar

apache-xml.jar ime.odex

apache-xml.odex input.jar

bmgr.jar input.odex

bmgr.odex javax.obex.jar

bouncycastle.jar javax.obex.odex

bouncycastle.odex mms-common.jar

bu.jar mms-common.odex

bu.odex monkey.jar

com.android.future.usb.accessory.jar monkey.odex

com.android.future.usb.accessory.odex pm.jar

com.android.location.provider.jar pm.odex

com.android.location.provider.odex requestsync.jar

com.android.nfc_extras.jar requestsync.odex

com.android.nfc_extras.odex send_bug.jar

com.google.android.maps.jar send_bug.odex

com.google.android.maps.odex services.jar

com.google.android.media.effects.jar services.odex

com.google.android.media.effects.odex settings.jar

com.google.widevine.software.drm.jar settings.odex

292 Part III: Pushing the Limits

com.google.widevine.software.drm.odex svc.jar

content.jar svc.odex

content.odex telephony-common.jar

core-junit.jar telephony-common.odex

core-junit.odex uiautomator.jar

core.jar uiautomator.odex

core.odex

These files are all the Java-based system libraries on your Android device. They are the optimized DEX files that

are loaded by the Dalvik VM. The next step is to decide which file contains the hidden APIs you want to convert

back to Java class files that you can use when compiling your application. Most of the hidden APIs are placed in

framework.odex, whereas the crypto-libraries are in the bouncycastle.odex file.

Starting with Android 4.2, several hidden APIs that used to be in framework.odex are now placed

in other files. For instance, the hidden Telephony class is now optional (because not all Android

devices have telephony support) and can now be found in telephony-common.odex.

Once you know which file you need to convert, you download a tool named Smali that can convert the

optimized DEX files (.odex) to an intermediate format (.smali). You can then convert this intermediate

format back to Java class files using another tool named dex2Jar. You can download Smali at https://

code.google.com/p/smali, and you can find dex2Jar at https://code.google.com/p/dex2jar.

Download both and extract them to an appropriate location (for instance, next to your working directory). Start

by converting the ODEX file to the intermediate format as shown here:

$ mkdir android-apis-17

$ java -jar ~/Downloads/baksmali-2.0b5.jar -a 17 -x framework.odex -d . -o

android-apis-17

When you run this command from the same directory where you pulled the files from the device, the file

framework.odex is converted to a number of SMALI files placed in the correct package structure in the

directory android-apis-17. Next, you need to convert these files into a single DEX file.

$ java -jar ~/Downloads/smali-2.0b5.jar -a 17 -o android-apis-17.dex

android-apis-17

You can repeat the two preceding steps for each file you need to convert. For instance, on Android 4.2, the

hidden Telephony class is placed in telephony-common.odex. This way, you can create a single JAR file in

the end with all the hidden classes you need, even if they’re contained in different ODEX files from the start.

Finally, you need to use the dex2Jar tool to convert the DEX file into a JAR file containing all the Java class files.

$ ~/Downloads/dex2jar-0.0.9.15/d2j-dex2jar.sh android-apis-17.dex

dex2jar android-apis-17.dex -> android-apis-17-dex2jar.jar

The resulting JAR file contains all the classes, both hidden and public, from the original ODEX file (or files). To

use this file instead of the default android.jar in your SDK, simply rename it to android.jar and replace

Chapter 15: The Hidden Android APIs 293

it with the one in your SDK (for instance, <sdk root>/platforms/android-17/android.jar).

Remember to back up the original file in case you want to revert to the original SDK without the hidden APIs.

This approach provides you with a set of platform APIs that are guaranteed to work only on the device

you extracted the files from. Because this is the baseline for all other Android devices, I recommend

doing this only from a Nexus device with an official factory image. Perform this only on a non-Nexus

device if you need to use the hidden, proprietary APIs implemented by the device’s manufacturer (for

instance, a hidden camera extension API or something similar).

Error Handling for Modified SDK

When utilizing the approach for using the hidden APIs described previously, it’s difficult to know whether the

method signatures from your extracted classes match the ones that users have on their devices. Although

modifying the SDK may work on the device you use for development, a user who installs your application may

have a device where the hidden APIs are modified by the vendor. When that happens, your application will

throw a NoSuchMethodException or ClassNotFoundException.

You can deal with this situation a couple of ways. You can combine this approach with the use of reflections

(described in the next section) to detect the presence of your hidden APIs. In this way, you have the benefit of

both solutions, which I recommend. Another way is to simply catch the exception so that you can perform some

graceful degradation of the functionality.

Whatever you do, be sure to perform some error handling when calling hidden APIs. At the very least, you can

limit the availability of your application to the devices you’ve tested on. You always want to avoid having your

application crashing on a user’s device.

Calling Hidden APIs Using Reflections

The Reflections API in Java (found in the java.lang.reflect package) gives you a safer approach than

modifying the SDK does because you can use it to detect the presence of an API (or lack thereof) before calling

it. However, because all binding and invocation of hidden APIs occur in runtime, this method is also slower than

the alternative method described in the previous section.

Calling a hidden API using Reflections is a two-step process. First, you need to look up the class and methods

you want to call and store a reference to the Method object. After you have this reference, you can invoke

the method on an object. The two steps are shown in the following code, where you look up the method for

checking the state of Wi-Fi tethering:

public Method getWifiAPMethod(WifiManager wifiManager) {

try {

Class clazz = wifiManager.getClass();

return clazz.getMethod(“isWifiApEnabled”);

} catch (NoSuchMethodException e) {

throw new RuntimeException(e);

}

294 Part III: Pushing the Limits

}

public boolean invokeIsWifiAPEnabled(WifiManager wifiManager,

Method isWifiApEnabledMethod) {

try {

return (Boolean) isWifiApEnabledMethod.invoke(wifiManager);

} catch (IllegalAccessException e) {

throw new RuntimeException(e);

} catch (InvocationTargetException e) {

throw new RuntimeException(e);

}

}

The preceding example shows a fairly simple invocation using the Reflections API. If the hidden method

takes parameters, you need to provide the classes for those in the call to Class.getMethod(). Also, in this

example, the only error handling is to throw a RuntimeException. In your own application, you should

handle errors properly and do a graceful degradation of your application’s feature set.

Never assume that methods you retrieve using Reflections are available on all devices. If they’re hidden, the

manufacturer may have modified them and changed their signature (number of parameters, for instance).

In the early days of Android, this situation was quite common because many features were missing in the

platforms added by manufacturers. However, now you can usually expect the API to be there, just take care to

do proper error handing and feature fallback when using the hidden APIs.

Examples of Hidden APIs

In this section, I show a few examples of how hidden APIs are used. These are some of the typical scenarios I’ve

discovered that developers are asking for.

Receiving and Reading SMS

The most commonly requested hidden API in Android is related to receiving and reading SMS. Although the

public API contains two permissions, RECEIVE_SMS and READ_SMS, the actual API for performing these

actions is hidden.

An application that must be able to receive an SMS must declare the use of the RECEIVE_SMS permission and

implement a BroadcastReceiver that is triggered for incoming SMS.

public class MySmsReceiver extends BroadcastReceiver {

// Hidden constant from Telephony.java

public static final String SMS_RECEIVED_ACTION

= “android.provider.Telephony.SMS_RECEIVED”;

public static final String MESSAGE_SERVICE_NUMBER = “+461234567890”;

private static final String MESSAGE_SERVICE_PREFIX = “MYSERVICE”;

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (SMS_RECEIVED_ACTION.equals(action)) {

Chapter 15: The Hidden Android APIs 295

// “pdus” is the hidden key for the SMS data

Object[] messages =

(Object[]) intent.getSerializableExtra(“pdus”);

for (Object message : messages) {

byte[] messageData = (byte[]) message;

SmsMessage smsMessage =

SmsMessage.createFromPdu(messageData);

processSms(smsMessage);

}

}

}

private void processSms(SmsMessage smsMessage) {

String from = smsMessage.getOriginatingAddress();

if (MESSAGE_SERVICE_NUMBER.equals(from)) {

String messageBody = smsMessage.getMessageBody();

if (messageBody.startsWith(MESSAGE_SERVICE_PREFIX)) {

// TODO: Message verified - start processing...

}

}

}

}

The preceding code shows a BroadcastReceiver that listens for the Intent action android.provider.

Telephony.SMS_RECEIVED (remember to add this to the intent-filter in the manifest as well). The

only “hidden” parts in this example are this Intent action and the String to retrieve SMS data from the

Intent (“pdus”).

For reading already received SMS, you need to query a hidden ContentProvider and declare the use of the

permission READ_SMS. The hidden class Telephony, found in the android.provider package, provides

all of the information needed. The best way to use this class is to simply copy it to your own projects and refactor

it to suit your package structure. Because it also contains calls to other hidden classes and methods, you must

either remove or refactor these calls to make your code compile. Depending on how much of this hidden API

you’ll need to use, sometimes it’s enough to simply copy a number of constant declarations instead of the

entire class.

@Override

public void onActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

mAdapter = new SimpleCursorAdapter(this,

R.layout.sms_list_item, null,

new String[] {Telephony.Sms.ADDRESS, Telephony.Sms.BODY,

Telephony.Sms.DATE},

new int[] {R.id.sms_from, R.id.sms_body, R.id.sms_received},

CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

setListAdapter(mAdapter);

getLoaderManager().initLoader(0, null, this);

}

public Loader<Cursor> onCreateLoader(int id, Bundle args) {

Uri smsUri = Telephony.Sms.CONTENT_URI;

296 Part III: Pushing the Limits

return new CursorLoader(getActivity(), smsUri, new String[] {

Telephony.Sms._ID,

Telephony.Sms.ADDRESS,

Telephony.Sms.BODY,

Telephony.Sms.DATE},

null, null, Telephony.Sms.DEFAULT_SORT_ORDER);

}

Here are two methods shown from a custom ListFragment, which loads a Cursor from the Content

Provider for SMS and loads that into a SimpleCursorAdapter. The Uri for the provider and the name

of the columns are all the content used from the Telephony class.

Wi-Fi Tethering

Android smartphones can enable Wi-Fi tethering, which makes it possible to create a mobile Wi-Fi hotspot that

allows other devices (usually your laptop) to connect to the Internet when you’re traveling. This feature is a very

popular one on Android, but it has caused some problems for application developers.

When the user enables Wi-Fi tethering, the state of the Wi-Fi is neither on nor off, but “unknown” if you query it

through the public API. In a previous example (see the “Calling Hidden APIs Using Reflections” section), I showed

how to detect whether Wi-Fi tethering is enabled using hidden method isWifiApEnabled(). A number of

other hidden methods in the WifiManager class provide you with more information about Wi-Fi tethering.

private WifiConfiguration getWifiApConfig() {

WifiConfiguration wifiConfiguration = null;

try {

WifiManager wifiManager =

(WifiManager) getSystemService(WIFI_SERVICE);

Class clazz = WifiManager.class;

Method getWifiApConfigurationMethod =

clazz.getMethod(“getWifiApConfiguration”);

return (WifiConfiguration)

getWifiApConfigurationMethod.invoke(wifiManager);

} catch (NoSuchMethodException e) {

Log.e(TAG, “Cannot find method”, e);

} catch (IllegalAccessException e) {

Log.e(TAG, “Cannot call method”, e);

} catch (InvocationTargetException e) {

Log.e(TAG, “Cannot call method”, e);

}

return wifiConfiguration;

}

The preceding code shows how you can retrieve the WifiConfiguration for the Wi-Fi tethering settings

on a device. Note that calling these methods requires that your application have the permission android.

permission.ACCESS_WIFI_STATE. All Wi-Fi networks that an Android device has configured (that is,

connected to) can be enumerated as a list of WifiConfiguration objects through the WifiManager.

getConfiguredNetworks(). In each of the WifiConfiguration objects retrieved through this

method, the preSharedKey is set to null for obvious security reasons. However, when retrieving the

WifiConfiguration object for the Wi-Fi tethering settings as shown in the preceding code, you’ll find the

Chapter 15: The Hidden Android APIs 297

clear-text password present in the preSharedKey variable. In this way, your application can retrieve both the

name for the access point that is created when you activate Wi-Fi tethering and the password.

Although this feature can be considered a security flaw, it’s good to know that the permission needed to

activate Wi-Fi tethering requires your application to be signed with the system certificate. Thus, even if an

application can read the password, there’s no way for it to activate Wi-Fi tethering without the user’s consent.

Hidden Settings

An Android device has hundreds of different settings that are available through the class Settings. Besides

providing access to the value for each setting, it publishes a number of Intent actions that you can use

to launch a specific part of the settings UI. For instance, to launch the settings for airplane mode, you use

Settings.ACTION_AIRPLANE_MODE_SETTINGS when creating the Intent. Figure 15-4 shows how the

file Settings.java looks like when viewed through AndroidXRef.

Figure 15-4 Some of the hidden constants in the source file Settings.java

298 Part III: Pushing the Limits

A number of hidden setting keys and Intent actions are in the Settings class, some of which can be very

useful when your application needs to figure out details about the device or when you want to present a

shortcut within your application to a certain system setting.

Summary

This chapter introduced how you can discover and use the hidden APIs in the Android platform. Although only

a few examples are shown, the number of available hidden APIs is quite large. Most of these APIs are not only

hidden but also protected with permissions with the signature or system protection level, which makes

them unusable for most Android developers. However, as you will see in the next chapter, they can be an

efficient method for building advanced applications with access to system APIs if you build them for a device

with a custom firmware.

Some of the APIs are simple constants used to access ContentProviders, Intent actions for launching

Activities or settings keys for reading hidden system settings, whereas others are methods that you need

to invoke.

Although most applications will never require these APIs, in some situations, you’ll benefit by using an API that

is officially unavailable. Using the hidden APIs in a smart way will allow you to further enhance your apps.

Further Resources

Websites

An index of all the Android source code sorted according to specific versions: http://android

xref.com

The Java tutorials on the Reflection API: http://docs.oracle.com/javase/tutorial/reflect

The utility for converting ODEX files to DEX format: https://code.google.com/p/smali

The utility for converting DEX files to JAR files with Java classes: https://code.google.com/

p/dex2jar

Chapter 16

Hacking the Android

Platform

Although I expect most of you will be developing Android applications, sometimes using the NDK (refer to

Chapter 14) and sometimes using hidden APIs (refer to Chapter 15), some of you may have specific reasons

to modify the Android platform. Google provides the source code for the Android platform as an open-

source project called (you guessed it) Android Open Source Project (AOSP) just so third-party developers can

experiment and modify Android to suit their needs.

You can download all the code and compile it, but you also need a supported device if you want to test

your modifications on real hardware. Fortunately, all the devices announced under the Google Nexus brand

allow you to do so, and many manufacturers, such as Sony Mobile and HTC, allow you to unlock their devices

and flash them with a custom firmware as well. A company working on a low-level software feature that it

intends to license to handset manufacturers can use this approach to demonstrate its products on real devices,

for example.

Until Google released Android as an open-source project and provided open devices, this approach required

expensive development platforms from chipset vendors like Qualcomm and Texas Instruments. Today, hundreds

of independent developers spend a significant amount of time hacking on their own version of the Android

platform. Some of these developers have organized themselves into community groups focusing on building

custom firmware for Android devices that users can download and install.

In this chapter, I describe how you can build your own firmware or a custom ROM for an Android device,

including how to access and download the AOSP source code, set up the build environment, compile the

code, and flash the firmware to a device. In this chapter, I’m using the Galaxy Nexus, but you can use any of the

Google Nexus-branded devices with the same instructions. I also provide some general directions on how to

proceed with building custom firmware for other manufacturers’ devices.

In addition, I describe how to modify and extend the Android platform. Because the Android source code is

very large, I selected a few simple examples that will introduce you to the platform’s design. If your company is

developing custom solutions for the Android platform, you should find this information valuable.

I end this chapter talking about how you can contribute your changes to the Android platform to the AOSP.

If you think a feature is missing and not prioritized by Google, you can use this approach to make it happen.

( Note: This isn’t a guarantee Google will accept your patch, but you’ll never know unless you try.)

300 Part III: Pushing the Limits

A word of caution: The things described in this chapter can, if you’re not careful, break your device. If

you need to do this, use a device that is not your primary phone and know that your actions could

render the device unusable or break the warranty. When you flash your device, all its content will be

deleted, so be sure to back up content you want to save.

Several of the major Android device manufacturers allow you to flash a custom firmware on their devices, which

you do by unlocking the bootloader so that the device will accept firmware images with signatures other than

the device’s manufacturer. Doing so usually affects the warranty of the device, but if your goal is to demonstrate

a new low-level feature to that manufacturer, it might be worth doing. I briefly cover some of the major

manufacturers approach to unlocking the bootloader later in this chapter.

Unlocking Your Device

All Android devices come with a locked bootloader that you need to unlock before you can flash them with

new firmware (this also applies to flashing the factory firmwares from the link in the section “Flashing Factory

Images”). You can unlock all Google Nexus devices with a simple command. The following instructions apply to

Google Nexus devices.

Start by connecting your device to your computer and ensure that USB Debugging is enabled (see Chapter 1).

Next, reboot the device to the bootloader using adb as shown here:

$ adb reboot-bootloader

Your device will reboot into the bootloader, as shown in Figure 16-1, where you can issue fastboot

commands, which are used to unlock the device and flash new firmware images.

Figure 16-1 A Galaxy Nexus device in fastboot mode with the bootloader locked

Chapter 16: Hacking the Android Platform 301

Next, perform the following command, which unlocks the bootloader so you can flash your custom firmware

images:

$ fastboot oem unlock

It’s also possible to lock the bootloader again by issuing the command fastboot oem lock.

The screen of the device changes (see Figure 16-2) and shows information about the effects of unlocking

the bootloader. Press Volume Up/Down to select your choice and press the Power button to confirm. When

unlocked, the device returns to the fastboot mode and allows you to flash a new firmware image.

Figure 16-2 The warning box presented when you try to unlock the bootloader on a

Galaxy Nexus device

Flashing Factory Images

When you build your custom firmware and test your new features, the device is likely to crash. Although

generally you can return to fastboot mode and flash a new firmware image, sometimes it may be useful to

flash the factory image to the device. You may also want to do so when testing an older version of Android.

Download the factory image for your device from https://developers.google.com/android/

nexus/images and extract the archive into an empty directory.

In the directory, you’ll find a shell script named flash-all.sh, which will flash the entire factory firmware

to the device and restore it to its original state. After the process is complete (it could take a few minutes,

depending on the device and your computer), the device reboots into the freshly installed Android image.

302 Part III: Pushing the Limits

Unlocking Non-Google Nexus Devices

Most Android devices on the market aren’t Google Nexus brands, and not all of these device’s manufacturers

allow you to unlock the bootloader and flash a custom firmware. However, more and more manufacturers are

realizing the positive benefits of allowing advanced users and third-party companies to unlock and flash their

devices with custom firmware, so the list of officially unlockable devices is growing all the time.

Sony Mobile was the first manufacturer to announce that its devices could be unlocked, beginning with its

2011 device. Not every device after 2011 from Sony Mobile can be unlocked, and sometimes the devices sold

through operators can’t be unlocked. You can find more information about unlocking the Sony Mobile Android

devices at http://unlockbootloader.sonymobile.com.

HTC also provides an official method for unlocking its devices. Some devices require a firmware update before

they can be unlocked. More information is available at http://www.htcdev.com/bootloader.

Motorola has an official bootloader unlock program covering some of its devices. To find out if this program

covers your device, visit https://motorola-global-portal.custhelp.com/app/standalone/

bootloader/unlock-your-device-a.

Other manufacturers may also allow you to unlock your device. Visit their support sites for more information.

Also, even if there is no official way of unlocking your device, there may be an unofficial workaround. A good

place to start is on the XDA Developers forum (http://www.xda-developers.com). Remember that

unlocking your device this way will usually void the warranty. Also, even if you manage to unlock the device,

most manufacturers don’t provide a full set of hardware drivers necessary to build a working custom firmware.

Refer to the each manufacturer’s information on the details.

Community-Supported Firmwares

Ever since the first Android devices were released, a stable and growing community of developers has worked

on releasing custom firmware that users can download and flash onto their own devices. The most well-known

group is CyanogenMod, and it has custom firmware for a wide selection of devices. You can find more

information about its work at http://www.cyanogenmod.org.

Other groups have focused on devices from specific vendors. For instance, a group called the FreeXperia Project

is building high-quality community firmware for the Sony Mobile Xperia devices. Visit its site at http://free

xperiaproject.com for more information.

You can usually find the best source for community-developed custom firmware at the XDA Developer forums

(see http://forum.xda-developers.com). At this site, you can find information about most devices

that have been released.

The Android Source Code

The Android Open Source Project is the publically available source code for the Android platform, and Google

provides it at http://source.android.com. This site includes not only the source code for the Android

platform, but also for the SDK and other development tools. The project is structured around a number of Git

Chapter 16: Hacking the Android Platform 303

modules. Each module is part of the overall system, such as the Linux kernel, native libraries, the Dalvik VM, or a

system application.

Setting Up the Build Environment

You can work with the Android Open Source Project on Linux and Mac OS X. The build instructions tend to

be updated on a regular basis, with support for new versions of the host operating systems and updated

development tools, so I’m not covering the instructions on how to set up the environment on your system. You

can find the latest instructions for setting up the build environment for AOSP at http://source.android.

com/source/initializing.html.

Because the default partition on OS X is case-insensitive, for Mac OS X, you need to create a new case-sensitive

disk image. Also, downloading the source code (using repo sync) the first time will take quite a while—a lot

of code must be downloaded.

Nexus Binaries

Although the Android platform is open-source, all the hardware drivers for the Nexus devices are not. For

instance, the graphics driver for Galaxy Nexus is a proprietary library from Imagination Technologies, and the

source code for this hasn’t been released yet. Therefore, you will also need to download all the binary drivers for

your Nexus device before you proceed with building a custom firmware. You can find the available binaries at

https://developers.google.com/android/nexus/drivers.

You download all the binary drivers available for your device to the root of the Android platform projects you’ve

just downloaded. Run the following command to extract the archive, and you’ll be prompted with a license

agreement:

$ tar xzvf imgtec-maguro-jdq39-bb3c4e4e.tgz

x extract-imgtec-maguro.sh

$ sh ./extract-imgtec-maguro.sh

The license for this software will now be displayed.

You must agree to this license before using this software.

-n Press Enter to view the license

After you agree to the terms of the license, you have new projects under the vendor directory. These are

usually only headers, makefiles, and binary .so files that will be installed on the custom firmware when you

finish building.

It’s important to include all the binary drivers available, or the custom firmware you’ve built probably won’t

boot correctly.

Building and Flashing

After all the source code is downloaded and the development environment is set up, you can start building the

custom firmware. Make sure ccache is installed and configured (see http://source.android.com/

source/initializing.html#setting-up-ccache) because it will speed up subsequent builds.

304 Part III: Pushing the Limits

Building the platform the first time will take a bit of time (about 11⁄2 hours on my MacBook Pro with 16GB RAM).

First, you need to load the build environment into your current shell and tell the build system which device and

configuration you want to build.

$ source build/envsetup.sh

$ lunch full_maguro-userdebug

The first command loads the build system and sets all the right environment variables. The lunch command lets

you select the build target (when it’s called without arguments, you can manually select the target). In this case,

you want to build the userdebug configuration for the Galaxy Nexus (“maguro”). The userdebug configuration

allows you to access adb with root access and provides more privileges that enable you to debug the device and

write to the system partition, which is useful if you want to install a new system application later on.

Because building can take a significant amount of time, I suggest building on a high-end computer with a fast

disk (preferably a SSD)—although it’s possible to compile the platform on a low-end computer. To speed things

up, instruct the build system to use additional threads while building by giving the argument –jN to the make

command, where N is twice the number of hardware threads your computer supports. Generally, computers

can support two hardware threads per CPU core (using hyper-threading), so a computer with one CPU with four

cores will support eight threads.

$ make –j8

This is a good time to go and make some more coffee.

When the build is complete and, hopefully, successful, you can reboot your connected device in fastboot mode

and flash the firmware.

$ adb reboot-bootloader

$ fastboot flashall –w

When the process is complete, your device should boot up with your new custom firmware. Try running the

following commands from a terminal to confirm that you have root access:

$ adb root

restarting adbd as root

$ adb remount

remount succeeded

$ adb shell

root@android:/ #

The first command restarts the adb daemon on the device to run as root, giving it additional privileges. The

second command remounts the system partition on the device in read/write mode, allowing you to install

new system applications under /system/app. The final command simply confirms that you have root access

by opening a shell on the device. If the command line starts with root@android, you know that you have

root access.

Congratulations, you now have your very own custom firmware on which you have full root control and the

possibility to install new system and platform-signed applications.

Chapter 16: Hacking the Android Platform 305

Writing System Applications

As I describe in Chapter 3 and 12, Android’s permission system has various protectionLevels that describe

who can use a specific permission. If the permission has its protectionLevel set to system, the application

that uses this permission must initially be installed on the system partition (/system/app, to be specific). In

this way, third-party developers can build applications that the manufacturer installs on the system partition

and which then have access to system-level permissions. Note that updates to this application will retain the

same permissions, even though the updates are placed on the data partition. This is how Google’s Android

applications (such as Gmail, Google+, and Google Map) can gain additional permissions even though the device

manufacturer doesn’t sign them.

As a third-party developer, as long as you can get your application in the system partition for a device, you can

provide upgrades to users through the Google Play Store.

Along with the challenge of getting your system application on a manufacturer’s device, you should

also define a new system permission that the manufacturer then adds to the system permissions. By

using this permission in your system application, you can effectively filter the application on Google

Play to make it invisible to users who don’t have these modifications.

To install an APK as a system application, you need to have write access to the system partition (see the end

of the “Building and Flashing” section). Then you can push the new APK to the system application folder as

shown here:

$ adb push <apk file> /system/app

After the application is placed on the system partition, the package manager sets it up properly. Your

application can now use permissions requiring protectionLevel system, even though it isn’t signed with

the manufacturer’s platform certificate.

The signature permissionLevel is usually set for even more-sensitive permissions. For instance,

the permission android.permission.FORCE_STOP_PACKAGES has its protectionLevel set

to signature, meaning that only applications signed with the vendor platform certificate can use APIs

that require this. Third-party developers who want to sell their solution to a manufacturer, which requires

signature permissions can do so by providing a project for the platform build with an unsigned APK, similar

to how hardware drivers are distributed for the Nexus devices.

Platform Certificates

You can find the default test keys used to sign your custom firmware under <aosp root>/build/target/

product/security. Because these keys are available publically, never consider them safe. Refer to the

README file in this directory for additional information and instructions on how to generate your own platform

keys. However, you can use them for developmental purposes when you want to test new system applications.

The format of these keys is different from the one used to sign Android applications using the Android SDK or

Android Studio, so before you can use them in your development environment, you need to convert them.

306 Part III: Pushing the Limits

$ openssl pkcs8 -inform DER -nocrypt -in platform.pk8 -out platform.pem

$ openssl pkcs12 -export -in platform.x509.pem -inkey platform.pem -out

platform.p12 -name android-platform -password pass: <password>

$ keytool -importkeystore -destkeystore platform.keystore -srckeystore

platform.p12 -srcstoretype PKCS12

You now have a keystore with the format recognized by the Android development tools. Simply use this

keystore when you want to sign your application with the platform certificate. Doing so allows your application

to use all permissions requiring signature permissionLevel. Also, you can now set the sharedUserId

attribute in the manifest to android.uid.system, which gives the application the same user ID as the

system server and grants access to additional system-level APIs.

To use this certificate in your application, copy the keystore to your application and add the following to your

Gradle build:

android {

signingConfigs {

debug {

storeFile file(“platform.keystore”)

storePassword “password”

keyAlias “android-platform”

keyPassword “password”

}

}

// ... omitted for brevity ..

}

Now subsequent debug builds from Android Studio must use the extracted platform key when signing your

application.

Remember: Never use this certificate for applications published on the Google Play Store. Only use it

for testing and demonstration.

Writing Signature-Signed Applications

In Chapter 15, I discuss how to use the hidden platform APIs in the WifiManager to detect whether tethering

is enabled. Using some of the additional hidden APIs together with the platform key, you can build your own

application for controlling Wi-Fi tethering, as shown in the following example:

<?xml version=”1.0” encoding=”utf-8”?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package=”com.aptl.systemlevelapps”

android:sharedUserId=”android.uid.system”

android:versionCode=”1”

android:versionName=”1.0” >

Chapter 16: Hacking the Android Platform 307

<uses-permission android:name=”android.permission.CHANGE_WIFI_STATE”

/>

<uses-sdk

android:minSdkVersion=”17”

android:targetSdkVersion=”17” />

<application

android:allowBackup=”true”

android:icon=”@drawable/ic_launcher”

android:label=”@string/app_name”

android:theme=”@style/AppTheme” >

<activity

android:name=”.EnableTetheringActivity”

android:label=”@string/app_name” >

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

</application>

</manifest>

First, you add sharedUserId with the value android.uid.system to the manifest element to give your

application the correct user ID for controlling the Wi-Fi. Even though your application is now signed with the

platform certificate, you still need to declare the permissions your application requires, which is why you need

to add the permission CHANGE_WIFI_STATE to the manifest.

public class EnableTetheringActivity extends Activity {

private static final String TAG = “EnableTetheringActivity”;

private WifiManager mWifiManager;

private Method mSetWifiApEnabledMethod;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);

try {

Class clazz = WifiManager.class;

mSetWifiApEnabledMethod = clazz.getMethod(“setWifiApEnabled”,

WifiConfiguration.class, boolean.class);

} catch (NoSuchMethodException e) {

Log.e(TAG, “Error retrieving method setWifiApEnabled()”, e);

}

setContentView(R.layout.activity_main);

}

public void doEnableWifiTethering(View view) {

try {

if(mWifiManager.isWifiEnabled()) {

mWifiManager.setWifiEnabled(false);

}

308 Part III: Pushing the Limits

EditText ssidNameView = (EditText) findViewById(R.id.ssid_name);

String ssidName = ssidNameView.getText().toString();

EditText wifiPasswordView =

(EditText) findViewById(R.id.wifi_password);

String wifiPassword = wifiPasswordView.getText().toString();

WifiConfiguration wifiConfiguration = new WifiConfiguration();

wifiConfiguration.SSID = ssidName;

wifiConfiguration.preSharedKey = wifiPassword;

mSetWifiApEnabledMethod.invoke(mWifiManager,

wifiConfiguration, true);

} catch (IllegalAccessException e) {

Log.e(TAG, “Illegal access.”, e);

} catch (InvocationTargetException e) {

Log.e(TAG, “Invocation error.”, e);

}

}

}

The Activity shown here retrieves the hidden method setWifiApEnabled() from the WifiManager

class. When the click-callback doEnableWifiTethering() is called, the SSID and password are retrieved

and used to construct a default WifiConfiguration, which is then used as a parameter when invoking the

Method object.

You now have a way of controlling the state of Wi-Fi tethering through a standard Android application, which

just happens to be signed with the platform certificate.

Hacking the Android Platform

In this section, I describe how to work with the AOSP source code. The code examples are in Java, but the same

approach applies to native development as well.

Setting Up Your IDE

Although Android Studio works fine for developing regular Android applications, I recommend using

IntelliJ IDEA CE (Community Edition) for working with the AOSP source code. You can find this IDE at www.

jetbrains.com/idea. It’s also possible to work with AOSP in Eclipse, but I recommend IntelliJ IDEA CE

because of its superior performance and code navigation support.

Before opening the IDE and importing the source code, make a complete build as described in the section

“Building and Flashing” earlier in this chapter. Doing so generates the Java source files for platform resources,

which makes it easier to work with from the IDE.

After you have a complete build, you can generate the IntelliJ project files (and Eclipse .classpath file) by

executing the following command in the project root directory:

$ development/tools/idegen/idegen.sh

Read excludes: 3ms

Traversed tree: 50027ms

Chapter 16: Hacking the Android Platform 309

Now, you have two IntelliJ IDEA files in the root named android.ipr and android.iml.

Start IntelliJ IDEA CE and open the project file (android.ipr)—the first time you do this, IntelliJ will build an

index of all the source code, so it will take some time.

You can now start hacking the AOSP source code.

Android Projects

The Android platform consists of a number of projects, each in its own Git repository. Because there are more

than 300 of them, there’s not enough room in this book to cover and explain every project in the AOSP source

code. Instead, I’ll describe some of the more important ones, based on their path in the file system. Some

consist of several Git repositories (for instance, the packages project).

You can find a complete list of the projects in the file <aosp root>/.repo/project.list.

Frameworks/base

The central (and according to some, most important) project in the Android platform is frameworks/base.

This project contains most of the sources for the system server as well as many other components running

on an Android device. Here, you’ll also find the implementation of most of the Android APIs (except those

belonging to the java and javax packages). If you want to add a new system service or modify the Android

APIs, you make your changes here.

Packages

You’ll find all of the standard system applications in the packages project, divided into apps,

inputmethods, providers, screensavers, and wallpapers folders. The apps folder contains the

applications the user can see in the launcher, such as Email, Phone, and Calculator. The Providers folder

contains the system ContentProviders, such as the media or contacts provider. The Inputmethods

folder contains the default soft keyboards found in Android.

If you want to modify one of the existing system applications, this is where you start. Developers often start

modifying the default launcher application in the AOSP sources. You can find this app under packages/

apps/Launcher2.

Vendor

The vendor directory is not a single project but the place where vendor-specific platform projects

are contained. This is usually where the binary hardware drivers are placed. If you intend to provide a

manufacturer with your own project for the Android platform, such as a shared library with some proprietary

implementations, place it in this directory as well. Also, it’s best to place vendor-specific applications that

should be preinstalled in this directory. In fact, most vendor-specific code should end up here; this way, device

manufacturers can keep the Android Open Source Project code clean from source code that isn’t open-source.

310 Part III: Pushing the Limits

Here is the Android.mk for the Wi-Fi and Bluetooth drivers from Broadcom for the Galaxy Nexus:

LOCAL_PATH := $(call my-dir)

ifeq ($(TARGET_DEVICE),maguro)

include $(CLEAR_VARS)

LOCAL_MODULE := bcm4330

LOCAL_MODULE_OWNER := broadcom

LOCAL_SRC_FILES := bcm4330.hcd

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE_SUFFIX := .hcd

LOCAL_MODULE_CLASS := ETC

LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/firmware

include $(BUILD_PREBUILT)

endif

You can find this makefile in <aosp root>/vendor/broadcom/maguro/prebuilt. The content of this

file is very similar to what a normal Android.mk looks like when you’re doing native development using the

Android NDK. The source is the binary file bcm4330.hcd, and its output is in the firmware directory on

the device.

When building your own vendor extension, refer to the existing build files for other projects and construct your

own. For Android applications (APK) that you want to include in the system partition, refer to the build files

found in the packages directory for examples of how your Android.mk should look.

Android Linux Kernel

When you’re building Android for a Nexus device, note that the Linux kernel is usually prebuilt and can be found

under <aosp root>/device (for instance, <aosp root>/device/samsung/tuna for the Galaxy

Nexus). If you need to modify the kernel for your device, you must download the Linux source code for that

device, modify the build configuration, build the image, and copy it to the right location.

The Linux kernel has a different license (GPLv2) than the Android platform (usually Apache License v2). The

interesting thing with the Linux kernel is that this license guarantees that you can get access to the source code

for every single Android device. While modifying the Linux kernel for your own custom firmware is beyond

the scope of this book, you can refer to the instructions from the chipset vendor for each device to learn more

about kernel development on its hardware platform.

Adding a System Service

When modifying the Android platform, a common task is to add a new system service. These are the services

that Android developers get when calling Context.getSystemService(). In the following code, I

show you how to add a new system service by modifying the code in frameworks/base. I call this service

HomeDetector, and its task is to send a broadcast when the device enters the location defined as home. This

class belongs to the location APIs; thus it is placed in the android.location package.

Chapter 16: Hacking the Android Platform 311

public class HomeDetector {

private static final String TAG = “HomeDetector”;

/**

* Sticky broadcast indicating that the device arrives

* or leave the defined home wifi.

*

*/

public static final String ACTION_HOME_LOCATION_CHANGED =

“android.location.HOME_LOCATION_CHANGED”;

/**

* Extra <code>boolean</code> indicating if the device arrived

* to the home location or not.

*/

public static final String EXTRA_AT_HOME = “atHome”;

private IHomeDetector mHomeDetectorService;

public HomeDetector(IHomeDetector homeDetector) {

mHomeDetectorService = homeDetector;

}

/**

* Set the home wifi.

*

* @param homeWifi

* @hide Should only be available to system applications

*/

public void setHomeWifi(WifiInfo wifiInfo) {

try {

mHomeDetectorService.setHomeWifi(wifiInfo);

} catch(RemoteException e) {

Log.e(TAG, “setHomeLocation.”, e);

}

}

}

The class HomeDetector will be the class that is returned when an application calls Context.

getSystemService() with the parameter Contex.HOME_DETECTOR (which you also need to add to the

Context class). This class takes an instance of HomeDetectorService as a constructor parameter, which

will contain the actual implementation for the service.

When adding a new system service in Android, the standard approach is to define an AIDL file that will be

used internally by the system server. You create this AIDL in the same package as the HomeDetector class

(android.location).

package android.location;

import android.location.Location;

312 Part III: Pushing the Limits

oneway interface IHomeDetector {

void setHomeWifi(in WifiInfo homeWifi);

}

The AIDL is compiled as an ordinary application-specific AIDL and allows you to implement the other parts of

your new system service.

The following code goes into SystemServer.run() in the package com.android.server. This is the

central class for all system services.

try {

Slog.i(TAG, “Home Detector”);

homeDetector = new HomeDetectorService(context);

ServiceManager.addService(Context.HOME_DETECTOR, homeDetector);

} catch (Throwable e) {

reportWtf(“starting Home Detector”, e);

}

The preceding code simply creates an instance of HomeDetectorService and starts the service.

Here is the actual implementation of the new service:

public class HomeDetectorService extends IHomeDetector.Stub {

private static final String TAG = “HomeDetectorService”;

public static final String HOME_WIFI_SSID = “homeDetector.ssid”;

public static final String HOME_WIFI_BSSID = “homeDetector.bssid”;

public static final String SET_HOME_WIFI_PERMISSION

= “android.permission.SET_HOME_WIFI”;

private Context mContext;

private boolean mCurrentState = false;

public HomeDetectorService(Context context, WifiManager wifiManager) {

mContext = context;

WifiInfo wifiInfo = wifiManager.getConnectionInfo();

if(wifiInfo != null) {

String ssid = SystemProperties.get(HOME_WIFI_SSID);

String bssid = SystemProperties.get(HOME_WIFI_BSSID);

mCurrentState = wifiInfo.getSSID().equals(ssid)

&& wifiInfo.getBSSID().

equals(bssid);

}

sendHomeDetectionBroadcast(mCurrentState);

IntentFilter intentFilter =

new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_

ACTION);

mContext.registerReceiver(new WifiListener(), intentFilter);

}

Chapter 16: Hacking the Android Platform 313

public void setHomeWifi(WifiInfo homeWifi) throws RemoteException {

mContext.enforceCallingPermission(SET_HOME_WIFI_PERMISSION,

“Missing permission “ + SET_HOME_WIFI_PERMISSION);

SystemProperties.set(HOME_WIFI_SSID, homeWifi.getSSID());

SystemProperties.set(HOME_WIFI_BSSID, homeWifi.getBSSID());

}

public void sendHomeDetectionBroadcast(boolean state) {

Intent homeDetectorBroadcast =

new Intent(HomeDetector.ACTION_HOME_LOCATION_

CHANGED);

homeDetectorBroadcast.putExtra(HomeDetector.EXTRA_AT_HOME, state);

mContext.sendStickyBroadcast(homeDetectorBroadcast);

}

class WifiListener extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

NetworkInfo networkInfo = intent.

getParcelableExtra(WifiManager.EXTRA_NETWORK_

INFO);

boolean newState = false;

if(networkInfo.getState().equals(NetworkInfo.State.CONNECTED))

{

String ssid = SystemProperties.get(HOME_WIFI_SSID);

String bssid = SystemProperties.get(HOME_WIFI_BSSID);

WifiInfo wifiInfo =

intent.getParcelableExtra(WifiManager.EXTRA_WIFI_

INFO);

newState = wifiInfo.getSSID().equals(ssid) &&

wifiInfo.getBSSID().

equals(bssid);

}

// Only send new broadcast if changed

if(newState != mCurrentState) {

mCurrentState = newState;

sendHomeDetectionBroadcast(mCurrentState);

}

}

}

}

The usual place to put a new system service is under com.android.server.<category>, where

<category> is the type of service you created (in this case, location). Note the call to Context.

enforceCallingPermission(), which verifies that the calling process has the right to call this method.

In this case, I created a passive service, but if your service needs to run its own thread, you need to implement

the Runnable interface and initialize a new Looper in the run() method, as shown here:

314 Part III: Pushing the Limits

@Override

public void run() {

// Set the correct thread priority

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

Looper.prepare(); // Prepare the Looper

// Create a custom Handler for processing

mServiceHandler = new MySystemServiceHandler();

init(); // Perform initialization

Looper.loop(); // Start this thread as a Looper

}

With the run() method just shown, your background service can post Messages to the custom Handler

that has its own dedicated thread. In this way, you can perform blocking operations in your service that

otherwise could block the entire system.

The final step is to declare the new permission required for the setHomeWifi() method, which you do by

editing the AndroidManifest.xml of the system server, located in <aosp root>/frameworks/base/

core/res/. Locate the permissions belonging to the location group (android.permission-group.

LOCATION) and add the new permission there.

<!-- Allow an application to set the WiFi which should

be considered “home” -->

<permission android:name=”android.permission.SET_HOME_WIFI”

android:permissionGroup=”android.permission-group.LOCATION”

android:protectionLevel=”signature|system”

android:label=”@string/permlab_setHomeWifi”

android:description=”@string/permdesc_setHomeWifi” />

In this case, only system or signature applications are allowed to use this permission. If you want to create

a new public API that anyone can access, consider whether the protectionLevel should be normal or

dangerous.

Speeding Up the Platform Development Cycle

After you add your new system service, you’re ready to build the code and push the changes onto your device.

Because building the entire platform from scratch every time would take a significant amount of time, you can

use some tricks to reduce the turnaround time for this kind of development.

For example, instead of building the entire platform, build only the project you’ve changed. In the earlier

example in “Adding a System Service,” you modified the project frameworks/base, so give this project as a

parameter to the make command so that only that part is rebuilt.

$ make –j8 frameworks/base

This command compiles the code and builds the binaries for that specific project. Then you can push only the

changed binaries onto the Android device with the following commands:

Chapter 16: Hacking the Android Platform 315

$ adb shell stop

$ adb sync

$ adb shell start

The first command stops the Android system server, allowing you to safely push the new binaries onto your

devices without crashing them. The second command pushes the actual binaries. This command can also take

a second parameter stating which directory you want to synchronize. Finally, you call the third command to

restart the system server. Your newly modified system server will now start up together with the new service

you just added. This procedure lets you iterate through the development cycle quickly and try out small

changes to the system service and libraries, without the time-consuming task of having to shut down the

phone and flash a new firmware.

The preceding procedure doesn’t produce a new firmware image for each build. To do this, you have

to perform a new, full build. However, because you (hopefully) have enabled ccache and haven’t

deleted your build output directory, the second time around shouldn’t take too long.

Contributing to AOSP

Google is doing most of the new development on the Android platform, but a significant amount of code is

contributed by other companies and individuals through the Android Open Source Project. Manufacturers of

Android devices and chipset vendors are the largest contributors, but smaller companies also fix many bugs and

develop small features. Although Google has the final say in what ends up in the Android platform, it is always

interested in high-quality features that others are willing to contribute.

The first thing to do is visit the Android contribution discussion forum (https://groups.google.com/

forum/?fromgroups#!forum/android-contrib) and describe your new proposal there. If you want to fix a bug, visit the issue tracker for the Android platform (https://code.google.com/p/android/

issues/list) and report it there (don’t forget to check if it has already been reported!). When you feel comfortable that your proposed fix is okay, you can start your contribution process.

Contributions to one of the Android projects are described in detail at http://source.android.com/

source/contributing.html. Remember that your fix needs to maintain high-quality standard and conform to the Android Code Style Guidelines (see http://source.android.com/source/code-

style.html). Don’t expect that your contribution will get accepted right away or that the processing will go fast. Google deals with a huge number of contributions, and most never make it into the platform for various

reasons. Be patient and make sure that your code is well-tested and documented.

Figure 16-3 shows the workflow of a patch. As you can see, you must take a number of steps before your

contribution is accepted, but these steps help to guarantee that your code and feature is of high quality.

316 Part III: Pushing the Limits

Image reproduced from work created and shared by the Android Open Source Project (http://source.android.com/source/life-of-a-patch.html)

and used according to terms described in the Creative Commons 2.5 Attribution License.

Figure 16-3 The Android Open Source Project Contribution Workflow

Chapter 16: Hacking the Android Platform 317

Summary

In this chapter, I explained the basics of developing the Android platform. I showed how you can unlock your

Google Nexus device and flash it with a custom firmware and how you can download the entire Android

platform source code and relevant binary drivers to build your own version. I explained some of the reasons for

doing so from a business perspective. By working closely with a device manufacturer, your business can get its

proprietary third-party solutions that require system-level access onto a device and still be able to upgrade the

application through the Google Play Store.

I showed how you can build a system application and push it onto the system partition to test it on your custom

firmware. Next, I explained the steps for signing an application with the platform certificate in order to gain

access to permissions requiring signature permissionLevel.

I completed this chapter with an example for modifying the Android platform by adding a new system service.

Although the example is very simple and could just as well be created as a normal application, it demonstrates

how and where you should place your new code in the Android platform.

I believe that all Android developers would benefit from a full understanding of how the Android platform is

built and works, as well as how the platform certificates protect the sensitive platform APIs.

If you come up with a new feature for the Android platform, consider contributing it back to the Android Open

Source Project. There’s no guarantee it will be accepted by Google and merged into the Android source code,

but it’s a good way to practice participating in a large open-source project. Doing so also is great experience in

developing high-quality code—because the requirements from Google are very high.

Further Resources

Websites

Developing for the Android Open Source Project: http://source.android.com

The Gerrit site for reviewing patches to AOSP: https://android-review.googlesource.com

The main discussion group for Android platform and technologies: https://groups.google.com/

forum/?fromgroups#!forum/android-platform

The main discussion group for help with building the Android source code: https://groups.google.

com/forum/?fromgroups#!forum/android-building

The discussion group for those who want to port Android to a new device: https://groups.google.

com/forum/?fromgroups#!forum/android-porting

The main discussion group for those who want to contribute to the Android platform through AOSP:

https://groups.google.com/forum/?fromgroups#!forum/android-contrib

The public issue tracker for bugs in the Android platform: https://code.google.com/p/android/

issues/list

XDA Developers forums: http://forum.xda-developers.com

Chapter 17

Networking, Web Service,

and Remote APIs

A majority of all Android applications perform some kind of network communication over the Internet. Even

single-player games today provide social integration with an online web service. So, the data an application

generates should be stored online so that users will have access to the same data from other devices.

As a developer of Android applications, you need to consider all aspects of networking. If your application

wastes networking resources, it will consume more power and possibly increase the cost to users because of

mobile data charges.

In this chapter, I cover how to perform network calls as efficiently as possible. I introduce my favorite APIs for

HTTP-based communication. Then I move on to describe three different types of web services and how you can

integrate them into your application. If you’ll be using a web service in your application, you can likely integrate

it by using a technique very similar to one of the three I describe.

I complete this chapter with guidelines for reducing the power your application consumes in regard to network

operations. All network calls come with an additional drain on the battery, and you want to reduce this drain as

much as possible.

Android Networking

Although Android supports both TCP and UDP communication, most of the network calls for applications are

done over HTTP, which is built on top of TCP. In this section, I write mostly about HTTP communication and

related topics. I briefly cover connectionless protocols, like mDNS, in Chapter 18.

The first and most important rule to remember when it comes to networking on Android is: Never perform

network calls on the main thread. Starting with Android version 3.0 (Honeycomb), the main thread is protected

by the system, and your application will crash if it tries to do a network call from the main thread.

Second, and almost as important: Always do networking on Android from a Service and avoid performing

network calls from within an Activity . There are several reasons for doing so, but the most important one is

that you need to consider the fast changes in the Activity state if you perform network operations there.

The user might press Home in the middle of a network call, just to return to your application a second later. By

moving all network operations to a Service, you can avoid this issue. It also makes for a better overall design

because with this approach, you reduce the amount of code in your Activities, making it less complicated.

320 Part III: Pushing the Limits

However, in many cases you will be forced to perform network operations directly from an Activity,

as I show later in this chapter in the section “Foursquare API Using OAuth2” Please consider these cases as

exceptions to the rule. As much as possible, try to perform the network calls from a Service.

I recommend using either a callback interface or the LocalBroadcastManager to communicate network

results between the Activity and the Service. Another way is to store the results from a network call

directly into a ContentProvider and let the provider notify registered clients about the changes to any

data, as shown in Figure 17-1.

4. ContentProvider

1. User triggers a

Activity

notifies all connected

network refresh

clients

Content

3. Service insert data to

Service

Provider

ContentProvider

2. Service fetches data

from the Internet

Internet

Figure 17-1 Network calls from a Service inserting data into a ContentProvider

that notifies an Activity

HttpUrlConnection

The Android API has two APIs for HTTP communication, the Apache HttpClient and HttpUrlConnection.

Both have the same level of support in regard to features, but only HttpUrlConnection is recommended

because it’s the one being actively maintained by Google. Google has also implemented a number of useful

features that you otherwise would have to implement yourself, such as transparent response compression and

response cache.

private void enableHttpResponseCache() {

try {

long httpCacheSize = 10 * 1024 * 1024; // 10 MiB

File httpCacheDir = new File(getCacheDir(), “http”);

Class.forName(“android.net.http.HttpResponseCache”)

.getMethod(“install”, File.class, long.class)

.invoke(null, httpCacheDir, httpCacheSize);

} catch (Exception httpResponseCacheNotAvailable) {

}

}

The response cache is available from Android 4.0 (ICS) on, so if your application will also run on an earlier

version of Android, use the preceding approach to initiate the cache using Reflections. If your application will

target Android 4.0 at the minimum, you can use the following code to install the response cache:

Chapter 17: Networking, Web Service, and Remote APIs 321

try {

HttpResponseCache httpResponseCache = HttpResponseCache.

install(new File(getCacheDir(), “http”), CACHE_SIZE);

} catch (IOException e) {

Log.e(TAG, “Error installing response cache!”, e);

}

Choose a suitable cache size for your application. If you’re retrieving only small amounts of data, pick a cache

of only a few megabytes. The cache is application-private, so you don’t have to worry about leaking cached

responses to other applications on the device.

A Simple HTTP GET

Most network calls from applications will be in the form of simple HTTP GET requests. Here is an example of

how to perform such a request:

public JSONObject getJsonFromServer(URL url,

long lastModifiedTimestamp) {

try {

HttpURLConnection urlConnection = url.openConnection();

urlConnection.setRequestMethod(“GET”);

urlConnection.setInstanceFollowRedirects(true);

urlConnection.setIfModifiedBecause(lastModifiedTimestamp);

urlConnection.setUseCaches(true);

urlConnection.connect();

if (urlConnection.getResponseCode()

== HttpURLConnection.HTTP_OK) {

if (urlConnection.getContentType().

contains(“application/json”)) {

int length =

(HttpURLConnection) urlConnection.

getContentLength();

InputStream inputStream = urlConnection.

getInputStream();

String jsonString = readStreamToString(inputStream, length);

return new JSONObject(jsonString);

}

} else {

// TODO: Error handling...

}

} catch (IOException e) {

Log.e(TAG, “Error perform HTTP call!”, e);

} catch (JSONException e) {

Log.e(TAG, “Error parsing JSON!”, e);

}

return null;

}

private String readStreamToString(InputStream inputStream, int length)

throws IOException {

try {

BufferedReader bufferedReader =

322 Part III: Pushing the Limits

new BufferedReader(new InputStreamReader(inputStream));

StringBuilder stringBuilder = new StringBuilder(length);

char[] buffer = new char[length];

int charsRead;

while ((charsRead = bufferedReader.read(buffer)) != -1) {

stringBuilder.append(buffer, 0, charsRead);

}

return stringBuilder.toString();

} finally {

inputStream.close();

}

}

This example is a typical one for using HttpUrlConnection to make an HTTP GET and parse the response to

a JSONObject. The important thing to note here is the readStreamToString() method. Although you

may find code examples for how to read an InputStream to a String online, the preceding shows the

correct way of reading from a stream. Without any exception, you should always exhaust an InputStream

when reading. If you don’t, you leave data waiting in the lower layers of the platform that could waste resources

and keep your device from going to power saver mode, which is one of the most common mistakes when doing

network calls, so keep this in mind whenever you’re reading from an InputStream.

Uploading Files to a Server

Many applications will send data, like images or other files, to an online server. This has turned out to be one of

the more complex issues for developers because the standard Java APIs (including Android) doesn’t provide an

obvious, straightforward method for this issue. Sending data using HTTP involves doing an HTTP POST request

with the data in the body. However, the body requires some special formatting, and a number of header fields

must be set correctly.

The following example shows the necessary steps for posting a file to a server using HTTP POST.

public int postFileToURL(File file, String mimeType, URL url)

throws IOException {

DataOutputStream requestData = null;

try {

long size = file.length();

String fileName = file.getName();

// Create a random boundary string

Random random = new Random();

byte[] randomBytes = new byte[16];

random.nextBytes(randomBytes);

String boundary = Base64.

encodeToString(randomBytes, Base64.NO_WRAP);

HttpURLConnection urlConnection

= (HttpURLConnection) url.openConnection();

urlConnection.setUseCaches(false);

urlConnection.setDoOutput(true);

urlConnection.setRequestMethod(“POST”);

Chapter 17: Networking, Web Service, and Remote APIs 323

// Set the HTTP headers

urlConnection.setRequestProperty(“Connection”, “Keep-Alive”);

urlConnection.setRequestProperty(“Cache-Control”, “no-cache”);

urlConnection.setRequestProperty(“Content-Type”,

“multipart/form-data;boundary=” + boundary);

// If larger than MAX_FIXED_SIZE - use chunked streaming

if (size > MAX_FIXED_SIZE) {

urlConnection.setChunkedStreamingMode(0);

} else {

urlConnection.setFixedLengthStreamingMode((int) size);

}

// Open file for reading...

FileInputStream fileInput = new FileInputStream(file);

// Open connection to server...

OutputStream outputStream = urlConnection.getOutputStream();

requestData = new DataOutputStream(outputStream);

// Write first boundary for this file

requestData.writeBytes(“--” + boundary + CRLF);

// Let the server know the filename

requestData.writeBytes(“Content-Disposition: form-data; name=\””

+ fileName + “\”;filename=\”” + fileName + CRLF);

// ...and the MIME type of the file

requestData.writeBytes(“Content-Type: “ + mimeType + CRLF);

// Read the local file and write to the server in one loop

int bytesRead;

byte[] buffer = new byte[8192];

while ((bytesRead = fileInput.read(buffer)) != -1) {

requestData.write(buffer, 0, bytesRead);

}

// Write boundary indicating end of this file

requestData.writeBytes(CRLF);

requestData.writeBytes(“--” + boundary + “--” + CRLF);

requestData.flush();

return urlConnection.getResponseCode();

} finally {

if (requestData != null) {

requestData.close();

}

}

}

The important part here is to understand the boundary that is used to tell the server where the file data

starts and ends in the request body. Also, notice the check on whether the file size is larger than MAX_

FIXED_SIZE (in bytes), where chunked streaming or fixed-length streaming mode is used. For chunked

streaming, the parameter 0 means “system default” for the chunk size, which is what most clients should use

324 Part III: Pushing the Limits

in this mode. Chunking basically means that the data is sent to the server in parts, with each part prepended

with the size. Chunked streaming can be more efficient for memory use and reduces the risk of getting an

OutOfMemoryException. However, using fixed-length streaming mode is usually faster, although it requires

more memory while performing the streaming.

Volley

The standard HttpUrlConnection works well for most occasions, but it easily becomes quite complex

when you’re doing a lot of different HTTP requests. You still need to perform the requests on a background

thread and to ensure that everything is closed and shut down properly when your application is done. To make

things a bit easier, Google has started working on a new HTTP library that intends to make networking for

Android applications easier and faster. You can retrieve the latest version of the Volley library by cloning the

Git repository.

$ git clone https://android.googlesource.com/platform/frameworks/volley

The Volley library is currently under development but is already stable enough for use in applications. It

provides a very easy-to-use API for doing the typical network calls, and it handles all background threading and

other low-level things for you.

The following example shows how you can use Volley in a Service that makes HTTP GET calls and treats the

response as JSON data:

public class VolleyExample extends Service {

private LocalBinder mLocalBinder = new LocalBinder();

private RequestQueue mResponseQueue;

@Override

public void onCreate() {

super.onCreate();

mResponseQueue = Volley.newRequestQueue(this);

mResponseQueue.start();

}

@Override

public void onDestroy() {

super.onDestroy();

mResponseQueue.stop();

mResponseQueue = null;

}

public void doJsonRequest(String url,

Response.Listener<JSONObject> response,

Response.ErrorListener error) {

JsonObjectRequest jsonObjectRequest

= new JsonObjectRequest(Request.Method.GET,

url, null, response, error);

mResponseQueue.add(jsonObjectRequest);

}

Chapter 17: Networking, Web Service, and Remote APIs 325

@Override

public IBinder onBind(Intent intent) {

return mLocalBinder;

}

public class LocalBinder extends Binder {

public VolleyExample getService() {

return VolleyExample.this;

}

}

}

You pass in two callbacks, one for a successful response and one for error. You don’t need to worry about

starting a new background thread for the network call or initiating the connection yourself; this is all taken

care of by the Volley library. All you need to do is add the new request to the RequestQueue so that it gets

processed.

public void doJsonUpdateRequest() {

Response.Listener<JSONObject> responseListener

= new Response.Listener<JSONObject>() {

@Override

public void onResponse(JSONObject response) {

handleJsonResponse(response);

}

};

Response.ErrorListener errorListener

= new Response.ErrorListener() {

@Override

public void onErrorResponse(VolleyError error) {

handleError(error);

}

};

mService.doJsonRequest(API_URL, responseListener, errorListener);

}

In the preceding example, you define a callback for a successful response and an error callback. Both of these

will run on a thread other than the main thread, so if you intend to modify your UI, be sure to post that update

using a Handler or an AsyncTask.

Currently, the Volley library doesn’t support uploading arbitrary binary files. If you need to do

uploading, use the method described in the earlier section, “Uploading Files to a Server.” However,

continue to use Volley for other requests if possible.

If you’re doing lots of different HTTP requests, I highly recommend using the Volley library. Because it’s also

hosted as a part of the Android Open Source Project, it’s likely to become a central part of the Android platform

in the future.

326 Part III: Pushing the Limits

OkHttp and SPDY

A big problem with HTTP is that it allows only one request and response at a time per connection, which forces

browsers and other clients to spawn multiple sockets in order to perform requests in parallel. This issue is less

problematic for clients because there will be relatively few connections from one application at any given time;

however, it makes a huge difference for servers. In 2009, Google started working on an updated HTTP protocol

to address these issues. The result was a new wire protocol named SPDY (pronounced speedy) that allows

multiple HTTP requests to be multiplexed over a single socket connection. This protocol has become a de facto

open standard for the next generation of HTTP. This protocol doesn’t replace HTTP; instead, it modifies how

requests and responses are sent over the wire. The IETF working group for HTTP has announced that it will start

the work on HTTP 2.0 and use the SPDY protocol as a starting point.

If you have full control of both the clients and the server, it may be worth investigating the use of SPDY as an

alternative to regular HTTP/1.1—doing so will greatly reduce the network’s load and increase the performance.

SPDY is already seeing good support in the major web browsers, and there are several implementations for

different platforms available, including Android.

If you choose to use SPDY as your communication protocol, I recommend that you choose the third-party

library called OkHttp, which is developed by Square, Inc., and available as open source on GitHub (http://

square.github.io/okhttp). The library is available as a Maven dependency by including the following

line in your Gradle script:

compile ‘com.squareup.okhttp:okhttp:1.1.1’

This library is simply a new and improved HTTP client with support for the SPDY protocol. It uses the

HttpUrlConnection interface, so switching to this in your existing code should require little work.

The following code shows how you can use the OkHttp library in Android:

public class OkHttpExample {

private final OkHttpClient mOkHttpClient;

public OkHttpExample() {

mOkHttpClient = new OkHttpClient();

}

public String okHttpDemo(URL url) throws IOException {

HttpURLConnection urlConnection = mOkHttpClient.open(url);

InputStream inputStream = null;

urlConnection.setRequestMethod(“GET”);

urlConnection.connect();

if (urlConnection.getResponseCode()

== HttpURLConnection.HTTP_OK) {

inputStream = urlConnection.getInputStream();

return readStreamToString(inputStream,

urlConnection.getContentLength());

}

return null;

}

Chapter 17: Networking, Web Service, and Remote APIs 327

private String readStreamToString(InputStream inputStream,

int length)

throws IOException {

try {

BufferedReader bufferedReader =

new BufferedReader(new InputStreamReader(inputStre

am));

StringBuilder stringBuilder = new StringBuilder(length);

char[] buffer = new char[length];

int charsRead;

while ((charsRead = bufferedReader.read(buffer)) != -1) {

stringBuilder.append(buffer, 0, charsRead);

}

return stringBuilder.toString();

} finally {

inputStream.close();

}

}

}

When you create a new instance of the OkHttpClient, it will set up everything you need, such as connection

polling and response cache. This implementation is extremely fast even on regular HTTP requests, so using

it may it be a good idea in general. When using OkHttp for SPDY communication, you’ll notice a huge

improvement in performance for your network calls.

Web Sockets

Web Sockets is the latest darling of the web and is an extension running on top of standard HTTP. It allows for

asynchronous message-based communication between a client and a server. It starts with a regular HTTP GET

request that contains special HTTP headers indicating that the client wants to upgrade the connection to a Web

Socket connection.

Here is an example of the clients GET request for initiating a Web Socket connection:

GET /websocket HTTP/1.1

Host: myserver.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: MjExMjM0MTI0MTI0MTI0MTIzCg==

Sec-WebSocket-Protocol: chat

Sec-WebSocket-Version: 13

Origin: http://myserver.com

If the client request is accepted, the server responds with the following:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

Sec-WebSocket-Protocol: chat

328 Part III: Pushing the Limits

The values in the preceding headers aren’t valid for every case but should be calculated according to

the Web Socket protocol specification. Normally, you don’t need to bother about these details when

using a readymade library for Web Socket communication.

When a Web Socket is established, both sides (client and server) can send messages to each other asynchronously.

More importantly, you now have a way to quickly notify the client from your server. Messages can be either text

or binary and are usually quite small. If you need to transmit a large file between the client and server, stick to

standard HTTP instead. Web Sockets is intended for sending notifications with a relatively small payload.

I recommend that you choose a suitable data format when communicating over Web Sockets. In most cases,

JSON is sufficient and allows for a simple implementation. JSON messages should be sent as text-messages over

Web Sockets. For more advanced scenarios with mixed-type data, I recommend using Google Protocol Buffers,

as I describe in Chapter 9.

Although you can implement your own Web Sockets client using the standard Socket class in Android, I highly

recommend that you use one of the many third-party libraries available. You can choose from several; the one I

show here is the one I consider most stable at the moment, the Web Socket implementation for Java by Nathan

Rajlich (also known as TooTallNate), which you can find at http://java-websocket.org. In addition,

it contains a server implementation for Web Sockets, which is useful in Chapter 18. To use this library, simply

include the following dependency in your Gradle file:

compile ‘org.java-websocket:Java-WebSocket:1.3.0’

When using Web Sockets through this library, you don’t use the HttpUrlConnection: instead, you create a

WebSocketClient and connect to a URI.

The following code is a complete example of using this library to connect to a Web Socket online:

public class ChatService extends Service {

private static final String TAG = “ChatService”;

private ChatWebSocketClient mChatWebSocketClient;

private ChatClient mChatClient;

private LocalBinder mLocalBinder = new LocalBinder();

public IBinder onBind(Intent intent) {

return mLocalBinder;

}

public IBinder onBind(Intent intent) {

return null;

}

public void connectToChatServer(URI serverUri) {

new ChatWebSocketClient(serverUri).connect();

}

Chapter 17: Networking, Web Service, and Remote APIs 329

public void disconnect() {

if (mChatWebSocketClient != null) {

mChatWebSocketClient.close();

}

}

public void setChatClient(ChatClient chatClient) {

mChatClient = chatClient;

}

public void sendMessage(String message) {

if(mChatWebSocketClient != null) {

mChatWebSocketClient.send(message);

}

}

public boolean isConnected() {

return mChatWebSocketClient != null;

public interface ChatClient {

void onConnected();

void onMessageReceived(String from, String body, Date timestamp);

void onDisconnected();

}

private class ChatWebSocketClient extends WebSocketClient {

public ChatWebSocketClient(URI serverURI) {

super(serverURI);

}

@Override

public void onOpen(ServerHandshake serverHandshake) {

// Called when the Web Socket is connected

mChatWebSocketClient = this;

if(mChatClient != null) {

mChatClient.onConnected();

}

Notification notification = buildNotification();

startForeground(1001, notification);

}

@Override

public void onMessage(String message) {

// Called when a text message is received

if(mChatClient != null) {

try {

JSONObject chatMessage = new JSONObject(message);

String from = chatMessage.getString(“from”);

String body = chatMessage.getString(“body”);

330 Part III: Pushing the Limits

Date timestamp =

new Date(chatMessage.getLong(“timestamp”));

mChatClient.onMessageReceived(from, body, timestamp);

} catch (JSONException e) {

Log.e(TAG, “Malformed message!”, e);

}

}

}

@Override

public void onMessage(ByteBuffer bytes) {

// Called when a binary message is received

}

@Override

public void onClose(int code, String reason, boolean remote) {

// Called when the connection is closed

mChatWebSocketClient = null;

if(mChatClient != null) {

mChatClient.onDisconnected();

}

stopForeground(true);

}

@Override

public void onError(Exception e) {

// Called on in case of communication error

}

}

private class LocalBinder extends Binder {

public ChatService getService() {

return ChatService.this;

}

}

}

This is a guide for implementing Web Socket support in your code. In a real-life application, you should add

additional security and error checks. The important thing here is that when you call WebSocketClient.

connect(), it spawns a new thread on which this Web Socket will live, which means you don’t have to do the

background thread yourself when using this library.

The ChatClient extends the WebSocketClient class and implements the different event callbacks. All

these callbacks occur on the thread that the Web Socket is running on, so never block these calls because doing

so will stall any other traffic on the same Web Socket.

It’s good practice to use the onOpen() and onClose() callbacks for determining when the you can start

communicating over the Web Socket. In the preceding example, the mChatClient member is set and reset

(to null) in the respective method, allowing for a simple null check when sending a message.

Chapter 17: Networking, Web Service, and Remote APIs 331

I return to the topic of Web Sockets in Chapter 18, where I describe how you can communicate directly between

two devices.

Integrating Web Services

Most web services that you will use in your Android app usually fit into one of three categories: those that

don’t require authentication for a user, those that do require authentication for a user but lack a native SDK for

Android, and those that require user authentication and provide an SDK for Android. In this section, I provide

three examples that illustrate each of these categories.

Google Static Maps v2

In Chapter 13, I cover the use of the new and powerful Location API for Android. Although it’s fairly easy to use,

it may sometimes be more than you need. If your application needs to show only a static map that the user

won’t interact with, you can use Google Static Maps v2 API instead, which is a web service from Google that

allows you to fetch a piece of Google Maps as a bitmap image (PNG, GIF, or JPEG).

Here is a simple static method for retrieving a static map:

public class StaticMapsFetcher {

public static final String BASE_URL

= “http://maps.googleapis.com/maps/api/staticmap”;

// TOOD Create this before release!

public static final String API_KEY = null;

public static final String UTF8 = “UTF-8”;

private static final String TAG = “StaticMapsFetcher”;

public static Bitmap fetchMapWithMarkers(String address,

int width,

int height,

String maptype,

List<String> markers) {

HttpURLConnection urlConnection = null;

try {

StringBuilder queryString = new StringBuilder(“?”);

if (address != null) {

queryString.append(“center=”).

append(URLEncoder.encode(address, UTF8)).

append(“&”);

}

if (width > 0 && height > 0) {

queryString.append(“size=”).

append(String.format(“%dx%d”, width, height)).

append(“&”);

}

if (maptype != null) {

queryString.append(“maptype=”).

append(maptype).append(“&”);

}

332 Part III: Pushing the Limits

if (markers != null) {

for (String marker : markers) {

queryString.append(“markers=”).

append(URLEncoder.encode(marker, UTF8));

}

}

if (API_KEY != null) {

queryString.append(“key=”).append(API_KEY).append(“&”;

}

queryString.append(“sensor=false”);

URL url = new URL(BASE_URL + queryString.toString());

urlConnection = url.openConnection();

urlConnection.connect();

if (urlConnection.getResponseCode()

== HttpURLConnection.HTTP_OK) {

BufferedInputStream bufferedInputStream

= new BufferedInputStream(urlConnection.

getInputStream());

return BitmapFactory.decodeStream(bufferedInputStream);

} else {

return null;

}

} catch (IOException e) {

Log.e(TAG, “Error fetching map!”, e);

} finally {

if (urlConnection != null) {

urlConnection.disconnect();

}

}

}

}

Note that the API_KEY is left as null; this is allowed, but not recommended, for the Google Static Maps API.

Before you release an application that uses this API, you should create a new API key using the Google API

Developer Console.

Also note how the use of URLEncoder.encode() for the values of each parameter. This guarantees that

whatever the value is, it will be encoded properly in the URL. When a web service is called, an error stating a bad

request is common because the server cannot parse the request parameters properly.

This example shows how the simplest web services can be integrated into an Android application. They

are one-way, don’t require authentication, and have only a few parameters. You can find more information

about the parameters for Google Static Maps API API at https://developers.google.com/maps/

documentation/staticmaps.

Foursquare API Using OAuth2

When you use a web service that requires a user account, you need to authenticate the user in some way.

However, because your application will be a third-party client for this service, you need some way to provide

Chapter 17: Networking, Web Service, and Remote APIs 333

authentication that doesn’t compromise the user’s credentials, meaning the username and password shouldn’t

pass through your code. To solve this issue, a standard named OAuth was developed. This standard is now on

its second version and is called OAuth2. It was designed so that web applications can allow one website to

integrate with a second one on behalf of a user. To use it on Android, you route the authentication through a

WebView that displays the service authentication web page for the user.

Although it’s possible to implement all of the steps for OAuth2 yourself, I recommend using a third-party library

that hides away some of the complexity. The one I recommend is called Scribe and can be found at https://

github.com/fernandezpablo85/scribe-java. Scribe provides a simple wrapper on top of the

HttpUrlConnection class for OAuth2-enabled services, so it’s an excellent use for Android.

In this OAuth2 example, the user’s friends are displayed on Foursquare. Although many of the API calls for

Foursquare don’t require authentication (like searching for venues), getting a list of Foursquare friends will

obviously require authentication by the user.

The following code shows an Activity that you can use to get authorization for a user’s Foursquare account

using OAuth2:

public class OAuthActivity extends Activity {

public static final String CLIENT_ID

= “<Client ID from foursquare.com/developer>”;

public static final String CLIENT_SECRET

= “<Client SECRET from foursquare.com/developer>”;

public static final Token EMPTY_TOKEN = null;

public static final String ACCESS_TOKEN = “foursquare.access_token”;

private static final String TAG = “FoursquareOAuth2”;

private OAuthService mOAuthService;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

@Override

protected void onResume() {

super.onResume();

mOAuthService = new ServiceBuilder()

.provider(Foursquare2Api.class)

.apiKey(CLIENT_ID)

.apiSecret(CLIENT_SECRET)

.callback(“oauth://foursquare”)

.build();

String authorizationUrl =

mOAuthService.getAuthorizationUrl(EMPTY_TOKEN);

WebView webView = (WebView) findViewById(R.id.oauth_view);

WebViewClient webViewClient = new WebViewClient() {

@Override

public boolean shouldOverrideUrlLoading(WebView view,

334 Part III: Pushing the Limits

String url) {

if (url.startsWith(“oauth”)) {

Uri uri = Uri.parse(url);

String oauthCode = uri.getQueryParameter(“code”);

Verifier verifier = new Verifier(oauthCode);

new GetTokenAccess().execute(verifier);

return true;