As innovation continues to expand in the mobile device field, and as companies integrate chips into more consumer devices (those not traditionally associated with computing), the opportunities to tie these devices together to create smart environments are increasing. In this article, see how a package based on the Bluetooth Java APIs lets these devices trade arbitrary text-based messages, and presents a simple, useful API to the client program.
Bluetooth is a maturing technology targeted at building personal area networks (PANs) and connecting devices with relatively low bitrate requirements cheaply and without wires. Bluetooth is available in multiple flavors (or classes) with varying power requirements (and proportionally varying range). These attributes make it ideal both for ad hoc peer-to-peer networking between mobile devices and for networking between static devices, such as living-room consumer electronics.
PANs solve different problems from LANs, WANs, and the Worldwide Web; PANs' problems are often driven by convenience. Perhaps the technology's most attractive uses are wireless mobile phone headsets and keyless car entry systems. Neither of these solutions require Bluetooth or any other wireless technology, but by providing a cost-effective solution that makes the standard of living just that much higher, you can forge new markets.
To tie together disparate collections of devices from multiple manufacturers, you need a simple, flexible method of communication. Technologies such as Object Exchange (OBEX) deal with the transmission of binary data, which, while crucial for many Bluetooth applications, is not ideal for implementing simple message protocols. Also, you need to work with the concepts of device and service discovery, which are essential to building ad hoc peer-to-peer connections between devices. The btevents package provides an easy, repeatable technique to enable simple textual communication between Bluetooth devices.
For example, consider that you might want your television volume to reduce automatically when the phone rings. Listings 1 and 2 outline how you can use the btevents package to enable the two devices to communicate without requiring a huge amount of boilerplate code in the client application.
First, take a look at the code running on the telephone.
Listing 1. Sample code for a telephone
package bluetooth.livingroom;
import com.ibm.btevents.*;
public class TelephoneMonitor extends MIDlet implements BTEventListener,
PhoneListener {
private BTManager btManager;
public TelephoneMonitor() {
btManager = new BTManager(this);
}
public void incomingCall(PhoneEvent event) {
btManager.sendMessage(
"bluetooth.livingroom.TelevisionMonitor",
"incomingCall:"+event.getCaller()
);
}
public void callEnded(PhoneEvent event) {
btManager.sendMessage(
"bluetooth.livingroom.TelevisionMonitor",
"callEnded:"+event.getDuration()
);
}
public void messageReceived(BTEvent event) {}
public void messageSent(BTEvent event) {}
public void devicesDiscovered(BTEvent event) {}
public void diagnosticMessage(BTEvent event) {}
}
|
The TelephoneMonitor creates a BTManager object and passes in a BTEventListener object. In this case, the telephone isn't interested in any of the events generated by the Bluetooth communication; for example, look at the TelevisionMonitor. The interesting code in this class is in the incomingCall() and callEnded() methods. These methods are called to handle a notional PhoneEvent, which provides basic details about the call, such as caller ID and call duration. The only necessary action for these methods is to notify the TelevisionMonitor by transmitting the relevant message using the BTManager object.
Now, take a look at the other side:
Listing 2. Sample code for a television
package bluetooth.livingroom;
import com.ibm.btevents.*;
public class TelevisionMonitor extends MIDlet implements BTEventListener {
private BTManager btManager;
public TelevisionMonitor() {
btManager = new BTManager(this);
}
private void reduceVolume() {
...
}
private void restoreVolume() {
...
}
private void displayMessage() {
...
}
public void messageReceived(BTEvent event) {
String message[] = event.getMessage().split(":");
if (message[0].equals("incomingCall")) {
displayMessage("Incoming call from "+message[1]);
reduceVolume();
} else if (message[1].equals("callEnded")) {
displayMessage("Call ended, lasted "+message[1]);
restoreVolume();
}
public void messageSent(BTEvent event) {}
public void devicesDiscovered(BTEvent event) {}
public void diagnosticMessage(BTEvent event) {}
}
|
Here the client code must simply act on the received messages. If it receives an incomingCall message, the code reduces the volume and displays the caller on screen. If it receives a callEnded message, the code restores the volume and displays the call duration.
In each code block above, you make assumptions about access to device-specific APIs and event mechanisms such as the TelephoneMonitor's PhoneEvent objects and the TelevisionMonitor's ability to put a message on the screen and control volume. These functions are highly device-specific and may not be consistent between devices of similar function. However, you can localize these device-specific functions to the device; you don't need the TV to be aware of, or understand, PhoneEvents. All you do need is for device manufacturers to recognize a logical connection between two devices and code accordingly. In this case, the logical connection is that the TV would like to know when the user is on the telephone.
Next, take a look at how the btevents package handles these messages.
The btevents package in detail
The btevents package is a layer around the Java Bluetooth API introduced in JSR 82 (see Resources for more information). It takes the complexity out of implementing a simple, textual protocol between devices. As the name suggests, information is delivered in the form of events. Because PANs can be very dynamic, with devices dropping into and out of range without notice, any software using them must follow an asynchronous design to prevent pauses and hangs, particularly those presenting GUIs to users. The event mechanism in the Java language is the perfect tool for such an environment.
Figure 1 shows the important elements of the btevents package.
Figure 1. Package overview
The TelephoneMonitor and TelevisionMonitor classes both created an instance of a BTManager class in their constructors, so you should start by showing what the BTManager class does (see Listing 3).
Listing 3. The
BTManager class
package com.ibm.btevents;
public class BTManager {
private BTTransmitter transmitter;
private BTReceiver receiver;
private BTDiscoverer discoverer;
public BTManager(BTEventListener listener) {
transmitter = new BTTransmitter();
transmitter.addBTEventListener(listener);
transmitter.start();
receiver = new BTReceiver();
receiver.addBTEventListener(listener);
receiver.start();
discoverer = new BTDiscoverer();
discoverer.addBTEventListener(listener);
discoverer.start();
}
public void startDiscovery() {
discoverer.searchForDevices();
}
public void sendMessage(String remoteName, String message)
throws DeviceNotFoundException {
transmitter.sendMessage(remoteName, message);
}
}
|
As you can see, this class creates and starts three separate threads, each one with a specific task. Implementing these functions inside threads eliminates any possibility of the client application locking up due to a connection dropping mid-transmission.
Each of these three threads performs a subset of the tasks necessary to enable two-way communication, as well as device and service discovery.
Now, take a look at the BTDiscoverer thread. This thread allows the client application to request a local search at any point, making search results available asynchronously using a BTEvent. The most important code is the API call to initiate a discovery session:
localDevice.getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, this);
This API call generates its own set of events, which requires the BTDiscoverer class to implement the DiscoveryListener interface. To keep the btevents interface clean and consistent, these events are handled internally. Any discovery-related events that need to be externalized are converted into BTEvents and fired by BTDiscoverer.
The BTReceiver class plays a role familiar to anyone who has written a server application using the Worker Thread design pattern (see Resources for more information). It creates a StreamConnectionNotifier object and listens on this object for incoming connections. When BTReceiver detects a connection, the class passes the StreamConnection object off to a worker thread to process the input and fire a message-received BTEvent to any registered BTEventListeners.
StreamConnections are not the only option for creating connections between two Bluetooth devices. In fact, there are three different connection types available, as shown in Table 1.
Table 1. Types of bluetooth connections
| Connection identifier | Expanded name |
|---|---|
btgoep | General object exchange profile |
btl2cap | Logical link control and adaption layer protocol |
btspp | Serial port profile |
First, btgoep transmits binary data, so it is not appropriate for string-based communication. btl2cap requires more legwork from developers, setting and working around maximum message sizes. However, btspp hides these complexities from the developer, so this is the logical connection choice for a project aimed at simplifying the developer's process.
The format of the connection URL passed to the javax.microedition.io.Connector.open(connURL) call decides the type of connection established:
protected static final String BT_PROTOCOL = "btspp";
protected static final String BT_ID = "1234";
server = (StreamConnectionNotifier)Connector.open(
BT_PROTOCOL + "://localhost:" + BT_ID);
The reference to localhost in the connection URL is also crucial, as this instructs the open() call to create a server connection. If you use a remote address, you create a client connection.
The BTTransmitter class, as the name suggests, deals with the transmission of messages to remote devices. Client applications can submit messages for transmission through the BTManager class. These are passed through to the BTTransmitter class and added to a message queue vector. The BTTransmitter thread monitors the queue and sends any messages it finds using the Connector class. To do this, it must retrieve the services from the remote target. This process is similar to device discovery.
localDevice.getDiscoveryAgent().searchServices(
null, filter, message.getRemote(), this);
After this search is complete, you can use the ServiceRecord obtained to create an OutputStream into which you can write the message:
out = Connector.openOutputStream(
record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false));
out.write(message.getMessage().getBytes());
fireMessageSentEvent(message.getMessage());
After the code transmits the message, it fires events to registered listeners to notify them of the transmission.
Due to the unreliable nature of PANs, the BTTransmitter thread will retry a limited number of times to transmit a message that fails. After five attempts, it assumes that the device is no longer in range and drops the message -- such is the nature of ad hoc networking!
Now comes the interesting work: making devices talk to each other! You need just a small amount of boilerplate code to get a device up and running with the btevents package. Listing 4 shows a simple pseudocode overview.
Listing 4. Using the
btevents package
import com.ibm.btevents.*;
public class MyBTClass implements BTEventListener {
...
private BTManager manager;
public MyBTClass() {
...
manager = new BTManager();
manager.addBTEventListener(this);
...
}
public void messageReceived(BTEvent event) {
...
}
public void messageSent(BTEvent event) {
...
}
public void devicesDiscovered(BTEvent event) {
...
}
public void diagnosticMessage(BTEvent event) {
...
}
public void errorMessage(BTEvent event) {
...
}
}
|
The attached package contains a simple com.ibm.btevents.ut.BTTestsMIDlet class, which implements the above in a simple J2ME MIDlet suitable for deployment on JSR82-enabled J2ME devices.
In this article, you've seen how you can add a Bluetooth transport layer to client applications without overburdening the client with boilerplate code and asynchronous messaging considerations. You can achieve a clean factoring of function and leave code simple to develop and maintain.
| Description | Name | Size | Download method |
|---|---|---|---|
| Source code for the btevents package | btevents-src.zip | 10KB | HTTP |
| A deployable MIDlet sample suite | btevents.jar | 13KB | HTTP |
Information about download methods
Learn
-
JSR 82: Java APIs for Bluetooth: Find more details on the APIs from the Java Community Process.
-
Concurrent Programming in Java: Design Principles and Patterns: This book by Doug Lea shows how to do more with multithreading in Java.
- EclipseME:
Download the extension to Eclipse.
- Wireless Toolkits: Find supported toolkits for EclipseME.
-
"Developing J2ME applications with EclipseME" (developerWorks, November 2004): This tutorial demonstrates how to develop J2ME applications using the Eclipse IDE, the open source EclipseME plug-in, and the Sun J2ME Wireless Toolkit.
- Java™Bluetooth implementation: Get more information on the JavaBluetooth Stack.
Discuss
- developerWorks blogs: Get involved in the developerWorks community!
Comments (Undergoing maintenance)






