Developing Bluetooth Applications in Java: Part 2

It's impossible to predict all of the connectivity options that an end user will require for their Bluetooth device. Therefore, designers must provide a platform that end users can customize at the software profile layer.

To make this customization come to life, Bluetooth designers need to provide an open API that provides a standard interface for software developers to build profiles that can be ported to a host of Bluetooth systems. Answering this call, the Java community has produced the JSR-82 specification, also called Java APIs for Bluetooth wireless technology (JABWT), which defines a standard API for Bluetooth devices that support J2ME.

This is the second part in our look at the JSR-82 specification. In Part 1, we looked at the key decisions that went into defining the spec as well as at the RFCOMM and device discovery layers provided by JSR-82. Now, in Part 2, we'll further the discussion by looking at the service discovery layer and at the OBEX API defined in JSR-82.

Service Discovery
In Part 1, we spent some time discussing device discovery. Once device discovery has identified a list of nearby Bluetooth devices, the next step for a Bluetooth application is to determine what applications or services those devices provide.

Service discovery is the process of using the Bluetooth service discovery protocol to find services of interest on nearby Bluetooth devices. A server application advertises the service it offers through its service record. A service record describes a Bluetooth service using a set of service attributes. Service attributes may specify how to connect to a service, the name of the service, a textual description of the service, and other useful information. A Bluetooth profile specification describes the contents of the service records used for that profile.

Service records are made visible to potential clients of a Bluetooth service through a process called service registration. The following sequence of actions illustrates service registration using JABWT for a Serial Port server application on Bluetooth device A:

