Update: March 2004
Author: Ben Hui I have
published a Bluetooth developerment article on Java Developer
Journal (Feb 2004, Volume 9 Issue 2). The article titled Go
Wild Wirelessly with Bluetooth. In this article,
I developed an example chatting application, BlueChat, that
allow user chats over Bluetooth connection. You can download complete
source codes of this example application and obtain additional information.
The BlueChat source code as discussed in the article
works on Rococosoft
Impronto Bluetooth simulator. However it doesn't work on a real
phone because it was developred before any JABWT phone released.
I have updated the source code since the release of Nokia 6600.
The source code provided here is fully tested with Nokia 6600 phone
and it works.
If you wanted to compile the BlueChat example using
SUN J2ME WTK 2.1, please read this
tutorial.
Change history:
March 8, 2004
- Repackage it to work with SUN J2ME WTK 2.1.
March 7 2004
- Remove reference to non-javax.bluetooth classes.
It should now compiled with standard API.
- Verify to run on Nokia Series60 J2ME SDK 2.0
emulator
- Verify to run on Sony Ericsson P900 (Firmward
R3C)
Additional notes:
In this example application, you will learn the
following bluetooth development skills
- Dervice and service discovery
- Writing a Bluetooth server using Serial Port
Profile
- Writing a Bluetooth client using Serial Port
Profile
- Exchanging data continously between client and
server
- Working around real phone limitation and bugs
You must have two JABWT phones (Nokia 6600 or SE
P900) to see chatting in action. Following these steps if you run
BlueChat in Nokia 6600:
- Start one BlueChat, enter a name, select Chat
from menu.
- **Wait**until
the it finishs all the Bluetooth work and say "You are
ready to start chatting" on the screen.
- Then start BlueChat on another Nokia 6600 phone,
enter a different name, select Chat from menu.
- **Wait**
until it find the first Nokia 6600 and the first Nokia 6600 will
prompt you for accepting the connection.
- When you see "You are ready to start
chatting" on the second phone, you can start writing
something.
- Select Write from menu, type something
and select Send.
- You can see the message appear on the other phone.
You can turn on logging if you select Chat (Debug)
from menu when you logon to BlueChat. There will be a lot of logs
on the screen and you can scroll through the logs by using the joystick
on the phone.
I hope to provide an UML diagram that explains the
interaction between objects and some screenshots of execution as
well. Come back later. Email
me here if you want me to notify you when that is available.
And as always, any feedback
are welcome.
If you are using P900, you are welcome to try this
sofware and see if it works the same way. Please inform
me the result of your experience!
Known Issues:
I found that exiting BlueChat is not very stable.
Sometimes I got IOException with Symbian error (-32). I am not sure
what that means. Relogin seem to have some problems, too. If I exit
and enter again, it will get connected, but cannot exchange message.
If you found bugs in my code, You are welcome to send the patch
back to me and I will update it online.
Some interesting foundings:
Bugs, limitation, more bugs, more limitation. The
road of developing robust Java Bluetooth application is still very
very far away. Here are just some of the interesting bugs or limitation
or my lack-of-knowledge that I found during the development of BlueChat
application. Let's hope no body need to run into the same problems
again.
Problem |
On Nokia Series60 SDK emulator, setDeviceServiceClass()
doesn't seem do anything. |
Resolution |
Just don't use it in emulator. |
Bug
factor |
 |
|
Problem |
On SonyEricsson P900, BlueChat just crash
on firmware R3A024. It appears to have security exception
just before it crashes. |
Resolution |
Upgrade to firmware R3C006. The phone
will prompt you to allow the Bluetooth operation. (bluetooth.server).
Some developers suggest to sign the MIDlet to avoid the
crash as well. |
Bug
factor |
 |
|
Problem |
Your application UUID cannot take the
form of 0x????. I originally use UUID(0x6600) as my application
service class ID, but i never able to discover my service
(with searchServices()) using this UUID. At the end, I
found that UUID.equals() always return false if I use
0x6600 as the ID. As a result, the system never able to
match my service during service discovery. |
Resolution |
Use a 32-length string to represent your
UUID and initialize it using new UUID( uuid_string,
false ). In BlueChat, it is private
final static UUID uuid = new UUID("102030405060708090A0B0C0D0E0F010",
false);
|
Bug
factor |
 |
|
Problem |
Nokia 6600 phone says bluetooth.connected.devices.max
is 7, but it is actually 2. If you try to make more than
2 connections, you will get IOException with the message
"Is Busy". That means the Bluetooth system is
busy and unable to take any more connetion request. Oh..
did I mention that device discovery already take up one
connection? |
Resolution |
Don't try to develop a 4-players games
:) May be you can get around
by closing existing connection before making a new one.
Or wrap around the bluetooth connection with you own
connection pool, so you can emulate multiple connections.
If you have better resolution, email
me here. |
Bug
factor |
 |
