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:- Follow the build instructions which can be found in the Android Bluetooth FAQ.
- Copy the libraries libbluetooth.so and libbluedroid.so into $NDK_HOME/build/platforms/android-1.5/arch-arm/usr/lib/
- Copy the bluetooth folder from $SDK_SOURCE/external/bluez/libs/include/bluetooth/ to $NDK_HOME/build/platforms/android-1.5/arch-arm/usr/include/
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 BluezYou 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:
Excellent stuff, Stephan. I will have to spend some time root my 1.1 machine first!
Harish
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?
@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 :)
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
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!
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
Forget one thing, I am using HTC Hero
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
@Deny: Ok, download is now fixed.
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?
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
In /etc/bluez/main.conf
Find "DiscoverableTimeout = 300" change 300 to 0
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?
@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.
api 2.1 does not allow to make many things. For example to receive value RSSI for the paired devices.
The latest Android version for NDK is 5.
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??
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?
Pardon!
I said SE Nexus One, what I meant was SE Xperia X10.
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.
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.
Not too convinced by the c++ - why is infoArray twice new[]ed and then deleted (no [])? In fact, why's it malloced at all...?
@Anonymous: Oh wow. You found a bug in some example code someone posted on teh interwebs. You must be real l33t.
Post a Comment