GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,

GLES20.GL_TEXTURE_MIN_FILTER,

GLES20.GL_NEAREST);

GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES,

GLES20.GL_TEXTURE_MAG_FILTER,

GLES20.GL_LINEAR);

// Define the SurfaceTexture and assign it to our MediaPlayer

mSurface = new SurfaceTexture(mTextureID);

mSurface.setOnFrameAvailableListener(this);

Surface surface = new Surface(mSurface);

mMediaPlayer.setSurface(surface);

surface.release();

synchronized (this) {

updateSurface = false;

}

mMediaPlayer.start();

}

220 Part III: Pushing the Limits

Instead of GLES20.GL_TEXTURE2D, you use the constant defined earlier referring to the extension. Next, you

create a SurfaceTexture and add it to a Surface object, which in turn is used as the rendering surface for

the MediaPlayer. Doing so causes each video frame to be rendered into a streaming texture instead of to

the screen. A callback is also added that will update the variable updateSurface as soon as a new frame is

available. Finally, you start the playback of the video on the MediaPlayer.

In the onDrawFrame() method shown in the following code, you start by checking whether a new frame

is available. If so, the SurfaceTexture is updated and the transformation matrix relevant for this frame is

stored in mSTMatrix. This will be used to transform the texture coordinates to the proper sampling location

in the frame. Also, note that the binding of the texture is also changed to use the extension constant GL_

TEXTURE_EXTERNAL_OES.

public void onDrawFrame(GL10 glUnused) {

synchronized (this) {

if (updateSurface) {

mSurface.updateTexImage();

mSurface.getTransformMatrix(mSTMatrix);

updateSurface = false;

}

}

... omitted for brevity ...

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);

GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);

GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

checkGlError(“glDrawArrays”);

GLES20.glFinish();

}

In the following code you can see the vertex shader used in this example. Here, the uSTMatrix uniform is used

together with the regular transformation matrix and vertex position to calculate the final vTextureCoord.

uniform mat4 uMVPMatrix;

uniform mat4 uSTMatrix;

attribute vec4 aPosition;

attribute vec4 aTextureCoord;

varying vec2 vTextureCoord;

void main() {

gl_Position = uMVPMatrix * aPosition;

vTextureCoord = (uSTMatrix * aTextureCoord).xy;

}

The fragment shader starts by specifying that the GL_OES_EGL_image_external extension is used and

is required for this shader to work, as shown in the following code. Next, the sTexture changes its type to

samplerExternalOES.

Chapter 11: Advanced Audio, Video, and Camera Applications 221

#extension GL_OES_EGL_image_external : require

precision mediump float;

varying vec2 vTextureCoord;

uniform samplerExternalOES sTexture;

void main() {

gl_FragColor = texture2D(sTexture, vTextureCoord);

}

This simply renders the video as-is without changes to the screen, as shown in Figure 11-1.

Figure 11-1 Rendering a video using OpenGL ES 2 .0 and SurfaceTexture

By changing only the fragment shader, you can now achieve some cool effects on the video.

The following code applies a negative effect to the video in runtime (see Figure 11-2):

#extension GL_OES_EGL_image_external : require

precision mediump float;

varying vec2 vTextureCoord;

uniform samplerExternalOES sTexture;

uniform float uResS;

uniform float uResT;

void main() {

vec2 onePixel = vec2(1.0 / uResS, 1.0 / uResT);

float T = 1.0;

vec2 st = vTextureCoord.st;

vec3 irgb = texture2D(sTexture, st).rgb;

vec3 neg = vec3(1., 1., 1.)-irgb;

gl_FragColor = vec4(mix(irgb, neg, T), 1.);

}

222 Part III: Pushing the Limits

This shader takes two extra parameters telling how large the texture is. These two parameters are useful in other

types of filter effects as well. Refer to the OpenGL ES 2.0 resource listed in the “Further Resources” section at the

end of this chapter for more information.

Figure 11-2 Rendering a video with OpenGL ES 2 .0 and using a filter for negative effect

Camera Processing with OpenGL ES 2.0

Using OpenGL ES 2.0, you can follow the same process with the camera as you did with video in the previous

section. You can connect the stream of preview frames to the same type of streaming texture and perform

real-time processing of these frames. In this way, you can create powerful augmented reality (AR) applications,

showing image filters in real time or run some powerful computations on the image that would take much

more time if done on the CPU.

mSurface = new SurfaceTexture(mTextureID);

mSurface.setOnFrameAvailableListener(this);

try {

mCamera.setPreviewTexture(mSurface);

mCamera.startPreview();

} catch (IOException e) {

e.printStackTrace();

}

The only difference from the previous example is in the onDrawFrame() method, where instead of working

with a MediaPlayer, you give the SurfaceTexture directly to the Camera as a preview texture.

The fragment shader used in this example will perform edge detection on the preview frames. The result of this

can be seen in Figure 11-3.

Chapter 11: Advanced Audio, Video, and Camera Applications 223

Figure 11-3 The edge detection effect on the camera preview frames running on a Nexus 4

The following code shows the code for this shader:

#extension GL_OES_EGL_image_external : require

precision mediump float;

varying vec2 vTextureCoord;

uniform samplerExternalOES sTexture;

uniform float uResS;

uniform float uResT;

void main() {

vec3 irgb = texture2D(sTexture, vTextureCoord).rgb;

float ResS = uResS;

float ResT = uResT;

vec2 stp0 = vec2(1./ResS, 0.);

vec2 st0p = vec2(0., 1./ResT);

vec2 stpp = vec2(1./ResS, 1./ResT);

vec2 stpm = vec2(1./ResS, -1./ResT);

const vec3 W = vec3(0.2125, 0.7154, 0.0721);

float i00 = dot(texture2D(sTexture, vTextureCoord).rgb, W);

float im1m1 = dot(texture2D(sTexture, vTextureCoord-stpp).rgb, W);

float ip1p1 = dot(texture2D(sTexture, vTextureCoord+stpp).rgb, W);

float im1p1 = dot(texture2D(sTexture, vTextureCoord-stpm).rgb, W);

float ip1m1 = dot(texture2D(sTexture, vTextureCoord+stpm).rgb, W);

float im10 = dot(texture2D(sTexture, vTextureCoord-stp0).rgb, W);

float ip10 = dot(texture2D(sTexture, vTextureCoord+stp0).rgb, W);

float i0m1 = dot(texture2D(sTexture, vTextureCoord-st0p).rgb, W);

float i0p1 = dot(texture2D(sTexture, vTextureCoord+st0p).rgb, W);

float h = -1.*im1p1 - 2.*i0p1 - 1.*ip1p1 + 1.*im1m1 + 2.*i0m1 + 1.*ip1m1;

float v = -1.*im1m1 - 2.*im10 - 1.*im1p1 + 1.*ip1m1 + 2.*ip10 + 1.*ip1p1;

float mag = length(vec2(h, v));

vec3 target = vec3(mag, mag, mag);

gl_FragColor = vec4(mix(irgb, target, 1.0),1.);

}

224 Part III: Pushing the Limits

Encoding Media

Android 4.3, more specifically API level 18, includes a number of improvements to the media APIs in Android.

The two significant classes in this API are MediaCodec and MediaMuxer. The MediaCodec class, introduced

earlier in a more limited version in Android 4.2 (with API level 16), provides application developers access to

low-level media codec functionality. With API level 18, the MediaCodec class also supports encoding from a

Surface, which means it is possible to record OpenGL ES 2.0 scenes into a video stream.

The MediaMuxer class allows developers to mux raw media streams into a media file for playback. By using

both of these classes, you can add the functionality of recording a game session. You can also use these classes

with the previous examples of processing a video or camera feed to store the result as a new MP4 file.

Recording an OpenGL Scene

The following example shows the basics of using the MediaCodec and MediaMuxer for recording the result of

an OpenGL ES 2.0 scene to an MP4 file. This method should work with any OpenGL ES 2.0 content. With this code,

you create the encoder and the muxer, and use the MediaFormat to specify the parameters for the encoder:

private void prepareEncoder() {

mBufferInfo = new MediaCodec.BufferInfo();

MediaFormat format = MediaFormat.createVideoFormat(“video/avc”,

VIDEO_WIDTH, VIDEO_HEIGHT);

format.setInteger(MediaFormat.KEY_COLOR_FORMAT,

MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

format.setInteger(MediaFormat.KEY_BIT_RATE, 6000000); // 6 Mbps

format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);

format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);

// Create a MediaCodec encoder, and configure it with our format.

mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);

mEncoder.configure(format, null, null,

MediaCodec.CONFIGURE_FLAG_ENCODE);

mMuxer = new MediaMuxer(mOutputFile.getAbsolutePath(),

MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

mSurface = mEncoder.createInputSurface();

}

This example uses H.264 as the output format, with a bitrate of 6 Mbps and 30 frames per second with 10

frames between each I-frame. You also create the Surface that will be used as the input for the encoder.

After the encoder and muxer are created, you next set up the EGL context that will be used for recording, as

shown in the following code:

private static final int EGL_RECORDABLE_ANDROID = 0x3142;

private void recorderEglSetup() {

mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);

if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {

throw new RuntimeException(“EGL Get Display failed!”);

}

Chapter 11: Advanced Audio, Video, and Camera Applications 225

int[] version = new int[2];

if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {

mEGLDisplay = null;

throw new RuntimeException(“EGL init error!”);

}

int[] attribList = {

EGL14.EGL_RED_SIZE, 8,

EGL14.EGL_GREEN_SIZE, 8,

EGL14.EGL_BLUE_SIZE, 8,

EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,

EGL_RECORDABLE_ANDROID, 1,

EGL14.EGL_NONE

};

EGLConfig[] configs = new EGLConfig[1];

int[] numConfigs = new int[1];

if (!EGL14.eglChooseConfig(mEGLDisplay, attribList,

0, configs, 0, configs.length,

numConfigs, 0)) {

throw new RuntimeException(“EGL Config error!”);

}

int[] glAttribs = {

EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,

EGL14.EGL_NONE

};

mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0],

EGL14.eglGetCurrentContext(),

glAttribs, 0);

int[] surfaceAttribs = {

EGL14.EGL_NONE

};

mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0],

mSurface, surfaceAttribs,

0);

}

public void releaseRecorder() {

mEncoder.stop();

mEncoder.release();

mEncoder = null;

mMuxer.stop();

mMuxer.release();

mMuxer = null;

EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);

EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);

EGL14.eglReleaseThread();

EGL14.eglTerminate(mEGLDisplay);

226 Part III: Pushing the Limits

mSurface.release();

mSurface = null;

mEGLDisplay = null;

mEGLContext = null;

mEGLSurface = null;

}

The two most important parts are marked in bold. The constant defined before the method is used when

setting up EGL to tell Android that this is a recordable context. The mSurface variable is the same as the one

created in the prepareEncoder() method.

The release() method must be called after recording is completed in order to clean up all the resources held

by your application. Note that releasing this EGL context doesn’t release the same context used for rendering to

