HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

Go Wild Wirelessly with Bluetooth and Java
Developing portable Bluetooth apps

As a frequent visitor to J2ME and Bluetooth developer forums, I've noticed that one of the most frequently asked questions is "How do I get started with Bluetooth?" Despite its introduction in 1998 and a highly hyped year in 2001, Bluetooth application development remains hideous and challenging for lots of developers. Many experienced developers are looking for material that goes beyond a basic introduction and covers various aspects of real-life Bluetooth application development.

This article will take you to the next step of Bluetooth development and look at a newly developed specification that can assist Bluetooth developers in building applications rapidly. I'll discuss some important concepts of this specification and provide a walkthrough of a real Bluetooth application, BlueChat, that will cover some important design and implementation elements of a typical Bluetooth application. For those readers who have little or no experience with Bluetooth, the resources listed at the end of this article provide additional information.

To compile and execute the BlueChat example application, you need to obtain the J2ME Wireless Toolkit 1.0.4 and the Rococo Impronto Simulator. Refer to the resources section for the download location.

Bluetooth Concept
Bluetooth is one of the wireless connectivity options available for mobile application development. It is characterized by a short range, low-power consumption; ad hoc networking; and usage-oriented design. Because of these characteristics, Bluetooth is often employed in consumer devices such as mobile phones and personal digital assistants (PDA). Typical usages of Bluetooth include answering voice calls using a wireless headset, synchronizing data between a PDA and a PC, and sending images from a camera phone to a PDA.

The functionality of Bluetooth is governed by a public and royalty-free specification and defined by the Bluetooth Special Interest Group. The Bluetooth specification defines both hardware and software layers. Traditionally, Bluetooth module manufacturers provide the necessary SDK to interface with their modules. One of the complexities of Bluetooth development is the fragmentation of these SDKs and APIs.

Vendor-specific SDKs force developers to adopt proprietary APIs for their respective Bluetooth chip sets. Bluetooth applications built on top of these APIs are not portable across devices and platforms. But isn't Bluetooth a standardized specification? The Bluetooth specification as defined by Bluetooth SIG is a functional specification of the technology. It describes how proper Bluetooth devices behave, and how they interoperate with each other (via Profiles). It does not describe how Bluetooth devices can be programmed nor how applications exercise a Bluetooth device's functions and utilize communication channels.

The need for a standardized API arises in order to develop Bluetooth applications in a platform-independent manner.

Java Bluetooth API (JSR-82)
The Java Community Process introduced the first standardized API specification for Bluetooth back in 2000. This specification (JSR-82), Java API for Bluetooth Wireless Technology (aka JABWT), establishes a common ground for rapid Bluetooth application development. Developers are now able to write Bluetooth applications independent of hardware vendors. Most important, JABWT-compatible applications are portable across various JABWT-equipped devices.

The benefits of JABWT nevertheless come with a cost. The Bluetooth specification is designed to cover a diverse range of devices and usage scenarios. To complement this diversity the scope of JABWT took the lowest common denominator approach. Only the most commonly used profiles and functions are included in JABWT. In particular, Generic Access Profile, Service Discovery Profile, Serial Port Profile, Generic Object Exchange Profile, and their respective protocols are supported. These profiles allow JABWT applications to perform the following functionalities:

  • Generic Access Profile: Provides the basic building blocks of a Bluetooth application, such as local device, remote device, Bluetooth address, and device discovery.
  • Service Discovery Profile: Provides the ability to find available services to access remote functionalities.
  • Serial Port Profile: Provides a stream-based connectivity between Bluetooth applications.
  • Generic Object Exchange Profile: Provides support for OBEX protocol, which allows applications to exchange simple objects such as business card data.
To simplify the programming model, two entities, the Bluetooth Configuration Center (BCC) and the Service Discovery Database (SDDB), are abstracted from the Java API. BCC includes the capabilities that globally configure the Bluetooth stack and prevent one application from adversely affecting another, typically a native application that exposes a user interface for users to parameterize the device. Although BCC is transparent to Java applications, it is important to realize that BCC has the ultimate authority over the Bluetooth host and may affect the behavior of your Bluetooth applications. SDDB is an abstract database of service records, a collection of attributes that describe your Bluetooth services. Java applications interact with SDDB indirectly via the update and retrieval of service records.

