Introduction
The purpose of this article is to provide hands-on experience with the Java APIs for Bluetooth Wireless Technology, JSR-82 API. If you're unfamiliar with the semantics of Bluetooth, don't worry. I'll cover those items with a brief introduction to the Bluetooth protocol and its use cases (called Bluetooth profiles). Because this application will demonstrate how to use Bluetooth to transfer images to other Bluetooth devices, I'll also show you how to use the File Connection API of the JSR-75 in order to have programmatic access to the file system of your mobile device. At the end of the article, we'll have a working example that's capable of transferring images (and any file for that matter) to remote Bluetooth devices. The Bluetooth Protocol
Here's a little known fact about Bluetooth: It's one of the most (if not the most) widely deployed and successful SOA (Service Oriented Architecture) systems in the world. Bluetooth technology has a very large installed based (over 500 million devices deployed) with current figures estimating that an additional five million Bluetooth devices are shipped every week. Long before the term "Service Oriented Architecture" became a buzzword, the Bluetooth protocol provided a mechanism for service registration, service discovery, and service invocation. So, the Bluetooth protocol incorporates a service-oriented architecture and employs a familiar client/server communication scheme used in other protocols such as HTTP and FTP: Servers wait patiently until a client initiates a request. Current Bluetooth devices on the market are capable of communicating at 3 Mb/s and can support wireless audio in stereo. Figure 1, below is a diagram showing the layers of the Bluetooth protocol stack.
Because the focus of this article is OBEX, I won't go into details about all the layers shown in Figure 1, but I do want to provide some details on the major layers that support OBEX. As you can see, one of the major protocol layers in the stack is L2CAP (the Logical Link Control and Adaptation Protocol). L2CAP functions as the multiplexer of packet data between all the other upper layers. RFCOMM, on the other hand, is known as the "virtual serial port" layer. RFCOMM works well when you need to communicate with devices that support data streams. OBEX (which stands for Object Exchange) is the protocol layer that is best suited for file transfers. With OBEX you can create and send messages to remote Bluetooth devices that contain the payload (i.e. the file that you want to send) and also important metadata (such as the filename, file size, and file type). Bluetooth ProfilesTh
Bluetooth Profiles allow various Bluetooth devices with different capabilities to interact and cooperate. Each profile is a use case that defines functionality for a specific purpose. For instance, if you want to print from your mobile device to a printer, both devices must implement the Basic Printing Profile. Or, for instance, if you want to sync a desktop and PDA contact list, both devices must support the Synchronization Profile. Table 1, located below, lists the profiles that use the OBEX protocol layer of the Bluetooth stack. Table 1. Current OBEX-based Profiles
According to the Bluetooth SIG, there are well over 30 Bluetooth profiles defined, ranging from audio distribution to personal area networking. In this article, we'll use the JSR-82 API to implement the Object Push Profile and send images to any Bluetooth device that also supports OPP. Creating the ImageSender Midlet
The ImageSender Midlet was created using the NetBeans Mobility Pack of the NetBeans 5.0 IDE. The Mobility Pack includes a very handy GUI designer tool that allows mobile developers to quickly create mobile applications using drag-and-drop techniques. The ImageSender Midlet contains several static GUI components (i.e. components that are not created dynamically), and the Mobility Pack is very efficient at creating the GUI components and the workflow between them. Figure 2, shown below, depicts the NetBeans project used for the ImageSender Midlet.
In order for the ImageSender to accomplish its duties (such as reading files from the file system, and sending data to remote Bluetooth devices), the ImageSender employs the functionality of inner classes that encapsulate three significant areas of functionality:
That takes care of the preliminaries, so let's get started! ImageSender.FileNavigator
Figure 3, located below, is a sequence diagram showing the interactions between the ImageSender Midlet and its inner class, FileNavigator, which is used exclusively for reading and traversing the file system of a mobile device.
To start things off, you'll see that ImageSender obtains an instance of the FileNavigator and calls the method getListofFolder() which returns a javax.microedition.lcdui.List. FileNavigator will in turn use the FileSystemRegsitry class in the JSR-75 File Connection API in order to get an Enumeration of the "roots" of the filesystem, which are the mount points of your device. If your mobile device contains removable media (like an SD memory card), it will also show up in the Enumeration. For each root of the filesystem, a FileConnection is made on that item to determine if it's a file or a folder. This is necessary because you definitely want to handle them differently (i.e. if the item is a folder, then you want to traverse that folder, but if the item is a file, then you would want to open that file to get the contents). After the enumeration has been iterated over, the FileNavigator inner class will return a List to the ImageSender, which simply displays the List on the mobile device, as shown in Figure 4 below.
Because the FileNavigator inner class implements the CommandListener interface, it will handle all requests from the user interface to change directories or to select a file. This approach allows parent class, ImageSender, to be free from the responsibility of responding to the user's input and knowing what to do with it. The inner class already has the reference to the JSR-75 classes that allow it to connect to the filesystem, so it's the best candidate for handling the user's requests and working with the filesystem. Located below is a portion of the commandAction() method of the FileNavigator; this section of code is executed when a user selects an item in the List:
As you can see, if the user selects a folder, the FileNavigator will traverse the folder, return another list, and display it. However, if the user selects a file, the FileNavigator inner class will open a FileConnection to the file and read the contents in a byte[] named "file". ImageSender.BTUtility
The second helper class employed by the ImageSender Midlet is BTUtility. As you can probably guess, the BTUtility inner class encapsulates all the JSR-82 Bluetooth API method calls from the rest of the code. The BTUtility provides two major areas of functionality to the ImageSender Midlet: It discovers remote Bluetooth devices in the vicinity and it performs a service search on those devices. Figure 5 is a sequence diagram showing how the ImageSender uses the BTUtility.
As shown above, after the ImageSender gets a new instance of the BTUtility, which in turn obtains a reference to the LocalDevice and DiscoveryAgent classes. In order to find Bluetooth devices in the vicinity, you need to call DiscoveryAgent.startInquiry(). The implementation will asynchronously call your deviceDiscovered() method for each remote Bluetooth device found in the area. Finally, when no more Bluetooth devices can be found, the JVM will call your inquiryCompleted() method. Figure 6, shows a list of Bluetooth enabled devices from the BTUtility class.
The other job that BTUtility performs is that searches for services on remote Bluetooth devices. As shown in Figure 7 below, the process of searching for services is more CPU intensive that discovering devices. That's the reason why the device discovery process could be initiated from the constructor of BTUtility, but the service searching portion must be started in a Thread.
Fortunately, BTUtility extends the Thread class, so that will prevent the user interface from hanging. When BTUtility calls DiscoveryAgent.searchServices(), the JVM will asynchronously call his serviceDiscovered() method when a matching service is found. When the service search process is completed, the JVM will call its serviceSearchCompleted() method. Alternately, we could have called DiscoveryAgent.selectService(), but according to the JSR-82 specification, that would have returned the connectionURL of only one service provider in the vicinity. As you can recall from Figure 6 above, in most circumstances you'd want to know who you're going to send the file to. Below is a listing of the BTUtility in its entirety:
In the BTUtility inner class, you may notice that we're using a few hexadecimal values here and there. In particular:
Now, if you remember the values in Table 1, above, then you can understand why we created a UUID value of 0x1105, since that's the UUID for the Object Push Profile. However, we're also using the value of 0x0100 in the attrSet, which allows us to know the service name of the remote service. ImageSender.FilePusher
So what do have so far? Well, first of all, we have a Midlet that can browse a filesystem using the JSR-75 FileConnection API (this of course, is all handled by our inner class, FileNavigator). This Midlet also has the capability of discovering remote Bluetooth devices in the vicinity and determining the services available on those devices (that capability is graciously provided to us by another inner class, BTUtility). So, all we need now is a mechanism for sending files to remote Bluetooth devices using OBEX. FilePusher accomplishes this task very easily by using the org.netbeans.microedition.lcdui.WaitScreen (depicted in Figure 8) and implementing the org.netbeans.microedition.util.CancellableTask which extends Runnable.
As illustrated above, the WaitScreen will execute CPU-intensive tasks (such as performing network I/O). That task must implement the CancellableTask interface (which is exactly what the FilePusher inner class does). Using the IDE, you can graphically connect to a success or failure screen depending upon the outcome of your operation. The code for the FilePusher is shown in the Listing below:
Now let's delve into the depths of the OBEX protocol by scrutinizing the run() method of the ObjectPusher inner class. The best way to understand the OBEX protocol is to look at the interaction of an OBEX client and server as depicted in Figure 9, below:
The scenario depicted in Figure 9 will take place after the physical Bluetooth connection between the client and server are established. In FilePusher, this is accomplished with the following line of code:
Now that the connection has been established, it's time to create a session between the client and the server by casting the connection object into a ClientSession object:
Now, with a session established, take another look at Figure 9. Be sure to observe that clients send OBEX Operations (like Connect, Setpath, Get, Push, etc.) and servers will respond to clients with OBEX Response Codes (160, 161, 193, 196, etc.). The code below demonstrates how to send the Connect operation:
As you can observe from the following code snippet, OBEX is the preferred means for file transfers over Bluetooth (as compared to RFCOMM or L2CAP) because you can set metadata about the file that you want to transfer using headers:
This allows the receiver to know the name, file type, and size of the file before accepting the file -- a very powerful capability the other protocol layers in the Bluetooth stack don't support. Now, after the appropriate headers have been set, we'll send the Put operation to the server:
When the method returns, you'll have an Operation object where you can open an OutputStream and send the file data stored in the byte[] named "file" (if you remember, we populated "file" from the FileNavigator inner class). Now if you'd like to know what the response code was from the server, optionally call the getResponseCodes() method on your instantiated Operation object. So, after we've sent our file to its destination using the OutputStreams that we've opened from the putOperation, we send the final OBEX Operation to wrap everything up: the Disconnect Operation.
Conclusion
As you can see, it didn't take a lot of effort to create a composite application that uses both the JSR-82 and JSR-75 APIs for wireless image transfer. Although this article set out to show you how to transfer images over OBEX, you probably realized that the code can be easily modified to send out file types. Enjoy! Acknowledgements
I would like to thank Uri Katz from Sun Microsystems for helping me improve this article. About the Author
Bruce Hopkins is the author of the book, Bluetooth for Java (by Apress Publishers) and is the creator of the JB-22 developer kit. He graduated from Wayne State University in Detroit with a B.S. degree in Electrical and Computer Engineering. He currently works as a Technical Architect for Gestalt LLC and focuses on distributed computing, Web services, and wireless technologies. He can be contacted at bhopkins@gestalt-llc.com. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||