Sunday, December 27, 2009

Android Bluetooth in Eclair


If you noticed the source code in Eclair branch, you will find few differences in Bluetooth function. There are three new git repositories for bluetooth. "bluez", "glib" and "hcidump", all of them are located in /external/bluetooth folder, not /external/bluez anymore.

bluez
android source: git://android.git.kernel.org/platform/external/bluetooth/bluez.git
Eclair is using bluez version 4.47 and the big difference would be bluez API. Since it has a lot of changes between bluez3 (android 1.6) and bluez4 (Android 2.0), we can tell from the documents in doc folder. Or we can use dbus-send to get the detailed information.



# dbus-send --system --type=method_call --print-reply --dest=org.bluez / org.bluez.Manager.DefaultAdapter
method return sender=:1.2 -> dest=:1.3
object path "/org/bluez/932/hci0"

# dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/932/hci0 org.freedesktop.DBus.Introspectable.Introspect


Also, bluetooth service name is changed in Eclair. It's "bluetoothd" now. Check init.rc in /system/core/rootdir.


service bluetoothd /system/bin/bluetoothd -d -n
socket bluetooth stream 660 bluetooth bluetooth
socket dbus_bluetooth stream 660 bluetooth bluetooth
# init.rc does not yet support applying capabilities, so run as root and
# let bluetoothd drop uid to bluetooth with the right linux capabilities
group bluetooth net_bt_admin misc
disabled

glib
android source: git://android.git.kernel.org/platform/external/bluetooth/glib.git
When we build bluez library, it would build glib as a static library.
GLib provides the core application building blocks for libraries and applications written in C. It provides the core object system used in GNOME, the main loop implementation, and a large set of utility functions for strings and common data structures. If you are interested in how bluez use glib, you can trace bluez source code.

new Bluetooth API
http://developer.android.com/guide/topics/wireless/bluetooth.html
About Bluetooth, Android website has a very good document. It lists the functions and example code.


Bluetooth
* Turn on/off Bluetooth
* Device and service discovery
* Connect to a remote device using RFCOMM and send/receive data
* Advertise RFCOMM services and listen for incoming RFCOMM connection


Bluetooth Chat application
What I am curious? It's related to using RFCOMM and send/receive data. In the beginning, I am not quite understand and I thought it's OBEX. After running Bluetooth Chat application, I know what it means now. I installed 0xlab experimental eclair image in Beagle board and installed Bluetooth chat example from Android. I also run a python script in my ubuntu machine and it's from pybluez. Then, I can chat between Beagle board and my laptop via Bluetooth. When we start to run Bluetooth Chat application, it would create a RFCOMM socket and set it to listen mode. We can retrieve the information from sdptool. Then other BT devices can connect to Android using RFCOMM protocol and set channel to the same one.


# sdptool browse local
Browsing FF:FF:FF:00:00:00 ...
Service Name: BluetoothChat
Service RecHandle: 0x10005
Service Class ID List:
UUID 128: fa87c0d0-afac-11de-8a39-0800200c9a66
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 30


From Collages

Friday, December 18, 2009

0xdroid beagle-donut-0x3 is released now!

So exciting is our brand new release called '0xdroid beagle donut-0x3' that we forgot all about the coming bad weather in Taipei (raining, humid, and chill). This is our third released image for Beagle board in Android and it's based on Donut branch. If you have a beagle board on hand, you may give it a try. We provide a 'Happy installer' tool and it could give you a 'no pain no tears' good experiences on installing Android. Here is a 2 minutes video in youtube based on real booting time! If you don't have any beagle board to see our work, you could take it a look from our released videos in youtube.


What kind of meat we have this time?

Easy access to Internet through USB OTG network routed via host
  • Ethernet support + USB OTG network with default static IP configurations ...
Performance improvements
  • Dalvik VM + JIT compiler for ARMv7 ...
  • ARM NEON optimizations for PixelFlinger ...
Theme Flexibility
  • Theme selector introduced ...
  • Flexible resolution support for Launcher ...
