Thursday, July 23, 2009

Writing applications using the Android NDK

I did not find any easy to read tutorials on how to exactly build an Android app with some native code component. Its true that the NDK package comes with some docs, but for Android beginners, this may not be an exciting prospect. However, I still maintain that the docs in the package are a must-read.

The following app simply prints out a small string returned from a C function on the LogView.

1. Create a Normal Android app. Lets call it 'native1'.
2. The main source file should look like this.


package com.earl.native1;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class native1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Log.i("--EARL--", new NativeLib().getHelloWorld());
}
}

class NativeLib
{
public native String getHelloWorld();

static {
System.loadLibrary("helloworld");
}

}



3. If you are using Eclipse, the class files built will be in the 'bin' directory.
4. Navigate to it and run 'javah' on the class file that contains the native method.

javah -jni com.earl.native1.NativeLib

5. After this, you will get one header file. This file contains the JNIEXPORTs generated from the java source.
6. Copy this file and paste it in a folder in the 'sources' dir of the Android NDK. I'm calling the folder 'earl'.
7. Now, create the C implementation file - earl.c It should look like this.


#include "com_earl_native1_NativeLib.h"

JNIEXPORT jstring JNICALL Java_com_earl_native1_NativeLib_getHelloWorld
(JNIEnv * env, jobject obj)
{
return (*env)->NewStringUTF(env, "Greetings from Earlence !");
}



8. Now you have to create the Android.mk file. This file describes your native code module to the build system. This file is created in the same dir as the source file.



# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := helloworld
LOCAL_SRC_FILES := earl.c

include $(BUILD_SHARED_LIBRARY)




9. Now, navigate to the 'apps' dir in the NDK home and create a folder of your choice. This will hold the Android Application you built using Eclipse earlier. I am calling it earl_jni. In this folder, create another one and call it 'projects'. In this, copy your ENTIRE eclipse project 'native1'. Then, navigate one level up to earl_jni and create the Application.mk file.



APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES := helloworld



10. Now, navigate to the NDK home and run the make command.

$ make APP=earl_jni

11. You should see this output if everything works correctly.


Android NDK: Building for application 'earl_jni'
Compile thumb : helloworld <= sources/earl/earl.c
SharedLibrary :
libhelloworld.so
Install : libhelloworld.so => apps/earl_jni/project/libs/armeabi


12. Now, a folder called 'libs' will be created in apps/earl_jni/projects. This contains the .so file i.e the native library. Copy this into the eclipse project root. Compile and run. You should see something like on the LogView


I/ActivityManager( 564): Starting activity: Intent { action=android.intent.acti
on.MAIN categories={android.intent.category.LAUNCHER} flags=0x10200000 comp={com
.earl.native1/com.earl.native1.native1} }
I/--EARL--( 699): Greetings from Earlence !
I/ActivityManager( 564): Displayed activity com.earl.native1/.native1: 704 ms


Congratulations!!! You have just built your first JNI app for Android.





Installing Android NDK on Windows XP/Vista

What you need:

  • A Windows XP/Vista computer (yeah, u absolutely need it :)).
  • cygwin (make sure that make and gcc utilities are installed).
  • Android SDK 1.5
Steps to install:

  • Download the NDK from the Android developer site and unpack it to a disk location. I'm calling this NDK_HOME.
  • Startup cygwin and navigate to NDK_HOME.
  • Enter this command
  • export ANDROID_NDK_ROOT=NDK_HOME (note, use forward slashes '/' instead of the usual windows backslash. Remember that you are using a Linux emulation tool!).
  • Run this setup command.
  • bash build/host-setup.sh
  • You should get a "Host Setup Complete" message if everything went properly.
You're done.

Tuesday, July 21, 2009

NAT Traversal for Android

STUN (Simple Traversal of User Datagram Protocol through Network Address Translators) is a protocol that helps in the traversal of NATs/Firewalls. STUN works with full cone NAT, restricted cone NAT, and port restricted cone NAT. It does not work with Symmetric NATs. Traversal Using Relay NAT(TURN) maybe used in these cases. Another method for traversal is ICE (Interactive Connectivity Establishment). Here, I am focusing on NAT traversal for Android phones using STUN

  • jStun
  • stun4j
These are two STUN libraries available for the Java language. A jStun port is available from HSC. I've tested this on the Android Emulator. It returns an IP address and port mapping.

stun4j is another STUN library. This I tried directly on the Android emulator with no porting. It works directly with no code changes. I have compared results from both the libraries. Results are the same!!!


Monday, July 20, 2009

Real Time Audio Streaming for Android - Some Developments

OK, so I've made some progress on this project.

  • I have figured out how the Audio Streaming APIs work on the 1.5 SDK.
  • I have obtained a G 711 speech codec in Java which runs on Android. This was taken from the 'sipdroid' VoIP client.
  • I have been able to stream a pre-recorded PCM voice file to the emulator via UDP and play it using AudioTrack.
  • I have figured out how to use the jLibRtp RTP stack in a Java program. There is a port of the same stack by HSC for Android. So I guess it should work there also, but, I must still test it out.
So, it looks like most of the pieces of the puzzle are in place. However, while using AudioRecord and UDP on the emulator, I have encountered the following problems.

  1. On the emulator, I instantiate AudioRecord with a sampling rate of 8000Hz and an encoding of 16BIT PCM. This works fine. However, when I try to run the same code on a device, the AudioRecord object does not get created. Looks like I will have to experiment with different sampling rates and buffer sizes. Strange, huh?
  2. To test the sending and receiving of PCM data, I built a small Java applet that streamed the PCM data to the emulator. I setup proper port redirections. I am able to receive the data in the emulator, but, when I try to send data OUT from the emulator, nothing happens.
Looks like once these problems are sorted out, we are good to go!!!