20. Bluetooth and OBEX

BLUETOOTH is short-range wireless technology, good for connecting mobile phones, headsets, keyboards, mice, computers, and other devices together. Many modern mobile phones have Bluetooth hardware, as you have probably noticed by the proliferation of people walking around with those mondo tomorrow Bluetooth headsets tucked in their ears.

JSR 82 defines the Bluetooth API and the OBEX API. The Bluetooth API lives in javax.bluetooth, while the OBEX API is in javax.obex.

OBEX stands for OBject EXchange. It is a standard for moving data between two endpoints. It is independent of the underlying network transport, so it can run on top of a Bluetooth connection or an infrared data connection.

The Bluetooth and OBEX APIs defined by JSR 82 are entirely independent, but OBEX frequently runs on top of a Bluetooth connection. Nevertheless, there are devices out in the world today that support the JSR 82 Bluetooth API but not the JSR 82 OBEX API. For a list of devices that implement one or both JSR 82 APIs, see here:

Image

MSA devices, if they have Bluetooth technology, are required to support both the Bluetooth API and the OBEX API. If the device supports infrared communication or sockets, the OBEX API might be usable over these transports, but it’s not required.

Bluetooth is a deep subject all by itself, and the JSR 82 specification does not hide much of its complexity. This chapter teaches you the fundamentals of Bluetooth communication but does not attempt to cover the Bluetooth and OBEX APIs in meticulous detail. For a thorough treatment of JSR 82, consult the specification.

20.1. Control Your Own Bluetoothiness

The device upon which your MIDlet is running is represented by LocalDevice. Retrieve an instance like this:

Image

Like most methods in the Bluetooth API, this one can throw BluetoothStateException if something goes wrong. LocalDevice has methods for finding the device’s name, finding the device’s address, determining if the Bluetooth system is turned on, and more.

One useful thing you can do with LocalDevice is retrieve a DiscoveryAgent, which knows how to find other Bluetooth devices and services.

20.2. Finding Other Bluetooth Devices and Services

Having Bluetooth is great, but it won’t do you any good unless you have more than one Bluetooth device.

Discovery is how Bluetooth devices find each other. There are two types of discovery in the Bluetooth API. Device discovery lets you find other Bluetooth devices in your immediate vicinity, while service discovery is how you find interesting services on specific devices.

Bluetooth devices can control whether or not they are discoverable using LocalDevice. If you want other devices to be able to find you, do something like this:

Image

There are two kinds of discoverable. GIAC means that the device will be discoverable indefinitely, and LIAC means the device will be discoverable for some period of time, typically one minute. The exact amount of time is configured outside the scope of the Bluetooth API.

To find other devices, tell the Bluetooth API to start searching and then sit back while the listener object is notified about devices or services.

The listener object is an implementation of DeviceListener. The name is a little misleading, because the listener receives notifications of both device and service discovery.

To kick off device discovery, use the DiscoveryAgent you can get from LocalDevice. Call its startInquiry() method to get the search started.

Image

The DiscoveryListener interface has four methods, two for device discovery and two for service discovery.

Whenever a device is found, the listener’s deviceDiscovered() method is called. When the device is done with discovery, inquiryCompleted() is called. Each device found is passed to deviceDiscovered() as a RemoteDevice.

Once you find devices, you can kick off a search for specific services by calling another DiscoveryAgent method, searchServices(). Services are represented by a universally unique identification number (UUID). The javax.bluetooth. UUID class encapsulates a UUID. You can create one by specifying a 128-bit hexadecimal number as a string.

Image

searchServices() looks on the specified device for services that match the supplied list of UUIDs. The first argument to searchServices() is an array of field identifiers if you want to retrieve the values of special fields about the services. In this case, passing null means that just the default field values will be retrieved.

Any matching services are passed to the DiscoveryListener’s servicesDiscovered() method as ServiceRecord objects. When the service search is done, serviceSearchCompleted() is called.

The coolest thing you can do with a ServiceRecord is get a connection string that will hook you up with the given service. Call getConnectionURL() to retrieve the connection string. You can toss it over to Connector.open() to open up a client connection to the service.

In summary, the ultimate result of device and service discovery is ServiceRecords, which provide connection strings for hooking up clients to servers.

20.3. Cheap Shots

Searching for available services is kind of complicated. There are two simplifications of the process that you might consider.

The first is to ask the local device if it remembers talking to any Bluetooth devices in the past. The method to use is retrieveDevices() in DiscoveryAgent. Pass in PREKNOWN or CACHED to retrieve an array of RemoteDevices.

The second shortcut is to ask DiscoveryAgent to find a service with a given UUID. Call selectService() with the UUID, and DiscoveryAgent will return a connection string with the very first matching service that it finds. selectService() is a blocking method, which means it won’t return until it finds a matching service or gives up. This is different from the behavior of startInquiry() and searchServices(), both of which kick off a search in a different thread and return directly.

20.4. Making a Client Connection