the display.

Because recording a scene requires two rendering passes, once to the physical display and once to the

Surface used for encoding, you need two methods for saving and restoring the rendering state of OpenGL.

The following code shows these two methods.

private void storeRenderState() {

System.arraycopy(mProjMatrix,

0, mSavedProjMatrix,

0, mProjMatrix.length);

mSavedEglDisplay = EGL14.eglGetCurrentDisplay();

mSavedEglDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);

mSavedEglReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ);

mSavedEglContext = EGL14.eglGetCurrentContext();

}

private void restoreRenderState() {

if (!EGL14.eglMakeCurrent(mSavedEglDisplay,

mSavedEglDrawSurface,

mSavedEglReadSurface,

mSavedEglContext)) {

throw new RuntimeException(“eglMakeCurrent failed!”);

}

System.arraycopy(mSavedProjMatrix,

0, mProjMatrix, 0, mProjMatrix.length);

}

Next, you need a method for transferring the encoded video stream to the muxer used for writing to the MP4

file:

private void drainEncoder(boolean endOfStream) {

if (endOfStream) {

mEncoder.signalEndOfInputStream();

}

ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();

Chapter 11: Advanced Audio, Video, and Camera Applications 227

while (true) {

int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, 0);

if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {

break;

} else if (encoderStatus ==

MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {

encoderOutputBuffers = mEncoder.getOutputBuffers();

} else if (encoderStatus ==

MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

MediaFormat newFormat = mEncoder.getOutputFormat();

mTrackIndex = mMuxer.addTrack(newFormat);

mMuxer.start();

mMuxerStarted = true;

} else {

ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];

if ((mBufferInfo.flags &

MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {

mBufferInfo.size = 0;

}

if (mBufferInfo.size != 0) {

encodedData.position(mBufferInfo.offset);

encodedData.limit(mBufferInfo.offset + mBufferInfo.size);

mMuxer.writeSampleData(mTrackIndex, encodedData,

mBufferInfo);

}

mEncoder.releaseOutputBuffer(encoderStatus, false);

if ((mBufferInfo.flags

& MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {

break;

}

}

}

}

The while loop in this example will continue until it has exhausted any remaining data from the encoder

output.

In the following code you can see a typical onSurfaceChanged() method used for OpenGL ES scenes in

Android, with the addition of setting up the encoder and EGL context for recording the scene:

public void onSurfaceChanged(GL10 gl10, int width, int height) {

GLES20.glViewport(0, 0, width, height);

float ratio = (float) width / height;

Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);

prepareEncoder(mContext);

if (mEncoder != null) {

228 Part III: Pushing the Limits

storeRenderState();

recorderEglSetup();

mEncoder.start();

if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface,

mEGLSurface, mEGLContext)) {

throw new RuntimeException(“eglMakeCurrent failed”);

}

restoreRenderState();

mFrameCount = 0;

}

}

The onDrawFrame() method in the following code first draws the scene to the main display, followed by

a check to determine if the encoder is valid. The remaining code changes the EGL context, configures the

viewport for the second rendering, and draws the scene to the new viewport.

public void onDrawFrame(GL10 gl10) {

drawFrame();

if(mEncoder != null) {

storeRenderState();

if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface,

mEGLSurface, mEGLContext)) {

throw new RuntimeException(“eglMakeCurrent failed”);

}

Matrix.orthoM(mProjMatrix, 0, 0, mWidth,

0, mHeight, -1, 1);

GLES20.glViewport(mViewportXoff, mViewportYoff,

mViewportWidth, mViewportHeight);

drawFrame();

drainEncoder(false);

long when = System.nanoTime();

EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, when);

EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);

restoreRenderState();

}

}

After the second drawing is complete the encoder is drained of any data.

The method in the following code shows how to signal to stop the encoder and release all its related resources:

public void stopRecording() {

drainEncoder(true);

releaseRecorder();

}

This method should be called either when the user leaves the application or when recording should be stopped.

Chapter 11: Advanced Audio, Video, and Camera Applications 229

Summary

With the later versions of Android, the possibilities for doing advanced media operations in applications have

become much better. The examples in this chapter serve to demonstrate some of the more advanced APIs. You

can build a complete application from a single example, or combine them into a more complex experience.

While the examples in this chapter are not complete implementations of an application, they should be enough

for experienced developers to get started.

Further Resources

Documentation

From the Khronos Group on the OpenSL ES API: www.khronos.org/opensles

Specification for the OpenGL extension GL_OES_EGL_image_external: www.khronos.org/registry/

gles/extensions/OES/OES_EGL_image_external.txt

LittleCheeseCake by Yu Lu using OpenGL ES 2.0 shaders on Android: http://littlecheesecake.

me/blog/13804700/opengles-shader

Using the MediaCodec and MediaMuxer in Android: http://bigflake.com/mediacodec/

Chapter 12

Secure Android

Applications

Security is such a complex topic that it deserves its own chapter in this book. Although security is composed of

many aspects, in this chapter I focus on how you as an application developer can manage the security around

your application and its data. Because of Andoid’s current, excellent API, I also include a section on device

administration.

Android Security Concepts

Android has an advanced security model for protecting application data and services from other applications.

Every application runs with its own unique user ID that provides basic protection. Each application is signed

with its unique key, which works as a foundation around the security model in the Android framework.

Moreover, Android’s permission system lets a given application share access to its component only to other

applications that explicitly declare the right permission in their manifest. Apps can also define permissions such

that only apps signed with the same key can use them. Finally, the Android APIs provide methods for verifying

signatures, for verifying the user ID of calling processes, and for using strong encryption schemes.

Signatures and Keys

All applications running in Android are signed with a key, including the Android system itself. During normal

development, you will use the auto-generated debug key for signing your application (which is done

automatically by the Gradle build system or the IDE). When you publish your application on the Google Play

Store, be sure to use a unique key that you generate manually using the keytool application.

You can use the same key for all the applications you publish, but I recommend that you create a unique

key for each application. The only reasons for using the same key for multiple applications is when the

applications need to access each other’s data directly, or you have defined permissions with the protection level

signature.

The following command shows how to generate a new, unique key for your application. A good practice is to

give the alias the same name as the package name for your application. Also, if you’re maintaining application

keys for multiple organizations, I suggest you use a separate keystore for each organization.

$ keytool -genkey -v -keystore <keystore filename> \

-alias <alias for key> -keyalg RSA -keysize 2048 -validity 10000

232 Part III: Pushing the Limits

When you generate a new key, the keytool asks you for a password. If you don’t provide a password, anyone

with access to the keystore file can create a properly signed application. Thus it’s highly recommended that you

use a unique password for each keystore file.

Always back up your keystore files online—using, for example, Google Drive or Dropbox. Otherwise,

if you lose your keystore file or forget the password, you’ll no longer be able to sign new versions of

your application and that effectively makes it impossible to provide upgrades for your users.

Android Permissions

Using a feature in Android that requires a special permission is only a matter of adding a uses-permission

tag in the AndroidManifest.xml file. This tells the system that your application requires that particular

permission, and notifies the user about this requirement before installation.

Android defines five protection levels: normal, dangerous, signature, signatureOrSystem, and

system. The default level, unless otherwise specified, is normal, which is generally used to signal the system

that an application requires the functions for this permission. The user is notified before installation (usually in

the Google Play Store application) only when the permission is defined as dangerous.

A signature protection level requires that the application be signed with the same certificate as the

application that defined the permission, which can be useful for device vendors because they can define

permissions that only applications signed with the same certificate as the system can use. In this way, it’s

possible for device vendors to release new applications for their devices that use protected system services.

The signatureOrSystem and system protection levels tell the Android system that applications must

reside on the system partition of the device in order to be able to use the permission. The most common

examples of this feature are the Google applications (Gmail, Google Play Services, YouTube, and so on)

that come pre-installed on the system partition. These applications can use many of the permissions that

normal applications cannot reach, even though Google, and not the device manufacturer, signed them. The

signatureOrSystem is basically a combination of the two protection levels.

Declaring Custom Permissions

In most cases, your application will be self-contained, so there will be little need to declare any new permissions.

However, if you will provide an API for other applications to use, for instance a plug-in feature, I recommend

defining your own permissions.

The following example shows the relevant parts for defining a read and write permission on a

ContentProvider.

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

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

package=”com.aaptl.security”

android:versionCode=”1”

android:versionName=”1.0” >

Chapter 12: Secure Android Applications 233

<permission android:name=”com.aaptl.security.READ_DATA”

android:description=”@string/read_perm_desc”

android:label=”@string/read_perm_label”

android:protectionLevel=”dangerous” />

<permission android:name=”com.aptl.security.WRITE_DATA”

android:descriptiona=”@string/write_perm_desc”

android:label=”@string/write_perm_label”

android:protectionLevel=”signature” />

...

<provider

android:name=”.TaskProvider”

android:authorities=”com.aaptl.security.provider”

android:readPermission=”com.aaptl.security.READ_DATA”

android:writePermission=”com.aaptl.security.WRITE_DATA”

android:exported=”true”/>

...

</manifest>

The read permission’s protection level is set to dangerous, which will show up when the user wants to install

an application that uses this permission. The write permission, on the other hand, has its protection level set to

signature, which limits the user of this permission to applications signed with the same certificate.

You can also add the attribute android:permissionFlags=”costsMoney” , which signals

the user that an application using this permission will generate a cost. Common examples are

applications that want to send SMS. Whenever your application provides an API that could result in a

monetary cost for users, protect the API with a permission that has this flag set.

Protecting User Data

If your application creates content that shouldn’t be accessible to other applications, store it in the default data

directory for the application. Storing data on external storage is never safe, unless you encrypt the content (see

the section “Client-Side Data Encryption,” later in this chapter). Files stored in an application’s data directory can

be accessed only by that application or an application with the same user ID (which requires the applications to

be signed by the same certificate).

When you create databases using the SQLiteOpenHelper, as shown in Chapter 9, they end up in the

application’s data directory by default, so these SQLite databases are always protected from other applications.

To create ordinary files, however, you use the method Context.openFileOutput(), which creates a new

file (or opens an existing file for appending data) in the application’s data directory.

234 Part III: Pushing the Limits

The following method is an example of how to append data to a private file.

public static void appendStringToPrivateFile(Context context,

String data, String filename)

{

FileOutputStream outputStream

= context.openFileOutput(filename,

Context.MODE_APPEND|Context.MODE_PRIVATE);

outputStream.write(data.getBytes(“UTF-8”));

outputStream.close();

}

You open files for reading using Context.openFileInput(). Notice the use of both Context.MODE_

APPEND and Context.MODE_PRIVATE as flags to openFileOutput(). These flags will make the file

always append data to the end when writing and accessible only to your application, respectively. To protect

your data, always use MODE_PRIVATE (which is also the default flag). To provide a file that other applications

can read, you should instead use a ContentProvider and define the appropriate permissions.

The two flags Context.MODE_WORLD_READABLE and Context.MODE_WORLD_WRITABLE make

