Android Bluetooth Hacking using the NDK

Many people lamented the omission of the Bluetooth APIs from Android 1.1 and 1.5 firmware builds. This post will detail a method of writing bluetooth-enabled apps for Android using nothing more than the recently released Native Development Kit (NDK). You will not have to root your phone. However, since the NDK is available for firmwares 1.5 onwards only, this method will unfortunately NOT work on 1.1 firmwares. Full source for an Eclipse demo project can be found at the bottom of this article. This article builds on my earlier post (Android Hello NDK).


Obtaining the C Headers and Bluetooth System Libraries

The bad news is that you will have to recompile the 1.5 (CupCake) sources with bluetooth support enabled. The somewhat good news is that you can find ready-built binaries and header files for download at the end of this article. If you prefer to build your own library though:
  1. Follow the build instructions which can be found in the Android Bluetooth FAQ.
  2. Copy the libraries libbluetooth.so and libbluedroid.so into $NDK_HOME/build/platforms/android-1.5/arch-arm/usr/lib/
  3. Copy the bluetooth folder from $SDK_SOURCE/external/bluez/libs/include/bluetooth/ to $NDK_HOME/build/platforms/android-1.5/arch-arm/usr/include/
You are now ready to start building bluetooth-enabled apps. Please note that we only need to place the ARM bluetooth binaries in the NDK to resolve link-time dependencies. We DO NOT actually have to deploy/add the bluetooth libraries to our phones using our .apk file, since the very same bluetooth libraries you built from the are already available on the Android device.

Android and Bluez


Bluetooth support on the Android is provided by the excellent Bluez library. There is however a dearth of good information/documentation on Bluez programming. What documentation there is on Bluez programming seems to mostly cover the Python bindings. There is only one resource that I could find which does a decent job of covering C programming using Bluez (See Resources).

The Android Project

This demo application will do a simple scan for bluetooth devices, and populate an Android ListView control with the results. Let us now create an Activity that includes a ListView, declares a native JNI method, and loads a shared library inside a static initializer.

"<project_home>/src/net/bruary/bluescan/BlueScan.java"


10 public class BlueScan extends Activity {
11
12 ListView lvDevices;
13 private ArrayList deviceArray;
14 private ArrayAdapter listAdapter;
15
16 /** Called when the activity is first created. */
17 @Override
18 public void onCreate(Bundle savedInstanceState) {
19 super.onCreate(savedInstanceState);
20
21 setContentView(R.layout.main);
22
23 lvDevices = (ListView) findViewById(R.id.lvDevices);
24 deviceArray = new ArrayList();
25 listAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, deviceArray);
26 lvDevices.setAdapter(listAdapter);
27
28 String[] devices = btDeviceScan();
29
30 for (String device: devices){
31 deviceArray.add(device);
32 }
33
34 listAdapter.notifyDataSetChanged();
35
36 }
37
38 /*Our native method which receives an array of Strings back from
39 * the C++ shared library
40 * */

41 public native String[] btDeviceScan();
42
43 static {
44 System.loadLibrary("bluescan");
45 }
46
47 }




Setting up our NDK Project

Our NDK project is setup as per usual. The notable difference is in our library's Android.mk file, where we now must include the libbluetooth.so library. In the following listing please pay special attention to line #5. Due to a bug in the current NDK, we must explicitly set our library path to $(SYSROOT)/usr/lib. If you omit this, the linker won't find libbluetooth(or libc, libm etc. for that matter)


 1 LOCAL_PATH := $(call my-dir)
2
3 include $(CLEAR_VARS)
4
5 LOCAL_LDLIBS :
= -L$(SYSROOT)/usr/lib -lbluetooth
6 LOCAL_DEFAULT_CPP_EXTENSION := cpp
7
8 LOCAL_MODULE :
= bluescan
9 LOCAL_SRC_FILES := bluescan.cpp
10
11 include $(BUILD_SHARED_LIBRARY)



Shared Library Sources