The Bluetooth API supports two types of connections (not counting OBEX, which is covered later). The first type is a packet protocol with the catchy name L2CAP. It is roughly analagous to IP datagrams. A connection string for L2CAP begins with btl2cap://, with bt standing for Bluetooth.

The second type is RFCOMM, which is a stream-based protocol, roughly analogous to TCP. Connection strings for RFCOMM connections begin with btspp://, where spp stands for Serial Port Protocol, another name for RFCOMM.

Most of the time, though, you won’t create your own connection strings. Instead, you will retrieve a connection string from the ServiceRecord of whatever service it is to which you want to connect. Once you’ve gotten the connection string from ServiceRecord’s getConnectionURL() method, it’s mostly straightforward GCF programming from there.

In the btspp case, in particular, everything is gravy once you’ve got the connection string. You call Connector.open(), as usual, and get back a StreamConnection. From there you can get input and output streams and read and write as much as you like. The BlueChew example, later in this chapter, shows how to set up btspp connections and send and receive data.

The case for btl2cap is a little more complicated. Connector.open() returns an L2CAPConnection, and you use its send() and receive() methods to send and receive packets of data. You have to make sure you observe the limits on the size of incoming and outgoing packets returned from getTransmitMTU() and getReceiveMTU().

20.5. Setting Up a Server

Running a Bluetooth service is accomplished through the GCF. Pass an appropriate connection string to Connector.open(). You’ll get back an object that can be used to field incoming connections.

For btl2cap connections, use a connection string like this (split into two lines for readability):

Image

The UUID of the service is a lot like a port number, a very long port number. The name parameter is optional.

Connector.open() will return an L2CAPConnectionNotifier. You can catch incoming connections by calling its acceptAndOpen() method, which blocks until a client connects. Then it returns an L2CAPConnection that can be used to exchange data with the client.

btspp server connections are, again, easier. Here is an example btspp server connection:

Image

For this type of connection string, Connector.open() returns a StreamConnectionNotifier. Its acceptAndOpen() method blocks until a client connects, then returns a StreamConnection that can be used to communicate with the client.

In either case (btl2cap or btspp), the service cannot be discovered by clients until the server is waiting on the acceptAndOpen() method.

20.6. Authorization and Encryption

Bluetooth supports several security mechanisms:

  • A device can verify the identity of another device, a process called authentication.

  • Two devices can encrypt data that passes between them.

  • A device can ask the user for authorization to perform an action.

These mechanisms are outside the scope of the Bluetooth API, but you can request one or more of these options for connections. In general, you do so by adding options to the connection string. For example, to create a server that wants encrypted connections, you could use this connection string:

Image

On the client side, you could create a connection string from a ServiceRecord and request authentication and encryption like this:

Image

20.7. What about OBEX?

OBEX can be layered on Bluetooth, sockets, or an infrared connection. It is a client/server protocol for moving files between devices.

The client side of an OBEX conversation is represented by ClientSession. The exact form of the connection string depends on the transport under OBEX. For OBEX over infrared, do something like this:

Image

You can add hint bits, which are described in specifications listed in the References section of the JSR 82 specification. For an example of the use of hint bits, consult the Sun Java Wireless Toolkit’s ObexDemo application, in the ObexImageSender class.

For OBEX over sockets, just specify the server address and port number. If you leave off the port number, the default is 650.

Image

Finally, OBEX can run over a Bluetooth RFCOMM connection. In this case, things work as usual: clients discover available services and use the connection strings from the service records to make a connection. The connection string starts with btgoep, which stands for Bluetooth Generic Object Exchange Protocol.

Regardless of the underlying transport and connection string, an OBEX connection is represented by a ClientSession:

Image

Clients connect to servers and send a sequence of commands. ClientSession has methods that correspond to the available OBEX commands: connect(), put(), get(), setPath(), delete(), and disconnect().

A client command is answered with a server response. In this respect, OBEX resembles HTTP. Command requests and server responses are composed of headers, represented in the OBEX API as HeaderSet.

Most methods in ClientSession take a HeaderSet argument representing the command and return a HeaderSet representing the server response. However, get() and put() are slightly more complicated. Each returns an Operation that can be used to work with streams for sending and receiving data. Operation is a subinterface of ContentConnection, which means you can retrieve input and output streams from it.

On the server side, connection strings also depend on the underlying transport. For infrared, do something like this:

Image

Again, hint bits can be used. See ObexDemo’s ObexImageReceiver class for an example.

For OBEX over sockets, just leave off the address. Add a port if you don’t want the default of 650.

Image

For OBEX over Bluetooth RFCOMM, use localhost and the UUID of the service you are providing, just as you would do with a regular RFCOMM server.

Image

Whichever connection string you use, Connector.open() will return a SessionNotifier.

Image

Now you sit in a loop and call acceptAndOpen(), which blocks until a client makes a connection. However, the acceptAndOpen() method in the OBEX API takes a ServerRequestHandler argument that does all the work. In fact, even though acceptAndOpen() returns a Connection, you won’t do anything with it.