files in the application’s data directory world readable and writable. However, these flags are now

deprecated, and you’re strongly encouraged not to use them.

Because the MODE_PRIVATE flag sets the correct Linux file permissions, they protect files from being accessed

by other applications. However, simply using the method described in the preceding example should not be

considered a secure solution. If you’re storing very sensitive information on a device, I strongly advise applying

some encryption to the files.

Verifying Calling Applications

Although permissions provide a way of ensuring that the user accepts that an application can access your

services or providers, at times it’s necessary to identify the application of an incoming call. Consider a

ContentProvider that provides a way for third-party applications to add (insert) data. However, you want

applications to be capable of accessing only the records they’ve’ created, so you need some way of securely

identifying the remote process in each call to query(), insert(), update(), and delete().

The following method shows how to use the Binder to retrieve the UID (User ID) for the calling applications.

From there, you can retrieve the package names (that is, applications) that have this UID.

private String getPackageNameForCaller(Context context) {

int callingUid = Binder.getCallingUid();

PackageManager packageManager = context.getPackageManager();

String[] packages = packageManager.getPackagesForUid(callingUid);

if(packages != null && packages.length > 0) {

return packages[0]; // Return the first matching package...

}

return null;

}

Chapter 12: Secure Android Applications 235

Because you can have the same UID for multiple applications, signed with the same key, by using the

android:sharedUserId attribute in the manifest, you may receive multiple package names in this lookup.

So, in the public documentation of your provider, you need to state that applications using your API can’t share

the UID with another application.

After you get the package name for the application, you can use it when querying the database. For each insert,

make sure that you populate a column with the value of the calling application’s package name. In the query,

update and delete, you modify the selection and selection argument so that the package name is prepended,

as shown here:

private String prependSelection(String selection) {

return Columns.PACKAGE_NAME + “ = ? AND (“ + selection + “)”;

}

private String[] prependSelectionArgs(String[] selectionArgs) {

String[] newSelArgs = new String[selectionArgs.length + 1];

System.arraycopy(selectionArgs, 0, newSelArgs, 1, selectionArgs.

length);

newSelArgs[0] = getPackageNameForCaller(getContext());

return newSelArgs;

}

In this way, you can provide a single ContentProvider that other applications can work with while

simultaneously providing protection so that data cannot leak. This approach is suitable for applications

that aggregate data from different sources and in which you enable the use of plug-ins from third-party

developers.

Client-Side Data Encryption

Imagine an app that syncs user-generated data to a cloud service. However, in this case, the data stored on the

cloud service must be encrypted so that no one but the user can decrypt the data. This scenario means that

the encryption and decryption must happen on the client-side. A couple of examples today are LastPass, which

manages all your passwords, and Wuala, a cloud-storage service where all data is encrypted on the client-side,

which effectively protects the user’s data even if the cloud service is compromised.

The challenge with such an application is that you wouldn’t want to store anything on the device that isn’t

encrypted. For instance, if you have a notebook application for storing secure notes, you can’t store decrypted

notes in the application’s data directory, because if the user were to lose the device, it wouldn’t take much effort

for a hacker to gain access to the file system and read the unencrypted files. To solve this problem, only keep

unencrypted data in memory. Although it’s still possible to read the state of a memory, doing so is significantly

harder than reading the file system.

In this section, I explain how to encrypt and decrypt data using a password and how to create a memory-based

database that you can use in your application like any other ContentProvider.

236 Part III: Pushing the Limits

Android Crypto API

The API for encrypting and decrypting data in Android is based on the API found in the javax.crypto

package in Java SE. The actual implementation is based on the open-source Bouncy Castle Crypto APIs, a clean

room implementation of the JCE 1.2.1 specification. As a result, you can use most Java libraries that use the

javax.crypto APIs from Java SE when writing Android applications.

Generating a Key

When using encryption and decryption functions, you need to generate a key that is secure, unique, and

capable of being re-created based on the user’s input (that is, a password or other secure method).

The following code shows how to generate a SecretKey for the AES algorithm, which is secure enough

for most situations. The salt is the input to the generation of the key that you need to keep track of. In

cryptography, a salt is a random piece of data used as input for the one-way function in the encryption

algorithms.

public static SecretKey generateKey(char[] password, byte[] salt)

throws Exception {

int iterations = 1000;

int outputKeyLength = 256;

SecretKeyFactory secretKeyFactory

= SecretKeyFactory.getInstance(“PBKDF2WithHmacS

HA1”);

KeySpec keySpec = new PBEKeySpec(password, salt,

iterations, outputKeyLength);

byte[] keyBytes = secretKeyFactory.generateSecret(keySpec).

getEncoded();

return new SecretKeySpec(keyBytes, “AES”);

}

Encrypting Data

To encrypt data, you must generate the salt and an initialization vector as an input to the Cipher used for

encrypting.

The following method generates a salt of 8 bytes using the SecureRandom class. Note: You never manually

seed the SecureRandom class because the system takes care of that for you. You create an initialization

vector, initiate the Cipher, and encrypt your plain text to a byte array. When you have your cipher data, you

generate a normal String from those bytes by using the Base64 utility class. You append the initialization

vector and salt in the same way, separated by a non-base64 character.

public static String encryptClearText(char[] password, String plainText)

throws Exception {

SecureRandom secureRandom = new SecureRandom();

int saltLength = 8;

byte[] salt = new byte[saltLength];

secureRandom.nextBytes(salt);

SecretKey secretKey = generateKey(password, salt);

Chapter 12: Secure Android Applications 237

Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);

byte[] initVector = new byte[cipher.getBlockSize()];

secureRandom.nextBytes(initVector);

IvParameterSpec ivParameterSpec = new IvParameterSpec(initVector);

cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);

byte[] cipherData = cipher.doFinal(plainText.getBytes(“UTF-8”));

return Base64.encodeToString(cipherData,

Base64.NO_WRAP | Base64.NO_PADDING)

+ “]” + Base64.encodeToString(initVector,

Base64.NO_WRAP | Base64.NO_PADDING)

+ “]” + Base64.encodeToString(salt,

Base64.NO_WRAP | Base64.NO_PADDING);

}

The returned String can be securely sent over the network or stored on the external storage. An application

using this approach for securing data should require the user to input a password that is secure enough. How

to choose such a password is beyond the scope of this book, but I recommend using a regular expression

for checking the complexity of a password. A good source for various regular expressions that you can use is

available at http://regexlib.com/.

Decrypting Data

Decrypting data works very much like encryption. You take the String generated by a previous call to

encryptClearText() and split it on the separator that you choose.

In the following method, you see how the Cipher, the initialization vector, and the SecretKey are re-created

from the input string. As long as the passwords match, you’ll be able to decode the encoded data.

public static String decryptData(char[] password, String encodedData)

throws Exception {

String[] parts = encodedData.split(“]”);

byte[] cipherData = Base64.decode(parts[0], Base64.DEFAULT);

byte[] initVector = Base64.decode(parts[1], Base64.DEFAULT);

byte[] salt = Base64.decode(parts[2], Base64.DEFAULT);

Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);

IvParameterSpec ivParams = new IvParameterSpec(initVector);

SecretKey secretKey = generateKey(password, salt);

cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams);

return new String(cipher.doFinal(cipherData), “UTF-8”);

}

This method is useful for fairly small sets of data. If you need to encrypt and decrypt larger files, check the

CipherInputStream and CipherOutputStream as they support streaming data that is too large to fit in

memory all at once.

Working with Encrypted Data

After you encode the data, you can safely write it to a file or send it over the network without the risk of leaking the

user’s private information. The only one who can decrypt the data is the person who knows the password, which

238 Part III: Pushing the Limits

means that you could basically store the encrypted data on a public server without anyone, including yourself,

being able to read its content. This is a great way to gain your users’ trust—as long as you can explain it in terms

that nontechnical people can grasp. A couple of services already use this capability as their main selling point.

Considering the example of a secure notebook application, you could use the approach just described for

encrypting and decrypting the notes. When the user makes an update to his notes, you can upload the file to

his Google Drive or similar service, and the notes will then be accessible from his other devices. (See Chapter 19

for an example of storing data in Google Drive.)

Consider the following Java object for representing notes in a notebook application:

public class NoteData {

private UUID mID;

private String mTitle;

private String mContent;

private String mCategory;

private Date mLastChange;

public NoteData() {

mID = UUID.randomUUID();

}

// Getters and setters omitted for brevity...

}

You can use Google Gson, as shown in Chapter 9, to serialize and deserialize objects of this class to the JSON

format. The JSON data can be represented as a normal String object, which you then can use for encrypting

your notes. Decrypting and deserializing works the same way.

The following example shows a simplified method for use in a notebook application that securely encrypts and

decrypts the users’ notes based on their passwords. The encrypted data can then be transferred online, which I

cover in more detail in Chapter 17.

public String encryptNoteDataCollection(Collection<NoteData> notes,

char[] password) {

StringWriter writer = new StringWriter();

JsonWriter jsonWriter = new JsonWriter(writer);

Gson gson = new Gson();

Type type = new TypeToken<Collection<NoteData>>(){}.getType();

gson.toJson(notes, type, jsonWriter);

String clearText = writer.toString();

try {

return encryptClearText(password, clearText);

} catch (Exception e) {

// Ignore for brevity

return null;

}

}

Chapter 12: Secure Android Applications 239

public static Collection<NoteData> decryptAndDecode(char[] password,

String encryptedData)

{

try {

String jsonData = decryptData(password, encryptedData);

Gson gson = new Gson();

Type type = new TypeToken<Collection<NoteData>>(){}.getType();

JsonReader jsonReader = new JsonReader(new

StringReader(jsonData));

return gson.fromJson(jsonReader, type);

} catch (Exception e) {

// Ignore for brevity...

return null;

}

}

If you prefer to work toward a ContentProvider, you can create an SQLite database in memory using the

following code:

mDatabase = SQLiteDatabase.create(null);

This database instance will be destroyed as soon as the database object is closed. This means that your

application should store the encrypted content to a local file for every change (insert, update or delete) to the

database and must repopulate the database content each time it is opened.

Android Key Chain Management

A feature that was long missing from Android was central secure credential storage and a way to control the

system’s trusted Certificate Authorities, which are crucial in order to build secure applications for enterprise and

other sensitive services.

In this section I’m assuming that you’re familiar with the basics of certificates, CA and PKI. If not, refer to the

“Further Resources” section at the end of this chapter. Although permissions can protect the data from other

applications and encryption can prevent access of the data even when the file is copied from the device, your

application still needs a secure method for verifying that the remote host it communicates with is indeed the

one it claims to be. Because you cannot assume that the user will always be on a safe, well-known, and secure

network, you need to verify any host that you communicate with using its certificates.

Explaining the details of certificate authority, key chains, and how they make the Internet secure is beyond the

scope of this book. However, I do show how you can use the key chain API that was made available in Android

4.0/ICS to store a new key pair, as well as how to verify the source of the data and sign it so that a recipient can