More and better peripheral support
  • External GSM modem for functional Android Telephony/RIL ...
  • Bluetooth OBEX OPush and FTP support ...
  • Motion sensor support ...
  • Camera capture / recording ...
Stability improvements with several issues fixed
  • Dalvik stability fix ...
  • Wifi signal strength with Linux Wireless Extension fix ...
  • Mouse stability fix ...
Here is our Roadmap about all these releases. If you have interest on particular item, you could get more information from our issue tracker. Furthermore, we welcome your feedback or any suggestion on 0xlab-devel mailing list!

Frankly speaking, my favorite feature in this released image is neither Bluetooth Obex nor GSM modem, what I like most is our theme launcher. It has a Christmas theme inside. This theme is our best wishes. Wish everyone have a Merry Christmas and a Happy New Year!



Tuesday, December 1, 2009

Provide Bluetooth FTP profile in Android

About providing Bluetooth FTP & OPP profile issue, I've merged all related source code to 0xdroid begle-donut branch. I verified these two services in both Beagle board and Android Dev phone. It looks good now.

When we turn BT on, we start 'obex-client' service in the background. It would register a record to SDP server in bluez and then other BT devices can find we provide this service. How it looks like? We provide channel 7 for FTP. Other BT device can browse our file system using rfcomm. They can pull data from us, modify file name, delete file, or push data to us. Below it's FTP record in SDP database:


Service Name: File Transfer server
Service RecHandle: 0x10003
Service Class ID List:
"OBEX File Transfer" (0x1106)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 7
"OBEX" (0x0008)
Profile Descriptor List:
"OBEX File Transfer" (0x1106)
Version: 0x0100





Let's explain it and trace the source code.
Eg. CreateSession method call in obex-client dbus API

How we connect obex-client with Bluetooth UI? [using JNI, aidl, and d-bus]
We register a FTP service from AndroidRuntime. We also create few methods and it's based on obex client API document. Below texts are related source code:

AndroidRuntime.cpp

REG_JNI(register_android_server_BluetoothFtpService),

android_server_BluetoothFtpService.cpp

int register_android_server_BluetoothFtpService(JNIEnv *env)
{
jclass clazz = env->FindClass("android/server/BluetoothFtpService");

method_onCreateSessionComplete = env->GetMethodID( clazz, "onCreateSessionComplete", "(Ljava/lang/String;Ljava/lang/String;Z)V" );
method_onChangeFolderComplete = env->GetMethodID( clazz, "onChangeFolderComplete", "(Ljava/lang/String;Ljava/lang/String;Z)V" );

return AndroidRuntime::registerNativeMethods(env,
"android/server/BluetoothFtpService", sMethods, NELEM(sMethods));
}


Below text is the sequence from UI to native code and then back to UI.

RemoteFileManagerActivity.java (Pick up one device from remote tab in Bluetooth UI)