Design of a Bluetooth Application - A Bluetooth Chat Room
To illustrate various aspect and design issues of a Bluetooth application, we'll develop a JABWT-based chat room application, called BlueChat, for mobile devices that must support the J2ME MIDP 1.0 profile. Users who have a JABWT-capable device (such as the upcoming Nokia 6600 phone and Sony Ericsson P900) can use this application to chat with their nearby friends in an IRC fashion. When BlueChat launches, it searches and joins any existing chat room within the Bluetooth effective range, or creates a new chat room if it's the first active BlueChat in that range. We use the words chat room to represent a virtual chat room that's formed by a network of BlueChat applications. Users can start messaging with each other within the same virtual chat room when there's more than one party connected to each other. If one user sends a message over the air, all parties of the chat room will receive the message. Users can join and leave the chat room at anytime.

For this article we make some assumptions to simplify the application in order to focus on Bluetooth implementation topics:

  • There's only one chat room that exists within effective Bluetooth range.
  • There is no security imposed when joining a chat room.
  • Users run one instance of BlueChat on a device at any given time.
Before we dig into the source code, let's look at some of the Bluetooth application design issues. JABWT does a good job of providing a familiar API to J2ME developers for accessing Bluetooth facilities. JABWT is integrated with the J2ME Generic Connection Framework. As a result, Bluetooth network programming is very similar to a stream-based connection model.

Like many other network protocols, the Bluetooth connection model employs a client/server architecture. Our BlueChat application, on the other hand, operates in a peer-to-peer manner. Each running instance of BlueChat (or a node) can serve as a client and a server at the same time. It behaves as a client when BlueChat starts up; it searches and connects to existing running BlueChat devices. Once connected, it makes itself available for future clients to connect to. In such cases, it serves as a server for future client connections.

Figure 1 represents the network relationship between three BlueChat applications. To logically represent an active BlueChat node, we use the concept of endpoint to encapsulate all the connectivity attributes of a node. An endpoint represents a unique message delivery destination and source regardless of whether it is a server or a client.

Figure 1

A Bluetooth connection differs from a regular socket connection by its unique device and service discovery processes. Bluetooth applications typically start the device discovery process to identify connectable devices, which is followed by a service discovery process to obtain a reference (URL) to suitable services. To hide these complexities from the Graphical User Interface (GUI) elements, a network layer is introduced to serve as a fašade to the Bluetooth API. This design is comparable to the Model-Viewer-Controller model where the Viewer component is decoupled from the Model component. The GUI can access Bluetooth connectivity via a simplified interface, which does all the discovery and connection establishment behind the scenes. This network layer also provides the functionality to send messages to and receive messages from other endpoints. A call back interface is in place to report any network activity back to the GUI. Figure 2 illustrates the relationship between various layers and components in BlueChat.

Figure 2

The communication channel between each connected BlueChat endpoint is a structured data stream connection. We put together a simple protocol to coordinate the activity between each endpoint. This protocol includes the following features:

  • Initial handshake: Each point must handshake with each other when the connection is first established. This ensures that the connecting device is a BlueChat node rather than a mistakenly connected application. During the handshake, we also exchange the screen names of the users (see Figure 3).

    Figure 3

  • Delivery of text message: Each sent text message is delivered to all endpoints connected to the BlueChat network.
  • Termination handshake: If the user quits the chat room gracefully, a termination token is sent to all the other endpoints to indicate its intention. We can clean up the necessary network and runtime resources associated with the leaving endpoint upon receiving this token. However, if the user walks away from effective range and becomes inaccessible, a termination token is not sent. Other active endpoints will discover the leaving party is inaccessible when the connections are lost, and they will clean up the resources (see Figure 4).
Figure 4

Implementation Consideration
The NetLayer class, which implements the BlueChat networking layer, does most of the Bluetooth-related work and provides the following functionality:

  • Initializes the Bluetooth stack
  • Registers BlueChat services to the Bluetooth device
  • Searches for nearby devices
  • Searches for BlueChat services on nearby devices
  • Establishes endpoint connectivity for found BlueChat services
  • Manages the life cycle of all endpoints