A1: Call Connector.open(“btspp://localhost:UUIDx “). This creates a service record with a default set of attributes whose service class ID list includes UUIDx
A2: Optionally modify the service record created by Connector.open() to add additional service attributes.
A3: Invoke the acceptAndOpen() method. Nearby devices are now able to access the service record using the service discovery protocol.
A4: The acceptAndOpen() method then waits for a client to connect to the server to access the service using the RFCOMM protocol.

Step A1 refers to a universally unique identifier (UUID) and a service class ID. A UUID is a 128-bit sequence. The Bluetooth specification assigns meanings to many UUIDs. Other UUIDs are given user-defined meanings. In Step A1, UUIDx identifies the type of service being offered; that is, the service class ID. Each Bluetooth profile defines one or more ServiceClass UUIDs. If a client wants to find the service records that use this particular service class ID, it can use the service discovery protocol to search for UUIDx .

The JSR-82 approach to service registration outlined in Steps A1-A4 above provides the first standard API for Bluetooth service registration. The following sequence of actions illustrates service discovery using JAWBT for a serial port client application on Bluetooth device B:

B1: Perform a device inquiry to locate nearby Bluetooth devices (see the previous article for a description of device inquiry).
B2: The device inquiry finds Bluetooth device A (and possibly others).
B3: Call the searchServices() method to use the service discovery protocol to find service records on device A that contain UUIDx . The searchServices() method takes an argument that specifies the list of service attributes that should be retrieved from every service record that contains UUIDx .
B4: As service records that contain UUIDx are returned from device A, the JSR-82 implementation sends them to the client's DiscoveryListener via the servicesDiscovered() method.
B5: Client applications typically define the servicesDiscovered() method to check the service attributes of each service record to determine if this service record describes a service that this client can interact with.
B6: Once an appropriate service record is found, the client has the option to terminate the service search by calling the cancelServiceSearch() method.
B7: When the service search is complete, the JSR-82 implementation sends the serviceSearchCompleted() method to the client's DiscoveryListener. An argument to this method indicates how the service search ended; e.g., all matching service records have been retrieved, or the search was terminated by the client, etc.

At this point the client will typically move from a service-discovery phase into service-use phase. The client can use the method getConnectionURL() to determine the connection string that should be used as an argument to Connector.open() to connect to this service using the RFCOMM protocol. The details of this connection string are determined by the information in the service record.

The JABWT approach to service discovery outlined in Steps B1 to B7 above is based on the service discovery application profile defined in the Bluetooth profiles specification. The sequence of actions described above in Steps A1-A4 and B1-B7 are also based on the RFCOMM communications layer. The only difference for L2CAP or OBEX communications over Bluetooth wireless technology would be in the argument to the server's Connector.open() call. Instead of a connection string starting with “btspp” for the serial port profile, the connections strings would begin with “btl2cap” or “btgoep.” (Note: GOEP stands for generic object exchange profile).

The details of the service records created by the JSR-82 implementation differ depending on the communications protocol employed. However, the process of service registration and service discovery remains the same.

It's important to note that although the sequence of events above describe service discovery between two JSR-82 applications, JSR-82 applications are able to inter-operate with Bluetooth applications that do not use the proposed Java API. In fact, JABWT applications would not typically know whether the remote application is a JSR-82 application or not.

Defining the OBEX API
As stated in the previous article, the API defined in JABWT for the object exchange protocol (OBEX) is a separate Java package from the Bluetooth API. This was done because the OBEX protocol can be used over a variety of different transport protocols including IrDA, TCP, and RFCOMM. Because OBEX has this capability, the OBEX API may be used over RFCOMM, over IrDA and over TCP. The OBEX API may appear on a device with or without the Bluetooth API portion of JABWT.

The OBEX protocol is built on eight basic operations: CONNECT, SETPATH, GET, PUT, CREATE-EMPTY, DELETE, ABORT, and DISCONNECT. Every OBEX session begins with a CONNECT request from the client to the server. Every session ends with a DISCONNECT request from the client. Between the CONNECT and DISCONNECT requests, the client may send any number of SETPATH, GET, CREATE-EMPTY, DELETE, or PUT requests. The ABORT request is a special type of request that ends a PUT or GET operation before the operation is completed.

Within each OBEX packet, OBEX headers may be sent. The OBEX protocol specification defines a list of common headers. For example:

  • NAME — Specifies the name of the object
  • LENGTH — Specifies the length of the object
  • DESCRIPTION — A short description of the object

The OBEX specification defines how these common headers are encoded. For example, the NAME header must be a Unicode string. In addition to the common headers, the specification also defines 64 user-defined headers. The specification breaks these user-defined headers into four groups of 16 headers. Each group represents a different type of data. There is a group for Unicode strings, four-byte longs, single bytes, and byte arrays.

To make the OBEX API easier to learn, it was designed based on existing Java APIs. As shown in Figure 4 , the OBEX client API design is based on the combination of the ContentConnection interface and the DatagramConnection interface from the existing generic connection framework (GCF). GET, PUT and CREATE-EMPTY operations utilize the JSR-82 operation interface, which extends the ContentConnection interface.


Figure 4: The OBEX client API resulted from the combination of two connection types.

The CONNECT, SETPATH, PUT-DELETE, and DISCONNECT operations work similar to the DatagramConnection interface. To send a message using the DatagramConnection, a Datagram object must be created and used as the argument to the send() method of the DatagramConnection interface. Similarly, to send OBEX headers a HeaderSet object must be created and passed to the connect(), setPath(), delete(), or disconnect() method of the ClientSession interface.

As shown in Figure 5 , the OBEX server API combines concepts from the StreamConnectionNotifier interface and from the Java servlet API.


Figure 5: The OBEX server API was created by combining two well-known APIs.

The server API, like the client API, is based on the GCF. After creating a SessionNotifier object by calling Connector.open(), acceptAndOpen() is called with a ServerRequestHandler object as an argument. The ServerRequestHandler class is similar to the HttpServlet class. The ServerRequestHandler class defines methods for each type of OBEX request that a server may receive. In particular, the onConnect(), onDisconnect(), onPut(), onGet(), onDelete(), and onSetPath() methods are defined in the ServerRequestHandler class to handle CONNECT, DISCONNECT, PUT, GET, DELETE, and SETPATH operations respectively. Only those requests that a server wishes to respond to must be implemented by an OBEX server. The ResponseCodes class defines all the valid response codes that a server may send to a client.

OBEX Authentication
The OBEX API also provides a mechanism for OBEX authentication. OBEX authentication works via a challenge and response scheme using two OBEX headers. The OBEX API uses an API similar to the J2SE authentication API for OBEX authentication.

The OBEX API defines the Authenticator interface. When an AUTHENTICATION CHALLENGE header is received, the onAuthenticationChallenge() method is called. This method returns a PasswordAuthentication object containing the user name and password pair that will be sent in the response. When an AUTHENTICATION RESPONSE header is received, the onAuthenticationResponse() method is called. The shared secret or password is returned from the onAuthenticationResponse() method. The OBEX API implementation handles all the hashing of challenges/passwords and the validation of the authentication request.

The OBEX API also adds three new interfaces to the GCF (Figure 6) . The ClientSession interface is the interface returned from Connector.open() when an OBEX client connection string is provided. The SessionNotifier interface is returned from the Connector.open() method for OBEX server connections. Finally, the Operation interface is used to process PUT and GET requests. The Operation interface hides the back and forth nature of the PUT and GET operation.


Figure 6: Diagram illustrating the three new GCF interfaces provided by the OBEX API.

In addition to these new interfaces, the OBEX API also defines the HeaderSet interface. The HeaderSet interface encapsulates a set of OBEX headers. Most OBEX headers can be set and retrieved in a HeaderSet object via a setter method and a getter method. The OBEX headers not set within the HeaderSet interface can be set and retrieved using other methods.

The OBEX connection string is slightly different from other connection strings that are defined in the GCF. Because OBEX can be used with a number of different transports, the connection string needs to specify the transport protocol to use in addition to specifying the OBEX protocol. The connection string defined by the OBEX API for OBEX over TCP is:tcpobex://{target}

The {target} is the IP address and port number of the server. For a server connection, the {target} is just the port number.

When RFCOMM is the transport protocol, the connection string has the following pattern:
btgoep://{target}{parameters}

The connection string for OBEX over RFCOMM connections begins with btgoep:// because the GOEP is the realization of the OBEX protocol in the Bluetooth specification. The {target} is the Bluetooth address and RFCOMM channel number needed to establish a client connection. For server connections, the {target} is the localhost: keyword followed by the UUID of the service. All the valid {parameters} for RFCOMM are also valid for OBEX over RFCOMM (see the previous article for examples of {parameters}).

After calling Connector.open() with a client connection string, a ClientSession object is returned. A transport connection has been established by the call to Connector.open(), but an OBEX layer connection has not yet been established. To establish an OBEX layer connection, ClientSession.connect() must be called. Before closing the transport layer connection, ClientSession.disconnect() must be called to close the OBEX layer connection.

On the server side, the SessionNotifier object returned by Connector.open() is used to accept connections from clients by calling acceptAndOpen() on the SessionNotifier object. The acceptAndOpen() method takes a ServerRequestHandler argument and an optional Authenticator argument. A developer creates a new class that extends the ServerRequestHandler class and implements the methods for the type of requests the developer would like the server to handle. For example, onConnect() should be implemented for CONNECT requests and onGet() for GET requests. The call to acceptAndOpen() will not return until a client connects. The acceptAndOpen() method will return a Connection object representing the transport layer connection to the client.

Differences from Other OBEX APIs
The JSR-82 OBEX API is different from most existing OBEX implementations, which provide only a high-level interface to the protocol. For example, Palm developers have an API for OBEX that allows a user to send a business card or receive an appointment, but that API provides no control over how the data is sent. The Java OBEX API provides a lower-level interface.

While the OBEX API provides greater access to the OBEX protocol, the OBEX API does hide some of the details of the OBEX protocol from developers. The OBEX API handles all the translation of OBEX headers to their corresponding byte representation. The JSR-82 OBEX implementation also handles the conversion of PUT and GET requests into multiple packets. This allows an application to simply send the data while relying on the JABWT implementation to convert the data into multiple OBEX packets.

Wrap Up
Several mobile phones with JSR-82 implementations are expected to be introduced later this year. Bluetooth applications written using JABWT will run on all these devices. Of course, this is just a start. Once JSR-82 becomes widely deployed, designers can start providing more flexible platforms that end users can more easily configure to meet their individual connection needs.

Author's Note: These two articles are based on a forthcoming book called Bluetooth Application Programming with the Java APIs by the authors. The complete JABWT specification can be downloaded from http://jcp.org/jsr/detail/082.jsp.

Editor's Note: To view Part 1 of this article, click here.

About the Authors
C Bala Kumar is the specification lead for JSR-82. He leads the systems software team on wireless platforms for Motorola's Semiconductor Products Sector. Bala received his Masters in Electrical Engineering at The University of Texas at Austin and can be reached at .

Paul J. Kline is the maintenance lead for JSR-82. He works on software architectures for wireless platforms for Motorola's Semiconductor Products Sector. Paul received his Ph.D. from The University of Michigan and can be reached at paul.kline@motorola.com.

Timothy J. Thompson is the OBEX architect on the JSR-82 specification team. He works on software architectures for wireless platforms for Motorola's Semiconductor Products Sector. Timothy received his Masters in Computer Science at Texas A&M University and can be reached at .

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.