verify its authenticity.

First, you need to generate a new certificate that you can use for testing your application, which you do using

the same keytool shown earlier in this chapter for creating the signature for your application.

240 Part III: Pushing the Limits

The following example shows how to generate a simple certificate using the keytool command. The result is

written to a file named MyKeyStore.pfx. In a real-world situation, the IT department of a company usually

generates these certificates for each user and distributes them securely. Next, the enterprise application imports

this certificate and stores it in the central credential storage.

$ keytool -genkeypair -alias MyCertificate -keystore MyKeyStore.pfx

-storepass thepassword -validity 10000 -keyalg RSA -keysize 2048

-storetype pkcs12

What is your first and last name?

[Unknown]: Erik Hellman

What is the name of your organizational unit?

[Unknown]: Development

What is the name of your organization?

[Unknown]: Hellsoft

What is the name of your City or Locality?

[Unknown]: Malmoe

What is the name of your State or Province?

[Unknown]: Skaane

What is the two-letter country code for this unit?

[Unknown]: SE

Is CN=Erik Hellman, OU=Development, O=Hellsoft, L=Malmoe, ST=Skaane,

C=SE correct?

[no]: yes

In the following code, you see how to install a certificate from a file (represented as a byte array).

public class SigningActivity extends Activity {

private static final int INSTALL_CERT_CODE = 1001;

private static final String CERT_FILENAME = “MyKeyStore.pfx”;

private static final String CERTIFICATE_NAME = “MyCertificate”;

@Override

protected void onActivityResult(int requestCode, int resultCode,

Intent data) {

if(requestCode == INSTALL_CERT_CODE) {

if(resultCode == RESULT_OK) {

// Certificate successfully installed

} else {

// User cancelled certificate installation

}

}

}

// click-listener for installing certificate

public void doInstallCertificate(View view) {

byte[] certData = readFile(CERT_FILENAME);

Intent installCert = KeyChain.createInstallIntent();

installCert.putExtra(KeyChain.EXTRA_NAME, CERTIFICATE_NAME)

installCert.putExtra(KeyChain.EXTRA_PKCS12, certificateData);

startActivityForResult(installCert, INSTALL_CERT_CODE);

}

...

}

Chapter 12: Secure Android Applications 241

The code will activate a dialog box where a user can enter the password for the chosen keystore. Next, another

dialog box, as shown in Figure 12-1, is activated in which the user can choose the alias for the certificate.

Figure 12-1 Dialog box asking for the name of the

certificate that is being installed by the application

When your application wants to use a certificate, it must first ask users which one they want to use, which is

done by calling KeyChain.choosePrivateKeyAlias(), as shown in the following code.

public void doSignNoteData(View view) {

KeyChain.choosePrivateKeyAlias(this, new KeyChainAliasCallback() {

@Override

public void alias(String alias) {

EditText editText = (EditText) findViewById(R.id.input_text);

String textToSign = editText.getText().toString();

new MySigningTask().execute(textToSign, alias);

}

}, null, null, null, -1, null);

}

242 Part III: Pushing the Limits

This activates a system dialog box, as shown in Figure 12-2.

Figure 12-2 System dialog box that allows the user to

pick a certificate from the trusted storage

The preceding callback will receive the alias for the chosen certificate, and you can now continue with retrieving

the certificate and the signing process. Note: The callback is executed on a Binder thread and not on the main

thread. Because the signing process could potentially block the thread it’s running on, you use an AsyncTask

for the actual signing.

The following method shows how to retrieve the private key of the certificate pair and use it to create a

signature of the String you want to sign. You use the same approach as in the earlier encryption example to

build two Base64 encoded Strings with a separator.

Chapter 12: Secure Android Applications 243

public String createSignedNote(String textToSign, String alias) {

try {

byte[] textData = textToSign.getBytes(“UTF-8”);

PrivateKey privateKey

= KeyChain.getPrivateKey(getApplicationContext(), alias);

Signature signature

= Signature.getInstance(“SHA1withRSA”);

signature.initSign(privateKey);

signature.update(textData);

byte[] signed = signature.sign();

return Base64.encodeToString(textData,

Base64.NO_WRAP | Base64.NO_PADDING)

+ “]” + Base64.encodeToString(signed,

Base64.NO_WRAP | Base64.NO_PADDING);

} catch (Exception e) {

Log.e(TAG, “Error signing data.”, e);

}

return null;

}

To verify the signature of signed data, your application must first retrieve the PublicKey from the certificate.

In the following code, it’s assumed that the chain contains only one entry, but in real-life situations, you will

probably have at least two entries (one for the organization and one for the user).

private boolean verifySignature(String dataAndSignature, String alias) {

try {

String[] parts = dataAndSignature.split(“]”);

byte[] decodedText = Base64.decode(parts[0], Base64.DEFAULT);

byte[] signed = Base64.decode(parts[1], Base64.DEFAULT);

X509Certificate[] chain = KeyChain.getCertificateChain(this,

alias);

PublicKey publicKey = chain[0].getPublicKey();

Signature signature = Signature.getInstance(“SHA1withRSA”);

signature.initVerify(publicKey);

signature.update(decodedText);

return signature.verify(signed);

} catch (Exception e) {

Log.e(TAG, “Error verifying signature.”, e);

}

return false;

}

You can also use the public and private keys to encrypt data. If two parties (for instance, the company server

and an application on an employee’s smartphone) need to communicate securely, you use the receiving party’s

public key to encrypt data, which then can be decrypted only by using the private key.

244 Part III: Pushing the Limits

Most webservers today support the secure HTTP protocol called HTTPS. This protocol has built-in support to

set up the connection between the client and the server using this approach, so all you do is provide the

client-side certificate. Refer to the documentation for the HttpsURLConnection for more information on

this topic.

Device Management API

Although encrypting and verifying data are important parts of security, they’re often not enough for

most organizations. Within the domain of enterprise applications is a need to set certain restrictions on a

device before allowing it to communicate with a company’s intranet. This situation is resolved via a Device

Management API available in Android that provides a number of functions to handle enterprise requirements.

This API provides a number of policies that an application can apply to a device to ensure that it is secure. There

are policies for limiting and resetting the password, for locking and wiping a device, and for encrypting the

storage. Check the documentation for the Device Administration API (see http://developer.android.

com/guide/topics/admin/device-admin.html) for a full list of policies.

The first thing you need to enable this in your application is an XML file in the resources that defines which

device policies your application needs to apply.

The following example shows a simple device policy XML that you store in the XML resources. It enables your

application to register when a user logs in (unlocks the device), to limit and reset the password, to force the

device to lock down, and to encrypt the storage when required.

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

<device-admin xmlns:android=”http://schemas.android.com/apk/res/android”>

<uses-policies>

<watch-login/>

<force-lock/>

<limit-password/>

<encrypted-storage/>

<reset-password/>

</uses-policies>

</device-admin>

Next, you need to define a BroadcastReceiver that will be called when the device administration is

enabled for your application.

<receiver

android:name=”.MyDeviceAdminReceiver”

android:label=”@string/device_admin_label”

android:description=”@string/device_admin_description”

android:permission=”android.permission.BIND_DEVICE_ADMIN”>

Chapter 12: Secure Android Applications 245

<meta-data

android:name=”android.app.device_admin”

android:resource=”@xml/device_admin”/>

<intent-filter>

<action android:name=”android.app.action.DEVICE_ADMIN_ENABLED”/>

</intent-filter>

</receiver>

Now you need to implement a custom BroadcastReceiver (extending DeviceAdminReceiver) where

you configure the manifest entry, as in the preceding example. First, you specify that the permission for the

receiver and add a meta-data tag that specifies the XML also shown in the preceding example.

public class MyDeviceAdminReceiver extends DeviceAdminReceiver {

@Override

public void onEnabled(Context context, Intent intent) {

super.onEnabled(context, intent);

PackageManager packageManager = context.getPackageManager();

ComponentName deviceAdminService

= new ComponentName(context, MyDeviceAdminService.class);

packageManager.setComponentEnabledSetting(deviceAdminService,

PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);

context.startService(new Intent(context,

MyDeviceAdminService.class));

}

@Override

public void onDisabled(Context context, Intent intent) {

super.onDisabled(context, intent);

context.stopService(new Intent(context,

MyDeviceAdminService.class));

PackageManager packageManager = context.getPackageManager();

ComponentName deviceAdminService

= new ComponentName(context, MyDeviceAdminService.class);

packageManager.setComponentEnabledSetting(deviceAdminService,

PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);

}

}

The receiver is notified when device administration for your application is enabled or disabled. There are also

callbacks for certain device policy events, such as password expiration. In the preceding example, you see how

to enable and disable a service in your application, depending on the state of device administration for this

application.

To enable device administration for your application, you need to start a system Activity that asks for the

user’s permission (see Figure 12-3).

246 Part III: Pushing the Limits

Figure 12-3 The system Activity that asks for permission

to enable device administration rights for an application .

The first paragraph under the title is the same as the text in

the EXTRA_ADD_EXPLANATION parameter .

The following example shows how to request the user to enable device administration for your application.

Note: You send the ComponentName for the receiver and you can add an extra explanation that is displayed

to the user. This is recommended for applications using the Device Administration API; otherwise, what the

prompted dialog box means may be confusing.

public static void enableDeviceAdmin(Context context) {

Intent enableDeviceAdmin

= new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);

ComponentName deviceAdminReceiver

= new ComponentName(context, MyDeviceAdminReceiver.class);

enableDeviceAdmin.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,

deviceAdminReceiver);

enableDeviceAdmin.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,

context.getString(R.string.device_admin_explanation));

context.startActivity(enableDeviceAdmin);

}

Chapter 12: Secure Android Applications 247

After the user accepts the device administration request, your application can start applying its policies and

control the device to a certain degree.

The following method is a simple example in which you reset the password and lock the device immediately.

private void changePasswordAndLockDevice(String password) {

mDevicePolicyManager = (DevicePolicyManager)

getSystemService(DEVICE_POLICY_SERVICE);

if(mDevicePolicyManager.resetPassword(password, 0)) {

mDevicePolicyManager.lockNow();

}

}

Doing so can be useful for a remote security feature. By sending a verified message (as shown earlier in this

chapter) to the device, the user or someone with the right authorization can lock the device if it’s stolen or

lost. The message could come from an SMS, which I describe in Chapter 15, or from a Google Cloud Messaging

(GCM) service, which I cover in Chapter 19.

Summary

In this chapter, I described how you can use the Android permissions to restrict what and how other

applications can access your application. I also showed how to identify the calling application in order to isolate

data you receive from multiple applications. This feature is especially powerful when your application is working

with aggregated data from several sources.

I then demonstrated how to encrypt and decrypt data using the crypto APIs in Android, which can often

provide enough security when the user’s data for your application is stored externally or on a remote server.

If your application will involve external parties, such as a remote server, simply performing encryption of

the data may not be enough. You also need to be able to verify that data your application receives is coming