The Bluetooth stack can be initialized by calling LocalDevice. getLocalDevice(). LocalDevice is a singleton that uniquely represents the underlying Bluetooth device implementation. You can use the LocalDevice instance to gain access to other Bluetooth features including:
  • Discovery agent (via getDiscoveryAgent())
  • Bluetooth physical network address (via getBluetoothAddress())
  • SDDB (via getRecord() and updateRecord())
The BlueChat NetLayer's initial work is to create and register a BlueChat service to a local device. A Bluetooth service is an entry point for other Bluetooth clients to access available functionalities. Since each BlueChat endpoint can serve as a server, it must register its service in order to make this server available to other BlueChat clients. JABWT utilizes the MIDP Generic Connection Framework to instantiate a server connection. A BlueChat application needs to instantiate a Serial Port Profile connection, basically a stream-based connection that allows two BlueChat applications to exchange data using Java input and output streams. A BlueChat server connection is created using the code in Listing 1.

After a server connection is created, the service is not yet available to external clients (it is not discoverable). What has happened is that JABWT created a corresponding ServiceRecord for this service. A ServiceRecord is a collection of attributes that describes our service, and these attributes are searchable by clients. We can use localDevice.getRecord( server ) to retrieve the newly created ServiceRecord. You may notice that the ServiceRecord is not empty at this point; it is already populated with some default values that are assigned by the JABWT implementation based on the connection string and the implementation configuration when we perform Connector.open().

The server.acceptAndOpen() method notifies the Bluetooth implementation that the application is ready to accept incoming connections and make the service available. This also instructs the underlying implementation to store the ServiceRecord object in the SDDB, which occurs when server.acceptAndOpen() is first invoked. Notice that only the attributes stored in the SDDB can be seen and queried by other Bluetooth clients. Any subsequent change to the ServiceRecord must be reflected in the SDDB by using localDevice.updateRecord().

Now our BlueChat application is ready to accept a connection. But what if your friends are already chatting prior to the start of your BlueChat? If there is an existing chat room available, BlueChat should join the existing network by searching for other BlueChat services on each individual device and connecting to their services. Three steps must be taken to perform this action.

  1. Search for an available device.
  2. For each available device, search for available and matching services.
  3. For each available and matching service, connect to the service and perform the initial handshake.
DiscoveryAgent, another singleton in JABWT, can help us find other devices and services. Listing 2 shows the steps to search for devices.

There are two other options for retrieving connectable devices, a cached devices list and a preknown devices list. Cached devices are remote devices that have been discovered in a previous inquiry. Preknown are remote devices that are preconfigured in BCC. In our example, we choose to ignore both cached and preknown devices. We want to retrieve the most up-to-date list of active BlueChat devices at the moment BlueChat is launched. Therefore, our BlueChat application always initiates a new search for all surrounding devices.

Devices can be searchable in two modes, General Inquiry Access Code (GIAC) and Limited Inquiry Access Code (LIAC). When a device is set to GIAC, it basically means "I want to be discovered all the time." Devices that provide public and permanent services fall into this category. Printers and fax machines are examples of GIAC devices. On the other hand, LIAC discovery mode means "I want to be discovered for a short period of time, as requested by my user." Devices that provide on-demand connectivity will fall into this category. Examples are multiple player game consoles, mobile modems, and our BlueChat program.

The device discovery and service discovery processes are performed in an asynchronous manner. A Bluetooth application must provide a callback object for the JABWT implementation to notify when devices or services are found. This callback object (the NetLayer$Listener inner class object in our case) implements the DiscoveryListener interface. When a device is found, the deviceDiscovered() method is invoked. We do some basic filtering (using the Major Service Class flag) to narrow down the candidate devices for our BlueChat application and ignore other unrelated devices (see Listing 3).