The methods in the ServerRequestHandler get called whenever the client tries to send a command. For example, ServerRequestHandler’s onConnect() method is called when a client calls connect() on its ClientSession.

Except for onGet() and onPut(), the other on- methods in ServerRequestHandler take two HeaderSet arguments, one for the client request and one for the server response. Those of you who have done any server-side programming will immediately recognize the parallel to servlets.

onGet() and onPut() are a little different. Each receives an Operation argument that can be used to directly manipulate the streams between client and server.

20.8. Don’t Forget the Push Registry

To make your applications useful even when they are not running, put an entry in the push registry. This works just as you’d expect. You map the server connection string to your MIDlet class name in the push registry. The device takes care of making sure that the corresponding Bluetooth service is discoverable. When clients attempt to connect, your MIDlet is launched to handle the connection.

20.9. Permissions for Bluetooth and OBEX

Incoming and outgoing network connections are sensitive operations with regard to security. The following permissions apply to Bluetooth and OBEX connections:

Image

20.10. The BlueChew Application

The BlueChew application demonstrates how to make devices communicate over Bluetooth. It includes a server, a discovery object, and a client. Devices running BlueChew can find and send short text messages to other devices running BlueChew. The whole application is too long to show here, but you can get the full source code from the book’s Web site:

http://kickbutt.jonathanknudsen.com/download.html

To use BlueChew, run the emulator more than once. Each instance of the emulator will find the other emulators and show them in the list of destinations, as shown in Figure 20.1.

Figure 20.1. Other devices found via Bluetooth discovery

Image

Here, two other running emulators were found. They are listed by their friendly name (which is Wireless Toolkit in both cases) and their Bluetooth addresses. Normally, you probably shouldn’t show Bluetooth addresses to a user, but I included them because the friendly names of all the emulator instances are the same.

You can check off all the devices to which you want to send a message. Edit the message itself and choose Send. BlueChew uses the connection strings of the corresponding ServiceRecords to make client connections and send the message.

On the receiving end, BlueChew accepts incoming connections, reads the message, and shows it on the screen, as in Figure 20.2.

Figure 20.2. A message is received.

Image

The application is divided into three main classes.

BlueChewMIDlet provides a user interface and coordinates the other objects. It also handles (in the run() method) sending the message to any checked destinations.

Image

Image

Image

Image

BlueChewFinder encapsulates discovery. It implements the DiscoveryListener interfaces. The device discovery is kicked off in start(). For each device found, service discovery is initiated. BlueChewFinder keeps two Vector lists of service records. Each time a device and service discovery cycle completes, the reconcile() method updates the main list on the basis of the latest findings.

During this process, reconcile() calls methods in BlueChewMIDlet to notify it to add or remove items from the list of destinations.

Image

Image

Image

Image

BlueChewServer is the service itself. Its run() method obtains the StreamConnectionNotifier and loops around on acceptAndOpen(). Client connections are handled immediately.

Image

Image

Image

Two small utility classes round out BlueChew. Neither is presented here, but you can download and examine the source code if you wish. First, BlueChewService is a compact representation of a ServiceRecord. It holds a name, an address, and a connection string.

The last class, Log, keeps track of a list of messages. It is useful for debugging. It includes a method that returns a Form with all the messages. The Form is good for showing debugging messages on a real device, where there is no console to examine.

If you want to explore further, there are plenty of possible improvements you could make to this application. Here are a few.

  • BlueChewFinder keeps a list of devices that are currently discoverable. Most devices are discoverable only for a short period of time but are still available for communication even when they are no longer discoverable. A more realistic implementation would keep previously discovered service records in the list and continue to display them in BlueChewMIDlet. If BlueChewMIDlet attempted a connection and was unsuccessful, the service record could be removed from the list on the assumption that the corresponding device is no longer available.

  • BlueChewFinder could also use cached and preknown devices in its discovery search. See retrieveDevices() in DiscoveryAgent for details.

  • Incoming messages are shown immediately. If a new message arrives while the user is still reading a previous message, the new message is shown and the previous message is lost forever. A more graceful way to handle this would be to keep a list of incoming messages and show them one by one, with each one dismissed by an explicit action from the user.

20.11. Summary

Bluetooth is wireless communication technology for short distances. The Bluetooth API allows your application to discover other Bluetooth devices and communicate with them. Methods in DiscoveryAgent start searches for devices and searches for services on specific devices. You supply a DiscoveryListener that is notified when devices and services are found. A ServiceRecord represents a service and can supply a connection string for hooking up a client and server. Standard GCF and stream programming are used for btspp connections. Servers are identified by a UUID, which is part of the server and client connection strings.

The OBEX API provides access to the OBEX protocol, which can run on top of Bluetooth, infrared, or socket connections. OBEX uses a client/server model that is similar to HTTP servlets. It is useful for exchanging files between devices.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.113.55