from a verified source. The new Key Chain API in Android 4.0 and later provides secure and trusted storage for

application-specific certificates.

Finally, I showed how to use the Device Administration API in order to programmatically control the security

features. As a result, you can write applications that can lock a device or reset the password. The Device

Administration API combined with the other security features described in this chapter give you a set of

powerful tools for creating applications that fulfill the security requirements of most enterprises.

Further Resources

Books

Adams, Carlisle, and Steve Lloyd. Understanding PKI: Concepts, Standards, and Deployment Considerations,

2nd edition. Addison-Wesley, 2002.

248 Part III: Pushing the Limits

Documentation

Documentation on the Device Administration APIs: http://developer.android.com/guide/

topics/admin/device-admin.html

Websites

Android Developers Blog. “Using Cryptography to Store Credentials Safely” : http://android-

developers.blogspot.se/2013/02/using-cryptography-to-store-credentials.

html

Android Developers Blog. “Unifying Key Store Access in ICS”: http://android-developers.

blogspot.se/2012/03/unifying-key-store-access-in-ics.html

Android Explorations. “Using the ICS KeyChain API”: http://nelenkov.blogspot.

se/2011/11/using-ics-keychain-api.html

“Sample CodeC for Using the ICS KeyChain API and Related Articles”: https://github.com/

nelenkov/keystore-test#readme

Chapter 13

Maps, Location, and

Activity APIs

In May 2013, at the annual Google IO conference, Google presented something slightly different for Android

developers. Google used to focus on new platform features and Android versions at this event, but at this

conference, they presented a new service API for location-based apps that works on older Android versions as

well. This new API allows developers to use the new and advanced Location and Activity API for Android, which

is part of the Google Play Services, on most Android devices deployed into the market today.

In this chapter, I cover the features of the new Location API and how you can integrate it into your applications.

Fused Location Manager

Google managed to provide a new unified Location API that works for older Android versions by fusing input

from all the sensors and other inputs into one library. Figures 13-1 and 13-2 show the differences between

the use of the old location-based APIs and the new fused Location Manager. The new library checks for the

existence of all the possible inputs and sensors and manages all the specifics for each Android version.

Apps

Platform

Location Manager

SensorManager

GpsLocationProvider

NetworkLocationProvider

Sensors

GPS

WiFi

Cellular

Hardware

Figure 13-1 The old location-based APIs in the Android platform . Using these APIs requires a great deal of effort

from developers who also have to consider the different Android platforms .

250 Part III: Pushing the Limits

Location

GeofencingActivity Recognition

Apps

Fused Location Manager

Platform

Platform Components

Hardware

Sensors

GPS

WiFi

Cellular

Figure 13-2 The fused Location Manager combines sensors, GPS, Wi-Fi, and cellular into a single API for

location-based applications .

There are several advantages to using the new approach, but the most obvious one is that Google can provide

updates and fixes for bugs that otherwise would have to be rolled out by each handset manufacturer. Also, it

allows you to build advanced maps and location-enabled applications on older devices, as long as they have

Google Play Services installed. For details on how to verify that Google Play Services is installed and working

correctly, refer to Chapter 19.

Google Maps v2 Integration

To integrate the Google Maps API on Android, you need to create a new project in the Google API Console at

https://code.google.com/apis/console. You should create a new project here for each Android

application that will use the Google Play Services APIs (see Figure 13-3). (I go into more detail about how to

work with the API console and Google Play Services in Chapter 19.) The first thing you need to do after you

create the project is to enable the Google Maps Android API v2 under Services and create a new Android key

under API Access.

You generate the key by entering the SHA1 value for the key used for signing your APK, as shown in Figure

13-4. Usually you will have two keys. One is used during development (debug key) that is usually shared among

multiple applications and considered insecure (usually found under $HOME/.android/debug.keystore).

The second one is your release key, which you should always keep secure. I recommend that you create two API

keys for each application, one for the development version and one for the release version.

You get the SHA1 by using the keytool in the terminal, as shown here with the relevant text shown in bold:

$ keytool -list -v -keystore ~/.android/debug.keystore -alias

androiddebugkey

-storepass android -keypass android

Chapter 13: Maps, Location, and Activity APIs 251

Alias name: androiddebugkey

Creation date: May 15, 2013

Entry type: PrivateKeyEntry

Certificate chain length: 1

Certificate[1]:

Owner: CN=Android Debug, O=Android, C=US

Issuer: CN=Android Debug, O=Android, C=US

Serial number: 5193e7a1

Valid from: Wed May 15 21:53:05 CEST 2013 until: Fri May 08 21:53:05 CEST

2043

Certificate fingerprints:

MD5: 09:AF:DA:16:0B:94:71:67:61:5B:C3:7D:9E:12:53:A0

SHA1: A0:D6:9F:F7:6E:F0:AD:B3:70:0B:74:91:13:E8:57:C3:C9:80:6D:EA

Signature algorithm name: SHA1withRSA

Version: 3

Figure 13-3 The Services section in Google APIs Console where you enable Google Maps Android API v2

252 Part III: Pushing the Limits

Figure 13-4 Creating a new Android key for the Google Maps API access

Copy the text and paste it into the dialog for configuring a new Android key on the API Console (as shown in

Figure 13-4). After the SHA1 value, you append a semicolon followed by the package name for your application.

When you click Create, the key will be generated and you can copy it into your manifest. This process is covered

in more detail in Chapter 19.

You need to add the API key as a metadata element in the manifest, as shown in the following example, with the

API key shown in bold.

<application

android:allowBackup=”true”

android:icon=”@drawable/ic_launcher”

android:label=”@string/app_name”

android:theme=”@style/AppTheme” >

...

<meta-data

android:name=”com.google.android.maps.v2.API_KEY”

android:value=” AIzaSyCXNWDY7nx-_0vnwMW-6mXryYD5BTblyVM”/>

</application>

Chapter 13: Maps, Location, and Activity APIs 253

Working with Google Maps

Working with the new (version 2) Google Maps API is simple and straightforward. In the following example, you

see how Java code adds Google Maps to your application:

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mMapFragment = (MapFragment) getFragmentManager().

findFragmentById(R.id.map);

GoogleMap map = mMapFragment.getMap();

map.setTrafficEnabled(true);

CameraPosition cameraPosition = new CameraPosition.Builder().

target(MY_HOME).

zoom(17).

bearing(90).

tilt(30).build();

map.animateCamera(CameraUpdateFactory.

newCameraPosition(cameraPosition));

map.setMapType(GoogleMap.MAP_TYPE_NORMAL);

map.setMyLocationEnabled(true);

map.setIndoorEnabled(true);

}

The GoogleMap object is the central piece when working with the maps. In this example, you also move the

camera for the map to MY_HOME (a predefined LatLng constant) and tell the GoogleMap object to enable

“My location” on the map as well as indoor positioning and maps.

The following XML layout is an example of how you can define the initial map in an XML layout:

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

xmlns:map=”http://schemas.android.com/apk/res-auto”

android:id=”@+id/map”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:name=”com.google.android.gms.maps.MapFragment”

map:cameraBearing=”112.5”

map:cameraTargetLat=”55.59612590”

map:cameraTargetLng=”12.98140870”

map:cameraTilt=”30”

map:cameraZoom=”13”

map:mapType=”normal”

map:uiCompass=”true”

map:uiRotateGestures=”true”

map:uiScrollGestures=”true”

map:uiTiltGestures=”true”

map:uiZoomControls=”true”

map:uiZoomGestures=”true”/>

You’ll find this using the XML layout useful if your application needs to display a default location on the maps

because the values here can be localized as any other resource can.

254 Part III: Pushing the Limits

To interact with a GoogleMap object, you can register a number of listeners for certain events.

Markers on Maps

A Marker is the simplest thing you can add to a GoogleMap object (see Figure 13-5). You simply need a

LatLng object with the coordinates of its position.

The following code shows how to add a new Marker to a GoogleMap object.

mMap.addMarker(new MarkerOptions().

position(latLng).

title(mMarkerTitle).

snippet(mMarkerSnippet).

icon(mMarkerDrawable));

The only required attribute for a marker is the position. It is also possible to define a draggable Marker

that the user can move around. You do so by calling draggable(true) on the MarkerOptions

object and ensuring that you’ve set the Marker drag listener on the map with GoogleMap.

setOnMarkerClickListener().

Figure 13-5 Placing markers on a map . Note the marker

with the custom icon .

Chapter 13: Maps, Location, and Activity APIs 255

Drawing Circles

Drawing circles on a GoogleMap is done in the same way as placing Markers.

Following is an example of how to use the onMapClick() callback on a GoogleMap to place a red circle with

the radius of 40 meters on the map where the user taps (see the result in Figure 13-6).

public void onMapClick(LatLng latLng) {

mMap.addCircle(new CircleOptions().

radius(40).

center(latLng).

fillColor(Color.RED).

strokeColor(Color.BLACK).

strokeWidth(6));

}

Figure 13-6 A Google map with a number of circles

256 Part III: Pushing the Limits

Drawing Polygons

Polygons are complex geometrical objects formed by a number of points. You can use polygons to cover an

area of a map (as shown in Figure 13-7).

Figure 13-7 A Google map with a number of different

polygons drawn on top

The following is a simple example where a quick tap on the maps adds a new point for the polygon and where a

long tap completes the polygon:

@Override

public void onMapClick(LatLng latLng) {

if (mNewPolygon == null) {

mNewPolygon = new PolygonOptions().add(latLng);

} else {

mNewPolygon.add(latLng);

}

}

Chapter 13: Maps, Location, and Activity APIs 257

@Override

public void onMapLongClick(LatLng latLng) {

Log.d(TAG, “Closing polygon at “ + latLng.toString());

mNewPolygon.add(latLng).

fillColor(getColor()).

strokeColor(Color.BLACK).

strokeWidth(6);

mMap.addPolygon(mNewPolygon);

mNewPolygon = null;

mDrawState = null;

}

Useful Location API Utilities

A common operation in location-based applications is to find out whether a certain location (latitude and

longitude) is close to another location.

The following method takes two LatLng parameters and returns the approximate distance between them in

meters.

public float distanceBetween(LatLng latLng1, LatLng latLng2) {

float[] results = new float[3];

Location.distanceBetween(latLng1.latitude,

latLng1.longitude,

latLng2.latitude,

latLng2.longitude,

results);

return results[0];

}

Another operation common for location-based applications is to determine if you are inside a certain area or

not. The most common case is to define an area as a rectangle formed by two coordinates. However, sometimes

you have a number of coordinates around which you first want to form the smallest bounding rectangle.

public LatLngBounds getBoundsForPoints(List<LatLng> coordinates) {

LatLngBounds.Builder builder = LatLngBounds.builder();

for (LatLng coordinate : coordinates) {

builder.include(coordinate);

}

return builder.build();

}

public boolean isWithinBound(LatLng latLng, LatLngBounds bounds) {

return bounds.contains(latLng);

}