When all candidate devices are discovered, the device search is completed and the searchCompleted() method is invoked. We initiate the service discovery process using DiscoveryAgent .searchServices(). This is where the ServiceRecord attributes become useful. ServiceRecord is not only a description of the services, but also a query of constraints during service discovery. The second parameter of searchServices() allows us to specify which attributes and values the services must have in order for us to discover them. We can provide the UUID for the service that we registered earlier and it narrows down the exact matching candidate services on a remote device. This mechanism not only improves the performance of the discovery process, but also reduces the possibility of conflict. Once the desired service (BlueChat service) is found, we can retrieve the corresponding connection URL and establish the physical connection (see Listing 4).

To further validate that the connected service is indeed a BlueChat service, we immediately perform a handshake with the other party by sending a handshake signal (SIGNAL_HANDSHAKE) and exchanging the user screen name. Receiving parties must respond with an acknowledgment (SIGNAL_HANDSHAKE_ACK) to confirm the request (see Listing 5).

To logically represent all the parties in the chat room, we introduce class EndPoint. From the application-level perspective, an endpoint encapsulates information for each actively connected BlueChat user and device. BlueChat uses EndPoint to identify which user to send a message to, and from which user a message is received. This abstraction allows us to hide the JABWT complexity from the GUI application. Endpoints are created when a connection is established between two BlueChat devices. Once created, we attach a reading thread and sending thread to the endpoint to manage the traffic between two endpoints. From this point on, two endpoints exchange user-entered messages (using SIGNAL_MESSAGE) until a termination signal is received. Implementation of this protocol can be found in the Reader and Sender classes.

