Bluetooth provides the easiest way to create Ad-hoc networks for portable
devices. The requirements for Bluetooth programming are more than just the API.
There are two basic things that you need. One is the programming environment,
which could be Java or .NET based. And two, you need a hardware toolkit,
specific to the device you want your program to work on. e.g. you could create a
generic Bluetooth program for say, file transfer, but if you want it to work on
a Nokia phone, then you'll need to get a Nokia toolkit for it. Likewise, for
other devices. In this series of articles, we will look into generic programming
using the JSR-82 reference implementations. The JSR-82 is the Java community
process for standardizing a specification for Bluetooth programming using Java
APIs.
The Bluetooth APIs
It is a non-proprietary implementation and concentrates on application
development only. In other words, the APIs abstract the underlying complexities
of the Bluetooth protocol and services stack for developers. This API consists
of two packages-the Bluetooth Core API (optional) and the OBEX (Object
Exchange) API. OBEX is a transport independent API, which can be used without
the core API being Bluetooth necessarily. The respective packages are 'javax.bluetooth'
and 'javax.obex'.
|
A point to note here is that these APIs do not implement the Bluetooth
Specification. They provide Bluetooth capabilities to Java ME enabled devices.
Getting started
There are quite a few toolkits available for Bluetooth programming as per
JSR-82. There is also a selective list of devices that are enabled for these
capabilities. In this first part of the series, we will develop a 'Hello
World!' example. We will be using the Net Beans IDE along with Net Beans
Mobility extension for demo. The Java ME WTK implements both the JSR APIs, so it
will suffice our needs. Our app will contain a Bluetooth client and a server.
These are basically two Bluetooth devices, out of which one (client) will query
the other (server) for Bluetooth services, and on being run successfully, the
client will send a 'Hello World' message to the server.We've given the
source code on this month's PCQ Xtreme DVD.
The client side
We will first implement a simple MIDlet that initiates a background thread when
started. The Client Java Class for this looks like the following:
public class HelloClient extends
PCQBluetoothMidlet {
...
public void run() {
Form f = new Form("Client");
f.addCommand(new Command("Exit", Command.EXIT, 1));
f.setCommandListener(this);
Display.getDisplay(this).setCurrent(f);
}
The first step in programming the application is 'Stack Initialization'.
It initializes the Bluetooth stack for controlling the device. This
initialization consists of a number of steps including initialization of BCC
(Bluetooth Control Center) which is left for the vendor to implement and, hence,
is a vendor specific step whose code differs accordingly. Next step is 'Device
Management'. The API has two classes for the purpose viz. LocalDevice and
RemoteDevice. They provide device management as per the Generic Access Profile.
The class LocalDevice is used to obtain a reference to the device running the
client application using the static method 'getLocalDevice()'. The method
retrieves information such as type of local device and the services it offers.
After we receive the reference to this device, we can extract its address and
name using the following code snippet.
LocalDevice local = LocalDevice.getLocalDevice();
String addr = local.getBluetoothAddress();
String name = local.getFriendlyName();
Next step is 'Device Discovery' wherein the local device finds out other
Bluetooth devices in its range and gains access to its capabilities. This
provides the 'DiscoveryAgent' class and 'DiscoveryListener' interface
for this purpose.
The discovery agent can retrieve a list of devices in three ways all of which
use static methods. First uses startInquiry() method that puts the device in
inquiry mode, and event listeners defined in the application then respond to the
inquiry-events. The 'deviceDiscovered()' is called when a device is
discovered and handles the process following and 'inquiryCompleted()' is
invoked when the inquiry completes successfully or unsuccessfully. The
getDiscoveryAgent() method gives a handle to the 'DiscoveryAgent' object.
DiscoveryAgent agent = local.getDiscoveryAgent();
What follows is ServiceDiscovery and Service Registration. We will look into
their details in the upcoming parts of the series. For this application, we now
try to connect to the server using a non-authenticated, non-encrypted
connection. This is done as follows.
String conStr = agent.selectService(
new UUID("86b4d249fb8844d6a756ec265dd1f6a3", false),
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
The selectService() method performs the service discovery task here, and
takes UUID (the security policy used) which is indicated using any of the fields
ServiceRecord.NOAUTHENTICATE_NOENCRYPT that we have used, and ServiceRecord.
AUTHENTICATE_ENCRYPT which means an encrypted service that requires
authentication. The last parameter is a Boolean variable indicating if this
client must be the master of the connection. Its value is 'true' if the
client must is master; and 'false' if the client can be the master or the
slave. Next we open a connection to the server using StreamConnection,
Connector, and OutputStream classes if the service is found successfully on the
server it will send 'Hello World!' message to it.
The server
The server implementation also follows the same steps. The only difference is
that the tasks to be performed in a few steps are different. These include the
following:
if (!local.setDiscoverable(DiscoveryAgent.GIAC))
{
f.append("Failed to change to the " +
"discoverable mode");
return;
}
StreamConnectionNotifier notifier =
(StreamConnectionNotifier) Connector.open("btspp://localhost:" +
"86b4d249fb8844d6a756ec265dd1f6a3");
StreamConnection conn = notifier.acceptAndOpen();
...
}
Device discovery is facilitated by providing the “btspp” protocol URL
that uses the same UUID as of client. The StreamConnectionNotifier class
handles the connection opening with the 'openAndAccept() method to complete
the connection and accept the string message. For running the
examples, you can use the emulators provided in WTK or the free emulator by 'Rococo
Software' available at www.rococosoft.com. For device
specific purposes, check the toolkits made available by various vendors.
In the upcoming parts of the series, we will take some advanced topics like
file transfers and messaging.