The two preceding methods demonstrate how you can use the class LatLngBounds to create a rectangle

around a number of points. You can then use the LatLngBounds.contains() method to determine

whether another LatLng object is contained within that rectangle.

258 Part III: Pushing the Limits

Geocoding

At this point you should be familiar with the GoogleMaps class and how to work with locations in Android

using the new Location API. However, locations are defined as latitude and longitude, which is rarely useful for

users of your application. Most users are more familiar with streets, city names, and names of places. To find a

useful name for a certain latitude and longitude, Android provides the Geocoder class:

public static Address getAddressForLocation(Context context, Location

location) {

try {

Geocoder geocoder = new Geocoder(context);

List<Address> addresses = geocoder.

getFromLocation(location.getLatitude(),

location.getLongitude(),

1);

if(addresses != null && addresses.size() > 0) {

return addresses.get(0);

} else {

return null;

}

} catch (IOException e) {

return null;

}

}

public String getStreetNameForAddress(Address address) {

String streetName = address.getAddressLine(0);

if(streetName == null) {

streetName = address.getThoroughfare();

}

return streetName;

}

The first method shows how to retrieve the first matching Address for a certain Location. There can be

multiple matches, so one alternative could be to provide the user with a list from which they can select the

address that matches.

The Address object is a bit vague on what the different fields represent because they can vary depending on

your country and location.

The second method usually gives you the current street name. However, always assume that the fields returned

from an Address object can be null.

Using the LocationClient

The new fused Location Manager is accessed through the LocationClient class. This class is used to listen

for location updates and to perform geofencing operations. I recommend that you create this instance in

onCreate() and release the API in onDestroy().

Chapter 13: Maps, Location, and Activity APIs 259

Initialize the Location API as shown in the following code:

@Override

protected void onCreate() {

super.onCreate();

setContentView(R.layout.activity_main);

MapFragment mapFragment = (MapFragment) getFragmentManager().

findFragmentById(R.id.map);

mMap = mapFragment.getMap();

mLocationCallbacks = new MyLocationCallbacks();

mLocationClient = new LocationClient(this,

mLocationCallbacks, mLocationCallbacks);

mLocationClient.connect();

}

@Override

protected void onDestroy() {

super.onDestroy();

if (mLocationClient != null && mLocationClient.isConnected()) {

mLocationClient.disconnect();

mLocationClient = null;

}

}

This is an asynchronous API, so you need to register the relevant callbacks as well. Remember to disconnect the

LocationClient in the correct callback as well:

private class MyLocationCallbacks

implements GooglePlayServicesClient.ConnectionCallbacks,

GooglePlayServicesClient.OnConnectionFailedListener,

LocationListener {

@Override

public void onConnected(Bundle bundle) {

LocationRequest locationRequest = new LocationRequest();

locationRequest.setSmallestDisplacement(TWENTYFIVE_METERS);

locationRequest.setExpirationDuration(FIVE_MIUTES);

mLocationClient.requestLocationUpdates(locationRequest, this);

}

@Override

public void onDisconnected() {

}

@Override

public void onConnectionFailed(ConnectionResult connectionResult) {

// TODO Error handling...

}

260 Part III: Pushing the Limits

@Override

public void onLocationChanged(Location location) {

LatLng latLng = new LatLng(location.getLatitude(),

location.getLongitude());

CameraPosition cameraPosition = new CameraPosition.Builder()

.target(latLng)

.zoom(17)

.bearing(90)

.tilt(30)

.build();

mMap.animateCamera(CameraUpdateFactory.

newCameraPosition(cameraPosition));

}

}

The callback just shown requests location updates every 25 meters for the next 5 minutes. After you receive

the call to onConnected(), you cannot perform any operations on the LocationClient. Also, it’s good

practice to set the expiration duration for your LocationRequest to a low number and renew the request if

needed. Doing so reduces the risk of unnecessary battery consumption.

When a callback to onLocationChanged() is triggered, you update the camera position for the

GoogleMap object. In this example, you perform an animation of the camera movement to the new location,

which is an efficient way of displaying the map as soon as possible and moving the camera as soon as the

asynchronous location request is complete.

Geofencing

One of the best features of the new Location API for Android is geofencing. Basically, geofencing allows you

to define a circle that represents a virtual fence around a specific location (longitude and latitude). Whenever

the device comes within the area of that circle, it will notify your application. A Geofence is defined by the

latitude, longitude, and a radius in meters, which is then registered using the new Location API with expiration

time, transition type (“enter” or “exit”) and an ID.

The following is the callback method you can set for long taps on a GoogleMap object:

public void onMapLongClick(LatLng latLng) {

Geofence.Builder builder = new Geofence.Builder();

builder.setCircularRegion(latLng.latitude,

latLng.longitude,

TWENTYFIVE_METERS);

builder.setExpirationDuration(ONE_WEEK);

// For now, use the lat/long as ID.

String geofenceRequestId = latLng.latitude + “,”

+ latLng.longitude;

builder.setRequestId(geofenceRequestId);

// Only interested in entering the geofence for now...

builder.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER);

List<Geofence> geofences = new ArrayList<Geofence>();

Chapter 13: Maps, Location, and Activity APIs 261

geofences.add(builder.build());

Intent intent = new Intent(MyIntentService.

ACTION_NOTIFY_ENTERED_GEOFENCE);

PendingIntent pendingIntent = PendingIntent.getService(this,

1001,

intent,

0);

mLocationClient.addGeofences(geofences, pendingIntent,

new LocationClient.OnAddGeofencesResultListener() {

@Override

public void onAddGeofencesResult(int status,

String[] strings) {

if (status == LocationStatusCodes.SUCCESS) {

double latitude = Double.parseDouble(strings[0])

double longitude = Double.parseDouble(strings[0])

LatLng latLng = new LatLng(latitude, longitude);

Circle circle = mMap.addCircle(new

CircleOptions().

fillColor(Color.GREEN).

strokeWidth(5).

strokeColor(Color.BLACK).

center(latLng).

visible(true).

radius(TWENTYFIVE_METERS));

mGeoReminders.add(circle);

} else {

// TODO: Error handling...

}

}

});

}

Here, you add a new Geofence at the position where the user performs a long tap. As with the other

operations on the LocationClient, this is an asynchronous call. After the callback is triggered, you check

the result and if the addition of the new Geofence was a success, you create a circle on the GoogleMap

object indicating where the Geofence is registered.

This example shows how to combine drawing objects on top of a GoogleMap with the Geofence API. The

PendingIntent in the preceding example contains information about the Geofences that was triggered.

The following IntentService will, in this case, show a notification to the user that a Geofence has been

triggered:

public class MyIntentService extends IntentService {

public static final String TAG = “MyIntentService”;

public static final String ACTION_NOTIFY_ENTERED_GEOFENCE =

“com.aptl.locationandmapsdemo.NOTIFY_ENTER_

GEOFENCE”;

private int mNextNotificationId = 1;

262 Part III: Pushing the Limits

public MyIntentService() {

super(TAG);

}

@Override

protected void onHandleIntent(Intent intent) {

String action = intent.getAction();

if (LocationClient.hasError(intent)) {

// TODO: Error handling...

} else {

List<Geofence> geofences =

LocationClient.

getTriggeringGeofences(intent);

for (Geofence geofence : geofences) {

showNotification(geofence);

}

}

}

private void showNotificat(Geofence geofence) {

// TODO: Show notification to user

}

}

Activity Recognition

The final part of the new Location API is Activity Recognition. This feature is controlled by the class

ActivityRecognitionClient that works much as the LocationClient does.

In the following example, you can see a simple Activity that initiates and connects to the

ActivityRecognitionClient:

public class ActivityRecognition extends Activity implements

GooglePlayServicesClient.ConnectionCallbacks,

GooglePlayServicesClient.OnConnectionFailedListener {

private static final long THIRTY_SECONDS = 1000 * 30;

private static final long FIVE_SECONDS = 1000 * 5;

private boolean mActivityRecognitionReady = false;

private ActivityRecognitionClient mActivityRecognitionClient;

private PendingIntent mPendingIntent;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mActivityRecognitionReady = false;

setContentView(R.layout.activity_recognition);

mActivityRecognitionClient =

new ActivityRecognitionClient(this, this, this);

mActivityRecognitionClient.connect();

}

Chapter 13: Maps, Location, and Activity APIs 263

@Override

protected void onDestroy() {

super.onDestroy();

if (mActivityRecognitionClient != null

&& mActivityRecognitionClient.isConnected()) {

mActivityRecognitionClient.disconnect();

mActivityRecognitionClient = null;

}

}

public void doStartActivityRecognition(View view) {

if (mActivityRecognitionReady) {

Intent intent = new Intent(MyIntentService.

ACTION_NOTIFY_ACTIVITY_DETECTED);

mPendingIntent =

PendingIntent.getService(this, 2001, intent, 0);

mActivityRecognitionClient.

requestActivityUpdates(FIVE_SECONDS,

mPendingIntent);

}

}

@Override

public void onConnected(Bundle bundle) {

mActivityRecognitionReady = true;

findViewById(R.id.start_activity_recognition_btn).

setEnabled(false);

}

@Override

public void onDisconnected() {

mActivityRecognitionReady = false;

findViewById(R.id.start_activity_recognition_btn).

setEnabled(true);

}

@Override

public void onConnectionFailed(ConnectionResult connectionResult) {

mActivityRecognitionReady = false;

findViewById(R.id.start_activity_recognition_btn).

setEnabled(true);

// Error handling...

}

}

The click listener (doStartActivityRecognition()) starts the request for activity (not to be confused

with the Activity component) changes and provides a PendingIntent that will be sent to the same

IntentService as shown earlier, but with modifications for handling activity changes as well. When

requesting activity changes, a frequency of 5 seconds is selected. You adapt this frequency depending on the

needs of your application. The higher the frequency, the more battery your application will consume.

264 Part III: Pushing the Limits

protected void onHandleIntent(Intent intent) {

String action = intent.getAction();

if (ACTION_NOTIFY_ACTIVITY_DETECTED.equals(action)){

if (ActivityRecognitionResult.hasResult(intent)) {

ActivityRecognitionResult result = ActivityRecognitionResult.

extractResult(intent);

DetectedActivity detectedActivity =

result.getMostProbableActivity();

Log.d(TAG, “Detected activity: “ + detectedActivity);

if(detectedActivity.getType() != mLastDetectedActivity) {

mLastDetectedActivity = detectedActivity.getType();

showNotification(detectedActivity);

}

}

} else if (ACTION_NOTIFY_ENTERED_GEOFENCE.equals(action)) {

... Geofencing detection showed in previous section...

}

}

private void showNotification(DetectedActivity detectedActivity) {

Notification.Builder builder = new Notification.Builder(this);

builder.setContentTitle(“Activity change!”);

builder.setContentText(“Activity changed to: “

+ getActivityName(detectedActivity.getType()));

builder.setSmallIcon(R.drawable.ic_launcher);

NotificationManager manager =

(NotificationManager) getSystemService(NOTIFICATION_SERVICE);

manager.notify(2001, builder.build());

}