When a user exits BlueChat, the application sends the last message - a termination token (SIGNAL_TERMINATE) - to all connected parties. This token signals that the endpoint is no longer active. All receiving parties must return an acknowledgment (SIGNAL_TERMINATE_ACK) and remove the leaving endpoint from the active endpoint list. An endpoint can also be removed when the connectivity is dropped, which suggests the user has left the chat room without an explicit exit command (possibly due to a user's walking away from the Bluetooth effective range).

Our GUI, based on the MIDP LCDUI API, provides a simple interface to send and receive messages. All received messages from all connected users are displayed sequentially on the screen, which creates a virtual chat room environment. When there are more messages to display than can fit onto one screen, older messages will roll off the upper edge. In this example application, users are not able to scroll back to see the past messages. Pressing the "Write" command takes users to a message-editing mode. Pressing the "Send" command sends the currently entered message to the chat room; all other connected users are able to see the message. To quit the chat room, pressing the "Exit" command sends a termination token to all other parties.

Conclusion
Java/J2ME is the first platform to introduce a standard Bluetooth API, the Java API for Wireless Bluetooth (JAWBT). With the emergence of some JAWBT-enabled devices, developing portable Bluetooth applications becomes a reality for J2ME developers. In this article, we developed a Bluetooth application that allows us to understand the essence of Bluetooth development from design to implementation. Thanks to the J2ME-friendly API, experienced J2ME developers will find Bluetooth programming a familiar exercise. Device and service discoveries are important additions to the Bluetooth programming model, and should be fully understood by all Bluetooth developers.

One aspect of Bluetooth development not touched upon in this article is Bluetooth security. Bluetooth provides basic security features for authenticating and authorizing access to devices and services. I recommend reading Chapter 8 of the JABWT specification for details on how to build a secure Bluetooth application.

Resources

  • Java APIs for Bluetooth Wireless Technology (JSR-82) Specification: http://jcp.org/en/jsr/detail?id=82
  • Benhui.net Bluetooth developer resources: www.benhui.net/bluetooth
  • JABWT discussion group at Yahoo: http://groups.yahoo.com/group/JABWT
  • The official Bluetooth member site: www.bluetooth.org/
  • Rococo Simulator Developers' Corner: www.rococosoft.com/devcorner/index.html
  • PaloWireless Bluetooth Resources Center: www.palowireless.com/infotooth/knowbase.asp
  • Sun J2ME Wireless Toolkit: http://java.sun.com/products/j2mewtoolkit/

    About the Author
    Ben Hui is a mobile technology specialist who develops software for PDAs and mobile devices and is keen on making J2ME technology work in harmony with daily life experiences. He has been programming with Java since its inception and has recently become addicted to consumer-device technologies. www.benhui.net. [email protected]

    "Go Wild Wirelessly with Bluetooth and Java"
    Vol. 9, Issue 2, p. 26

    	
    
    
    
    Listing 1: Creating a server connection
    
         // BlueChat specific service UUID
          private final static UUID uuid = new UUID(0x6600BC);
    
          ...
    
          StreamConnectionNotifier server = null;
    
          // Create a server connection object, using a
          // Serial Port Profile URL syntax and our specific UUID (0x6600BC)
          // 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 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  );
    		  
    Listing 2: Start discovering near-by devices
    
     // initialize the JABWT stack
          LocalDevice device = LocalDevice.getLocalDevice(); 
    	  // obtain reference to singleton
          device.setDiscoverable( DiscoveryAgent.LIAC ); 
    	  // set Discover mode to LIAC
          DiscoveryAgent agent = device.getDiscoveryAgent(); 
    	  // obtain reference to singleton
    
          // although JSR-82 provides the ability to lookup
          // cached and preknown devices, we intentionally by-pass
          // them and go to discovery mode directly.
          // this allow us to retrieve the latest active BlueChat parties
          //
          
    // Listener object implements DiscoveryListener
          agent.startInquiry(DiscoveryAgent.LIAC, new Listener() );
    
    Listing 3: Filtering relevant devices into pending EndPoint list
    
        /**
         * A device is discovered.
         * Create a EndPoint for the device discovered and put it on the pending list.
         * A service search will happen when all the qualifying devices are discovered.
         */
        public void deviceDiscovered(RemoteDevice remoteDevice,
                                     DeviceClass deviceClass)
        {
    
    // only device of SERVICE_OBJECT_TRANSFER service 
    // will be considered as candidate device
          // because in our BlueChat service, we explicitly set the service class to
          // SERVICE_OBJECT_TRANSFER. see the run() method
          if ( (deviceClass.getServiceClasses() & 
     BluetoothConstants.SERVICE_OBJECT_TRANSFER) != 0 )
          {
            try
            {
              // create a inactive EndPoint and put it on the pending list
              EndPoint endpt = new EndPoint(NetLayer.this, remoteDevice, null);
    
              pendingEndPoints.addElement( endpt );
    
            } catch (Exception e)
            {
            }
          }
        }
    
    Listing 4: Start discovering BlueChat services
    
       /**
         * device discovery completed.
         * After device inquery completed, we start to search for BlueChat services.
         * We loop through all the pending EndPoints and request agent.searchServices
         * on each of the remote device.
         */
        public void inquiryCompleted(int transId)
        {
          //
          // for each EndPoint, we search for BlueChat
          // services, i.e. ServiceClassIDList (0x0001) = uuid (0x6600BC)
          for (int i = 0; i < pendingEndPoints.size(); i++)
          {
            EndPoint endpt = (EndPoint) endPoints.elementAt(i);
    
              //
              // searchServices return a transaction id, which we will used to
              // identify which remote device the service is found in our callback
              // listener (class Listener)
              endpt.transId = agent.searchServices(new int[] {0x0001}, 
    // attribute ID for ServiceClassIDList new UUID[] {uuid}  
    // BlueChat service UUID endpt.remoteDev, new Listener());
          }
        } 
    
    Listing 5: Establishing connection to discovered BlueChat services
    
      /**
         * a service is discovered from a remote device.
         * when a BlueChat service is discovered, we establish a connection to
         * this service. This signal joining the existing virtual chat room.
         */
        public void servicesDiscovered(int transId, ServiceRecord[] svcRec)
        {
    
            if ( svcRec.length > 0 )
            {
    // We make an assumption that the first service is BlueChat. In fact, only one
    // service record will be found on each device.
    // Note: we know the found service is BlueChat service because we search on 
    // specific UUID, and this UUID is unique to us
              String url  = svcRec[0].getConnectionURL(
     ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false );
              StreamConnection con = (StreamConnection)Connector.open( url );
    
    	// Establish active EndPoint
    	// and start sending SIGNAL_HANDSHAKE
    	....
            }
         }
    
    

    All Rights Reserved
    Copyright ©  2004 SYS-CON Media, Inc.
      E-mail: [email protected]

    Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.