|
Problem |
Package javax.obex and any OBEX protocol
is not supported. It is a well documented limitation. |
Resolution |
Write your own OBEX layer on top of SerialPort
Profile. See if this
article helps you. |
Bug
factor |
 |
|
Problem |
You can only perform one service discovery
at a time. This is not a bug because bluetooth.sd.trans.max
does return '1' correctly. However, it does cause a lot
of trouble in the programming side. If you attemp to perform
multiple searchServices() concurrently, you will get IOException
with the message "Is Busy". |
Resolution |
You will need to wait until one discovery
is completely finished, before searching on the next device.
See BlueChat source code for example of doing this. And
remember not to block the callback method in DiscoveryListner,
otherwise the entire Bluetooth layer will hang. See how
I schedule a timer to avoid blocking in the code (NetLayer.java,
Listener.inquiryCompleted() ) |
Bug
factor |
 |
|
Problem |
You need to make a new instance of DiscoveryListener
for every device or service search. This is a limitation
of Rococosoft Bluetooth simulator. I am not sure
if the phone has the same limitation as well. If you use
the same instance repeatedly, you will not get any callback
after the first one. |
Resolution |
Just create a new instance of DiscoveryListener
for each invoke of startInquiry() and searchServices().
I create a inner class Listener (NetLayer.java) for this
purpose. |
Bug
factor |
 |
|
Problem |
If there are multiple matches fo ryour
service discovery, your listener servicesDiscovered()
is invoked multiple times with one search recrod each,
instead of invoked once, with multiple service records. |
Resolution |
Prepare your servicesDiscovered() to
be invoked multiple times. In the other words, this method
need to return quickly and cannot make another additional
connection because there isn't enough connection available
when service discovery is not finished. In BlueChat example,
my code remembers the service recrod in a Hashtable. Then
in serviceSearchCompleted(), I process the discovery service
recrod and start making connection to remote service. |
Bug
factor |
 |
|
Problem |
RemoteDevice.getFriendName(true) will
give you IOException. This because the 'true' tell the
Bluetooth layer to query the friend name from the remote
device. It does it by making a NEW connection to the remote
device. Remember we don't have enough connection? If you
are still have a active device searchs going on, you will
not have the extra connection to query the friend name. |
Resolution |
Use RemoteDevice.getFriendName(false).
This will give you the name from the last query. |
Bug
factor |
 |
|
BlueChat Service Record:
Wonder how does the BlueChat service record really
looks like? The following service registration code will produce
the following service record. This information is recorded using
our JABWT Browser.
|
// Create a server connection object, using a
// Serial Port Profile URL syntax and our specific UUID
// and set the service name to BlueChatApp
server = (StreamConnectionNotifier)Connector.open(
"btspp://localhost:" + uuid.toString() +";name=BlueChatApp");
// Retrieve the service record template
ServiceRecord rec = localDevice.getRecord( server );
// set ServiceRecrod ServiceAvailability (0x0008) attribute to indicate our service is available
// 0xFF indicate fully available status
// This operation is optional
rec.setAttributeValue( 0x0008, new DataElement( DataElement.U_INT_1, 0xFF ) );
// Print the service record, which already contains
// some default values
Util.printServiceRecord( rec );
// Set the Major Service Classes flag in Bluetooth stack.
// We choose Object Transfer Service
rec.setDeviceServiceClasses(
BluetoothConstants.SERVICE_OBJECT_TRANSFER );
|
|
Attribute |
Value |
URL |
btspp://xxxxxxxxxxxx:4;authentication=false;encrypt=false;master=false
where xxxxxxxxxxxx is a phone Bluetooth
address. |
ServiceName |
BlueChatApp |
ServiceRecordHandle |
100663552 This
may be different from phone to phone. |
ServiceClassIDList |
102030405060708090A0B0C0D0E0F010
Our Service unique UUID. |
ServiceClassIDList |
SerialPort (UUID(0x1101)) |
ProtocolDescriptorList |
L2CAP (UUID(0x0100)) |
ProtocolDescriptorList |
RFCOMM (UUID(0x0003)) |
ProtocolDescriptorList |
4 This
is the RFCOMM port number. Also appear on the URL. |
LanguageBasedAttributeID |
28261 |
LanguageBasedAttributeID |
27136 |
LanguageBasedAttributeID |
1 |
ServiceAvailability |
255 (0xFF) |
|