private String getActivityName(int type) {

switch (type) {

case DetectedActivity.IN_VEHICLE:

return “In vehicle”;

case DetectedActivity.ON_BICYCLE:

return “On bicycle”;

case DetectedActivity.ON_FOOT:

return “On foot”;

case DetectedActivity.STILL:

return “Still”;

}

return “Unknown”;

}

The three preceding methods show how the IntentService listens to both geofencing and activity

changes. When an activity change occurs, the result is retrieved through ActivityRecognitionResult.

extractResult(). From that point, you can inspect the type and the confidence of the detection result.

This API can provide you with very accurate information about the users’ behavior and the context they are in

at the moment. If a user is on a bicycle or in a vehicle, you could adapt the interface of the application and rely

more on speech recognition and text-to-speech. If the device reports that it is still for a while, you can make

further assumptions and adapt the behavior of your application accordingly.

Chapter 13: Maps, Location, and Activity APIs 265

Summary

The new Location API provided by Google doesn’t enable anything that Android developers didn’t have

access to before. There has always been a Location API in the Android platform, and there have been third-

party libraries that estimate the users’ activity by reading the sensors. However, with the new API, you have an

easy-to-use solution that works across all Android devices and is guaranteed to work as long as the device has

Google Play Services installed. The new API is very easy to use and can easily be integrated into any existing

application with little effort.

By combining the new Maps and Location API with Geofencing and Activity Recognition, you have a set of

really powerful tools for building context- and location-aware applications that otherwise could take months to

develop. You can now build new types of location-based social games much easier than ever possible before.

When your application is aware of the current context for the users, you can create a much richer user

experience and provide a user interface that varies depending on the situation.

However, beware of overusing these APIs because they do have an impact on battery consumption. Requesting

too frequent updates will consume a lot of power. Experiment and see what rate suits your application best.

Further Resources

Documentation

Google Maps Android API v2: https://developers.google.com/maps/documentation/

android

Websites

The Google APIs Console for setting up API access and new keys for your project: https://code.

google.com/apis/console

Developer guidelines for location-aware applications: http://developer.android.com/

training/location/index.html

Chapter 14

Native Code and JNI

Although the Java-based APIs available for Android are usually sufficient for most applications, a time will

come when you need to go native. You’ll find several reasons for having to use native code in your application.

Perhaps you have an existing proprietary native library that you need to integrate with an application on

Android and re-implementing it in Java would take too much time. Another common reason is performance.

Most performance-critical code in Android is already running in native, not within the Dalvik VM. When you

need to use a performance-critical library, it’s often better to use the native version than trying to find, or port,

a Java version.

I begin this chapter by explaining how to write pure native applications for Android. Because games are the most

common reason for writing pure native applications, I also show how to use OpenGL ES 2.0 for Android in C. Next,

I show how you can mix Java and native code to get the best from both worlds. Then I introduce the basics of the

Java Native Interface (JNI) and how you can use it in your code. Finally, I take you through an example of how to

integrate an existing native C library in your Java application using the stand-alone toolchain .

For this chapter, I assume that you have at least a basic knowledge of the C programming language.

However, even if you don’t, this chapter can help you understand how to work with native code for

your Android application.

A Note on CPU Architecture

When you build a piece of native code with the Android NDK, it will match a specific application binary

interface (ABI). These ABIs represent the different types of CPU architectures that Android supports. At the

time of this writing, the vast majority of Android devices run on ARM CPUs, or more specifically, ARMv7a. This

is an extension of the standard ARM architecture, but it supports a number of more advanced features such as

hardware floating-point computation. The ABIs supported by the Android NDK are armeabi (basic 32-bit ARM

support), armeabi-v7a (ARMv7), x86 (32-bit Intel x86 or IA-32), and mips (MIPS32 revision 1).

When you’re building your native code, consider which CPU architecture (ABI) your application needs to

support. If you compile your code only for ARMv7, your application won’t be visible on the Google Play Store for

devices with the other CPU architectures. In this way you can use native libraries as a sort of filter for the Google

Play Store as well.

You’re now probably asking why you shouldn’t build your native code for every possible platform because

you just need to specify which ABIs you target in the build script (see “Android NDK Build Scripts,” later in this

chapter). The reason for not choosing to support all ABIs is that the native code you’re compiling may not

support some platforms. For instance, if your native code uses a specific CPU feature, porting your code to other

architectures may require a bit of extra work.

268 Part III: Pushing the Limits

Writing Android Applications in C

The C programming language is one of the oldest programming languages still in use. Dennis Ritchie originally

developed it between 1969 and 1973 at AT&T Bell Labs. The language was standardized as ANSI C or C89 in

1989 and has since received several updates, named according to the year of release—for example, C90, C99,

and C11. The latest version, C11, was approved in December 2011.

In this chapter, I’ve intentionally focused on writing native code in C and excluded instructions and

examples for C++. Although a lot of the code here will work for C++, in some situations, C++ requires

different handling, and mixing the instructions for both of these languages can be confusing. Refer to

the official Android NDK documentation on how to use C++ to write native code for your applications.

Android has support for writing all or parts of your application in native C. To get started, you first need to

download the Android Native Development Kit (NDK) from the Android Developers site, which comes as a ZIP

file that you extract in your home directory.

You can find the full instructions on downloading and installing the Android NDK at http://

developer.android.com/tools/sdk/ndk.

Android NDK Build Scripts

After you download and extract the Android NDK, you can start developing native-only applications. However,

you still need the standard Android SDK to package your code into a working APK. You need to place all the

native code that your application will run in the jni directory of the main sources in your project.

In order for Android to understand how to compile your native code, you need to provide the NDK with the

proper build scripts. These files use the same syntax as the normal GNU makefiles, but their names differ from

what you find in source code you compile for Linux desktops or similar UNIX-based operating systems.

The only file required is Android.mk. Here is a simple example of such a file:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := native-activity

LOCAL_SRC_FILES := main.c

LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM

LOCAL_STATIC_LIBRARIES := android_native_app_glue

include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

Chapter 14: Native Code and JNI 269

This example is taken from the native-activity sample application found in the Android NDK (<ndk root>/

samples/native-activity/). I’m using this sample code as a foundation for explaining how a native-

only Android application works. Notice the line starting with LOCAL_STATIC_LIBRARIES that tells the build

system to include a local static library when building this code, a detail I get back to in the section “Porting a

Native Library to Android.”

The second file is Application.mk, which describes which native modules are needed by your application.

Here you also can define additional build parameters, as shown in this example:

APP_ABI := all

APP_PLATFORM := android-10

Note in the preceding example that the contents of Application.mk. APP_ABI are used to tell which ABIs

this application supports—in this case, all the ABIs supported by the current NDK. APP_PLATFORM is similar to

the uses-sdk element in the manifest and specifies which Android API level is supported.

Native Activities

After you define the build files, you can move on to the code for your application. The example in this section is

still based on the native-activity sample found in the Android NDK.

<activity

android:name=”android.app.NativeActivity”

android:label=”@string/app_name”

android:configChanges=”orientation|keyboardHidden”>

<meta-data

android:name=”android.app.lib_name”

android:value=”native-activity”/>

<intent-filter>

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

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

</intent-filter>

</activity>

Note: How you define the main Activity in your manifest differs from Java-based applications. A native

Activity will always use the name android.app.NativeActivity.

Next, you specify which native library should be loaded through the meta-data element with the name

android.app.func_name. This library must be the same as what you specified for LOCAL_MODULE in the

Android.mk file.

When writing a native-only application using the NativeActivity, you have a number of choices. The

native-activity sample found in the Android NDK shows the method that most developers are likely to be

interested in. It uses the API provided by android_native_app_glue.h, which makes it easier to handle

the threading and also shows how to set up sensor and event input.

#include <jni.h>

#include <errno.h>

270 Part III: Pushing the Limits

#include <EGL/egl.h>

#include <GLES/gl.h>

#include <android/sensor.h>

#include <android/log.h>

#include <android_native_app_glue.h>

In the preceding code, notice the included headers from the file main.c in the native-activity sample. By

including android_native_app_glue.h in your native code and also including the local static library

android_native_app_glue in Android.mk, you get a lot of boilerplate code for free. As a result, you can

focus on the actual work of your application, instead of having to set up everything yourself and manage all the

threading.

The following function is the entry point for your native Activity when using the android_native_app_

glue library. Because of the amount of code involved, I didn’t include the actual code from the native-activity

sample, but you can refer to the sample in the Android NDK for the specific details.

void android_main(struct android_app* state) {

// Application initialization and setup goes here...

state->onInputEvent = engine_handle_input;

while(1) {

// Application thread loops here...

}

}

The parameter state has the type of an android_app pointer, which points to a struct where you can

access application data and set callback functions. In the preceding code, you see how to give a pointer to

a previously declared function that implements the input event handling. Here is an example of how such a

function is declared:

static int32_t engine_handle_input(struct android_app* app, AInputEvent*

event) {

// Handle input events here...

}

The examples in this section provide a brief introduction for implementing your native-only Android

application. Refer to the native-activity sample in the Android NDK for further details and additional examples

of how to use OpenGL ES on the native side for your application.

Working with JNI

When writing an application with both native C and managed Java code, you need to use the Java Native

Interface (JNI) for bridging. This is a software layer and API that allows native code to call methods on Java

objects and Java methods to call native functions.

Chapter 14: Native Code and JNI 271

On the Java side, you only need to declare the methods that connect to a native function with the prefix

native. Doing so causes the VM to look up a native function, as I show in the next section. In order for native

code to invoke a method on a Java object, more work is involved, which I show in the section “Calling a Java

Method from Native,” later in this chapter.

Calling Native Functions from Java

To call a native function from a Java method, you need to declare a special method in your class with the keyword

native that will work as a bridge to the native side. This declaration causes the JVM to look up a native function

with the name formed by the package, class, and method name whenever that native method is invoked.

public class NativeSorting {

static {

System.loadLibrary(“sorting_jni”);

}

public NativeSorting() {

}

public void sortIntegers(int[] ints) {

nativeSort(ints);

}

private native void nativeSort(int[] ints);

}

The preceding is a simplified example of a class that provides a method for sorting an array of int. There are

two methods, besides the constructor, in this class. The first, sortIntegers(), is the one that you call from

your other Java classes, and it’s a regular Java method. The second, nativeSort(), is the method that points

to a function in your native code. Although you can have native methods declared as public, it’s good practice

to keep them private and wrap their call in a Java method that can manage error handling.

Although you can write the native code from scratch, a tool called javah found in the Java SDK (not the

Android SDK) can help you with this. This command-line tool generates a C header file with the function

declaration that your native methods map to. Run the following command in the src/main directory of your

project. Make sure you compile the Java code for your application first.

$ $ javah -classpath ../../build/classes/release/ -d jni/ com.aptl.

jnidemo.NativeSorting