public boolean onOptionsItemSelected(MenuItem item) {
// Intent intent;
switch (item.getItemId()) {
case MENU_SELECT_SERVER:
if (mFTPClient.isConnectionActive()) {
Toast.makeText(this, R.string.error_ftp_connect_timeout, Toast.LENGTH_LONG).show();
} else {
/* Connect to Server */
handleServerSelect();
}

public void handleServerSelect() {
mDirectoryButtons.removeAllViews();
currentDirectory = "/";
if (mContext.isBluetoothEnabled()) {
Intent intent = new Intent(getApplicationContext(), BluetoothDevicePicker.class);
intent.setAction(BluetoothAppIntent.ACTION_SELECT_BLUETOOTH_DEVICE);
intent.putExtra(BluetoothAppIntent.PROFILE, BluetoothAppIntent.PROFILE_FTP);
intent.setData(Uri.parse("file://" + currentDirectory));
try {
startActivityForResult(intent, SUBACTIVITY_PICK_BT_DEVICE);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "No Activity for : " + BluetoothAppIntent.ACTION_SELECT_BLUETOOTH_DEVICE, e);
}
}
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

switch (requestCode) {
case SUBACTIVITY_PICK_BT_DEVICE:
if (resultCode == RESULT_OK && data != null) {
/* Obtain the Server name and Address */
String serverAddress = data.getStringExtra(BluetoothDevicePicker.ADDRESS);
String serverName = data.getStringExtra(BluetoothDevicePicker.NAME);
if (mFTPClient != null) {
mFTPClient.setAddress(serverAddress);
mFTPClient.setName(serverName);
mServerConnectButtonLayout.setVisibility(View.GONE);
if(mFTPClient.createSession() != true) {
String szStr = getResources().getString(R.string.ftp_connect_failed, mFTPClient.getName());
Toast.makeText(this, szStr, Toast.LENGTH_LONG).show();
updateServerStatus();
} else {
String szStr = getResources().getString(R.string.ftp_connect_device, mFTPClient.getName());
showBusy(mFTPClient.getName(), szStr);
}
}
}
break;
}
} /* onActivityResult */

BluetoothFtpService.java (handle from Android Frameworks)

public synchronized boolean createSession(String address, IBluetoothFtpCallback callback) {
/*
* Need to register callback before calling native code
* (which could potentially call callback)
*/
BluetoothObexDatabase.SessionDbItem dbItem =
mSessionDb.new SessionDbItem(address,null,callback);
mSessionDb.insert(dbItem);

boolean ret = createSessionNative(address);

if (!ret) {
mSessionDb.deleteByAddress(address);
}

return ret;
}

android_server_BluetoothFtpService.cpp (use JNI and send a dbus method call to obex-client)
 
static jboolean createSessionNative(JNIEnv* env, jobject object, jstring address )
{
DBusMessage *msg = dbus_message_new_method_call(OBEXD_DBUS_CLIENT_SVC,
OBEXD_DBUS_CLIENT_PATH,
OBEXD_DBUS_CLIENT_IFC,
OBEXD_DBUS_CLIENT_CREATE);

pending = (dbus_async_call_t *) malloc(sizeof(dbus_async_call_t));
if (pending)
{
DBusPendingCall *call;
char *context_address = (char *) calloc(BTADDR_SIZE,
sizeof(char));
strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
pending->env = env;
pending->user_cb = onCreateSessionComplete;
pending->user = context_address;
pending->nat = nat;
dbus_bool_t reply = dbus_connection_send_with_reply(nat->conn,
msg,
&call,
10*1000);

/external/obexd/client/main.c (handle from obex-client)

static DBusMessage *create_session(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
if (session_create(source, dest, target, create_callback, data) == 0)
return NULL;

/external/obexd/client/session.c (use rfcomm to connect with specific channel )

int session_create(const char *source,
const char *destination, const char *target,
session_callback_t function, void *user_data)
{
if (session->channel > 0) {
err = rfcomm_connect(&session->src, &session->dst,
session->channel, rfcomm_callback, callback);

android_server_BluetoothFtpService.cpp (we get the callback!)
 
static void onCreateSessionComplete(DBusMessage *msg, void *user, void *nat_cb)
{
char* c_address = (char *)user;
JNIEnv *env = NULL;
nat->vm->GetEnv((void**)&env, nat->envVer);
jstring address = env->NewStringUTF(c_address);

env->CallVoidMethod(nat->me,
method_onCreateSessionComplete,
obj_path,
address,
is_error);

BluetoothFTPClient.java (handle from Bluetooth UI)

public void onCreateSessionComplete(boolean isError) {
Message msg = Message.obtain();
msg.what = TYPE_CREATE_SESSION_COMPLETE;
Bundle b = new Bundle();
b.putBoolean("isError", isError);
msg.obj = b;
mHandler.sendMessage(msg);
}


RemoteFileManagerActivity.java (device is connected, going to refresh panel to display Remote file system)

public void onCreateSessionComplete(boolean isError) {
mSessionCreated = !isError;
hideBusy();
if (mFTPClient != null) {
if (isError == false) {
mContext.onServerConnected();
goHomeDir();
} else {
String szStr = getResources().getString(R.string.ftp_connect_failed, mFTPClient.getName());
Toast.makeText(this, szStr, Toast.LENGTH_LONG).show();
}
}
updateServerStatus();
refreshDirectoryPanel();
}