The following listing shows our C++ implementation of the Bluez code for scanning for some nearby bluetooth devices. I don't want this to devolve into a Bluez programming tutorial, so I will keep my comments brief.
This sourcecode is adapted from the "simplescan" application in the "Bluetooth Programming in C with Bluez" tutorial. Therefore some of the code is a bit redundant (e.g. perror() is redundant in our NDK app). I also don't do any error/exception handling, so please don't deploy this source to production. You've been warned :)

The two bluez API methods we call are hci_get_route() on line 30, which gives us the deviceID of our Phone's bluetooth device. The other method is hci_inquiry() on line 40, which does the actual scanning. Notice that we pass an array of inquiry_info structs to hci_inquiry().
Please do read the article linked to in the Resources section for more details on Bluez programming.



1 #include <string.h>
2 #include <jni.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <sys/socket.h>
7 #include <arpa/inet.h>
8 #include <bluetooth/bluetooth.h>
9 #include <bluetooth/rfcomm.h>
10 #include <bluetooth/hci.h>
11 #include <bluetooth/hci_lib.h>
12 #include <bluescan.h>
13
14
15 JNIEXPORT jobjectArray JNICALL Java_net_bruary_bluescan_BlueScan_btDeviceScan(JNIEnv *env, jobject obj)
16 {
17 int max_rsp = 255;
18 inquiry_info* infoArray = new inquiry_info[max_rsp];
19 inquiry_info **ii = NULL;
20 int num_rsp;
21 int dev_id, len, flags;
22 int i;
23 char addr[19] = {0};
24 char name[248] = {0};
25 bool bHasError =false;
26 jstring errMsg;
27
28 jobjectArray btDevices;
29
30 dev_id = hci_get_route(NULL);
31 if (dev_id < 0 ){
32 bHasError = true;
33 errMsg = env->NewStringUTF("Error obtaining device ID");
34 }
35
36 len = 15;
37 flags = IREQ_CACHE_FLUSH;
38 infoArray = new inquiry_info[max_rsp];
39
40 num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &infoArray, flags);
41 if (num_rsp < 0)
42 perror("hci_inquiry");
43
44 btDevices = env->NewObjectArray(num_rsp, env->FindClass("java/lang/String"), env->NewStringUTF(""));
45
46 for (i = 0; i < num_rsp; i++){
47 ba2str(&(infoArray[i].bdaddr), addr);
48 memset(name, 0, sizeof(name));
49
50 env->SetObjectArrayElement(btDevices, i, env->NewStringUTF(addr));
51
52 }
53
54 delete(infoArray);
55
56 return btDevices;
57 }




Running the Application

When running the application, please note that I set the len parameter above to 15 (fifteen). The total scanning time will therefore be (1.28 * 15)seconds. I don't know where the 1.28 comes from, much of Bluez is still a mystery to me.

Resources

Blueooth Programming in C with Bluez
You can download the Eclipse project files here: Eclipse Project Files
You can download the NDK project files here: NDK Project Files
You can download my pre-built libbluetooth binaries for ARM NDK here: Bluetooth NDK Binaries

23 comments:

harishpillay said...

Excellent stuff, Stephan. I will have to spend some time root my 1.1 machine first!

Harish

Hugo said...

Thanks Stephan, I've tested this on my HTC Hero, but it doesn't work. The hci_get_route call returns -1, so aparently it can't find a bluetooth device (or isn't allowed to?). Any suggestions how to debug this?

Stephan February said...

@Hugo - Confirm that you have given your application the appropriate <uses> BLUETOOTH permissions in your android manifest file.
Also confirm that you actually have bluetooth turned ON on your phone :)

brain said...

I have same as Hugo
hci_get_route(NULL) return -1. all permissions granted and bluetooth is on.

this happens with HTC Hero and Samsung Galaxy. and same code without a change works fine on G1 and HTC Magic

Anonymous said...

If you get -1 from hci_get_route then add both BLUETOOTH and BLUETOOTH_ADMIN permissions to the manifest. I found just BLUETOOTH alone was not enough with an Android 1.5 device (HTC Magic).
Nice example -- thank you!

kun said...

Hi:

I tried exactly as the instruction above, but always met problem which is stopped unexpectedly when open the app, and output of logcat indicates

No JNI_OnLoad found in /data/data/net.bruary.bluescan/lib/libbluescan.so 0x43583218
I/WindowManager( 81): onOrientationChanged, rotation changed to 0
D/dalvikvm( 634): +++ not scanning '/system/lib/libwebcore.so' for 'btDeviceScan' (wrong CL)
D/dalvikvm( 634): +++ not scanning '/system/lib/libmedia_jni.so' for 'btDeviceScan' (wrong CL)
E/dalvikvm( 634): Rejecting allocation of 4294967295-element array

Any suggestion would be very appreciated, I added the include and lib path into $PATH already. Didn't do any other modification. Just open the eclipse and try to run.

Thanks a lot

kun said...

Forget one thing, I am using HTC Hero

Deny said...

I tried to download NDK Project Files and Bluetooth NDK Binaries which are attached in bottom of the article. as result these files are absent on the site. Could you give right paths to get these files?
Thank You

Stephan February said...

@Deny: Ok, download is now fixed.

Sam said...

Thanks for the article Stephan - unfortunately another user here with hci_get_route returning -1 on HTC Hero. Also, "hcitool dev" returns "Devices:", so I'm guessing for some reason no userland apps are being allowed access to the BT device. I have no idea why, anyone any clues?

Rafal said...

Great post. I was wondering if i can use this approach on my nexus one. The problem i have is that i can make bluetooth discoverable only for no longer then 300 seconds. After that i have to pop up the question to set device in discoverable mode again. I didn't have to do that on my G1. I guess the short question would be: can i use ndk to keep bluetooth device discoverable for long periods of time without bothering the user every 300s with a pop up? Thanks

Anonymous said...

In /etc/bluez/main.conf

Find "DiscoverableTimeout = 300" change 300 to 0

Anonymous said...

I cannot get hci_get_route working. It returns ENODEV on HTC Legend (Android 2.1) Is this because /dev/socket/bluetooth and /dev/socket/dbus_bluetooth have 660 permission for bluetooth/bluetooth?

Stephan February said...

@anonymous I don't have access to a 2.1 device. But I thought that there is now official API support for bluetooth in 2.1, so no need for these NDK hacks anymore. Please correct me if I'm wrong.

Anonymous said...

api 2.1 does not allow to make many things. For example to receive value RSSI for the paired devices.

Anonymous said...

The latest Android version for NDK is 5.

Marcus said...

hi! I have problems with connecting my HTC Legend via bluetooth to my car and my pc. It seems, the connection breaks 3 seconds after pairing. Any tools or hints how to fix it??

pikipirs said...

Hi!

Thanks for this tutorial! I have implemented this in an android app. However I found out that this works on some devices but not on all of them. Non-working are mostly phones sold by HTC.

Working: Google Nexus One, Motorola Droid/Milestone, SE Nexus One

Non-working: HTC Eris, HTC Incredible, HTC Desire, Samsung Moment.

Non-working phones report ERNODEV when hci_get_route is called.

Is there anything we could do about this?

pikipirs said...

Pardon!

I said SE Nexus One, what I meant was SE Xperia X10.

Anonymous said...

Has anyone found a solution to this? My company is willing to pay a consulting fee to anyone with a solution or who is capable of finding a solution to the HTC phones.

Anders Hedberg said...

Hi Anonymous, please contact me on anders.hedberg[At]join.se It's possible to use custom roms to get different bluetooth profiles working well, but not at the same time. If your willing to pay, we could have a look into the differences. (We stumbled upon this needing both the serial and headset profiles at the same time from a device) We can live with just the serial profile, but it bothers me a lot to not understand the problem, so I'm willing to dig deeper if someone can pay for it.

Anonymous said...

Not too convinced by the c++ - why is infoArray twice new[]ed and then deleted (no [])? In fact, why's it malloced at all...?

Stephan February said...

@Anonymous: Oh wow. You found a bug in some example code someone posted on teh interwebs. You must be real l33t.

Post a Comment