The following command shows how to generate a header file for the NativeSorting class shown in the

previous code example. The -classpath points to the compiled class files (usually under the <project

path>/build directory), not the DEX file, and the -d parameter points to the output directory for the

generated headers (usually your jni directory in your project). This command generates a file named com_

aptl_jnidemo_NativeSorting.h in the jni directory that contains the declaration of the native function.

272 Part III: Pushing the Limits

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_aptl_jnidemo_NativeSorting */

#ifndef _Included_com_aptl_jnidemo_NativeSorting

#define _Included_com_aptl_jnidemo_NativeSorting

#ifdef __cplusplus

extern “C” {

#endif

/*

* Class: com_aptl_jnidemo_NativeSorting

* Method: nativeSort

* Signature: ([F)V

*/

JNIEXPORT void JNICALL Java_com_aptl_jnidemo_NativeSorting_nativeSort

(JNIEnv *, jobject, jintArray);

#ifdef __cplusplus

}

#endif

#endif

This code shows the generated files. As the comment on the first line says, you shouldn’t edit this file. You only

copy the method declaration to the .c files that implement the function.

The following code shows the implementation of the JNI function declared in the header file com_aptl_

jnidemo_NativeSorting.h. This example doesn’t do much in the JNI_OnLoad function, other than

return a constant indicating that this code follows JNI version 1.6, which is the one supported by the Dalvik VM.

#include <jni.h>

#include <android/log.h>

#include “com_aptl_jnidemo_NativeSorting.h”

void quicksort(int *arr, int start, int end);

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

return JNI_VERSION_1_6;

}

JNIEXPORT void JNICALL Java_com_aptl_jnidemo_NativeSorting_nativeSort

(JNIEnv *env, jobject obj, jintArray data) {

jint* array = (*env)->GetIntArrayElements(env, data, 0);

jint length = (*env)->GetArrayLength(env, data);

quicksort(array, 0, length);

(*env)->ReleaseIntArrayElements(env, data, array, 0);

}

void quicksort(int *arr, int start, int end)

{

// Left out as an exercise for your upcoming interview at Google...

}

Chapter 14: Native Code and JNI 273

In this example, the calls to GetIntArrayElements, GetArrayLength, and ReleaseIntArray

Elements are the JNI-specific code. The first function call retrieves a pointer to the internal data of the array

that can be passed to regular C function. The second one gives you the size of the array. Finally, the third one

tells the JVM that the native side is done with the array and that it can copy back its content. These function calls

are needed because the complex data types (arrays and objects) that are passed from Java to native through JNI

must be accessed through the JNIEnv object.

Note: The call to GetIntArrayElements returns a jint pointer, which points to the data within the

jintArray received in the function, and you can treat the jint pointer as a normal int pointer.

The preceding JNI example is for demonstration purposes only; you should always perform sorting of

this kind using Arrays.sort() or Collections.sort(). Normally, you won’t need to perform

sorting on the native side because the Java implementation is fast enough.

Calling a Java Method from Native

Calling a Java method from the native side is a bit more complicated than calling a native function from Java.

Because Java is object-oriented, every method call must be made toward a Java object (or toward a Java class

for static methods). Thus you need to get a reference not only to the method but also to the object you want

to call the method on. This is where the JNIenv, JavaVM, and jobject parameters shown in the earlier

examples become important.

Consider the example of native sorting in the previous section. Everything in that example is happening on the

same thread, and the native function call will block until the sorting is completed. You can modify this so that

the sorting will happen asynchronously. A callback object is passed to the native method, which will then be

called by the native code using JNI functions, as shown here:

public void sortIntegersWithCallback(int[] ints, Callback callback) {

nativeSortWithCallback(ints, callback);

}

private native void nativeSortWithCallback(int[] ints, Callback callback);

public interface Callback {

void onSorted(int[]sorted);

}

The preceding methods show the Java side of the modified native sorting. With the following, you generate the

header file as before using the javah tool.

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_aptl_jnidemo_NativeSorting */

#ifndef _Included_com_aptl_jnidemo_NativeSorting

#define _Included_com_aptl_jnidemo_NativeSorting

#ifdef __cplusplus

274 Part III: Pushing the Limits

extern “C” {

#endif

/*

* Class: com_aptl_jnidemo_NativeSorting

* Method: nativeSortWithCallback

* Signature: ([ILcom/aptl/jnidemo/NativeSorting/Callback;)V

*/

JNIEXPORT void JNICALL Java_com_aptl_jnidemo_NativeSorting_

nativeSortWithCallback

(JNIEnv *, jobject, jintArray, jobject);

#ifdef __cplusplus

}

#endif

#endif

Note: The function declaration contains a jobject parameter. This is a JNI reference to the callback object

you’ll use for returning the result.

The following is the new implementation of the native code for your sorting, divided into multiple parts to

make things clearer. The changes from the previous example are shown in bold:

#include <jni.h>

#include <android/log.h>

#include <pthread.h>

#include “com_aptl_jnidemo_NativeSorting.h”

JavaVM *g_vm;

struct thread_args {

int* data;

int data_size;

jobject callback;

};

void quicksort(int *arr, int start, int end);

void background_sorting(void* args);

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

g_vm = vm;

return JNI_VERSION_1_6;

}

As you can see, first you include the pthread library that provides the native API for threading. Next, you declare

two global variables, a reference to the JavaVM and a struct that will be used to pass arguments to the

new thread. The JavaVM variable is assigned in the JNI_OnLoad function. Also note the declaration of the

function background_sorting. This function is passed to the new thread.

JNIEXPORT void JNICALL

Java_com_aptl_jnidemo_NativeSorting_nativeSortWithCallback

(JNIEnv *env, jobject obj, jintArray data, jobject callback) {

jint* array;

Chapter 14: Native Code and JNI 275

jint length;

jmethodID callbackMethodId;

jclass callbackClass;

pthread_t thread;

struct thread_args* myThreadData = malloc(sizeof(struct thread_args));

array = (*env)->GetIntArrayElements(env, data, 0);

length = (*env)->GetArrayLength(env, data);

myThreadData->data = array;

myThreadData->data_size = length;

myThreadData->callback = (*env)->NewGlobalRef(env, callback);

(*env)->ReleaseIntArrayElements(env, data, array, JNI_COMMIT);

pthread_create(&thread, NULL, (void*)background_sorting,

(void*) myThreadData);

}

In the preceding example the new JNI function mapping to your native method first allocates a new struct

thread_args that will contain the parameters for the new thread. Next, you create a global reference to

the callback object through the call to NewGlobalRef(), which you must do because all references in a JNI

function are valid only on the current thread. This is followed by a call to ReleaseIntArrayElements,

which notifies the VM that you’re done with the jintArray. However, the difference between this example and

the one shown in “Calling Native Functions from Java” is that you pass JNI_COMMIT as the last parameter—to

prevent the VM from freeing the array variable that contains the data. Finally, you call pthread_create() to

start the new thread. The third argument to pthread_create() is a pointer to the function that will run on

the new thread, and the fourth argument is the argument passed to that function.

The following code shows the function that will execute on the background native thread:

void background_sorting(void* arg) {

struct thread_args *data = (struct thread_args *) arg;

JNIEnv* env = NULL;

jclass callbackClass;

jmethodID callbackMethodId;

jintArray result;

quicksort(data->data, 0, data->data_size);

(*g_vm)->AttachCurrentThread(g_vm, &env, NULL);

result = (*env)->NewIntArray(env, data->data_size);

(*env)->SetIntArrayRegion(env, result, 0, data->data_size, data->data);

callbackClass = (*env)->GetObjectClass(env, data->callback);

callbackMethodId = (*env)->GetMethodID(env, callbackClass,

“onSorted”, “([I)V”);

(*env)->CallVoidMethod(env, data->callback, callbackMethodId, result);

free(data->data);

free(data);

276 Part III: Pushing the Limits

(*env)->DeleteGlobalRef(env, data->callback);

(*g_vm)->DetachCurrentThread(g_vm);

}

First, you cast the argument back to a pointer of the expected type. You can now call quicksort() using

these arguments to sort the array. Next, you need to connect to the Java VM, create a Java array for the result,

find the callback method and invoke it, and finally free all the resources.

The call to AttachCurrentThread() tells the Java VM that the current thread should be connected and

the JNIEnv argument will be assigned to a valid instance. Now you can make calls on the JNIEnv object

and create a new Java int array. To find the callback method, you need to look up the class by a call to

GetObjectClass() with the global jobject reference as an argument. Next, you retrieve a jmethodID

by calling GetMethodID() with the jclass, the name of the method, and its native signature as arguments.

With a jmethodID and a jobject, you can call CallVoidMethod() to invoke the Java method.

The signature of a Java method is a string that identifies the arguments and the return value. You can

read more about type signatures at http://docs.oracle.com/javase/6/docs/technotes/

guides/jni/spec/types.html#wp16432.

Lastly, you free the data allocated in the struct, delete the global reference to the callback object, and detach

the VM from this thread.

The example in this section doesn’t include error handling and is used only to illustrate how to pass Java objects

between two threads and how to invoke methods from a native pthread. The important thing to remember is

to free all resources properly to avoid the most common cause of errors when using JNI.

Android Native APIs

Whether you’re writing an application entirely in native C or by combining native code with Java using JNI, you

will most likely need to use some of the native APIs for Android. In this section, I cover some of these APIs and

how you can include them in your code.

Using a native API requires you to do two things: Add a reference in the Android.mk file so that your shared

library will link to it and include the correct header file in your C code.

To see the full documentation for the supported native APIs, refer to the NDK documentation (<ndk root>/

documentation.html).

The C Library

The C programming language has a standard library named C library, or libc. The Android implementation of

this library is called Bionic and is slightly different from the standard libc implementation found on regular Linux

distributions.

Chapter 14: Native Code and JNI 277

The Android NDK build system always links to the C library, so all you need to do when using this library is

include the correct header file (stdio.h, stdlib.h, and so on). Also, the pthread and math libraries are

automatically linked in the same way.

Native Android Logging

The logging functionality used in Android through the Java class Log is also available in native C through the

log library. To include support for this in your native code, add the following to your Android.mk file:

LOCAL_LDLIBS := -llog

In your C code, you need to include the header file as follows:

#include <android/log.h>

To simplify your code, wrap the functions declared in the Android log library in a macro. Use the following code

in your C code to define log macros to be used throughout your native code:

#define TAG “native-log-tag”

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, TAG,

__VA_ARGS__))

#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, TAG,

__VA_ARGS__))

#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, TAG,

__VA_ARGS__))

#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, TAG,

__VA_ARGS__))

Native OpenGL ES 2.0

If you’ll be writing a pure native application, you will most likely implement the user interface for your

Activity using OpenGL ES 2.0, as shown in an earlier example in the section “Native Activities.” To use this

API in your native code, add -lGLESv2 to LOCAL_LDLIBS in your Android.mk file and include the headers

GLES2/gl2.h and GLES2/gl2ext.h in your C code.