Java and
|
Modified: |
Disclosure
Some of the following examples are from:
- BlueCove - Java library for Bluetooth, Copyright (C) 2006-2007 Vlad Skarzhevskyy
- Using the Java APIs for Bluetooth Wireless Technology by C. Enrique Ortiz
- Bluetooth Essentials for Programmers by Albert S. Huang, Larry Rudolph
Resources
Bluetooth is a wireless protocol for local communications.
Java promises software portability across a range of platforms, JSR 82 is the standard Bluetooth definition for Java devices, BlueCove is an open source implementation for Windows, Mac and GNU/Linux.
While many similarities exist between Internet and Bluetooth protocols, one key difference is that Bluetooth devices move in and out of radio frequency range. Normally, before commencing communication, devices must discover others to detect the address and services that are provided by other devices.
Key programming issues
Bluetooth communications are part of the Generic Connection Framework (GCF).
Discovery
Devices must be set to discovery mode.
Known
Communication with a known remote device is established by:
btspp - Several protocols are possible, RFCOMM is connection-oriented, btspp is Bluetooth Serial Port Profile.
Address - Bluetooth devices have an 12 hexadecimal digit address (e.g. 0012D25ABDE4)
port - The port on which the remote device will receive data.
A complete specification would be:
btspp://0012D25ABDE4:3
Unknown
Usually the service desired (email, chat, etc.) is known but not device address and port. Discovery can determine the address and port used by a service (i.e. the URL). More follows.
UUID
Services are uniquely identified by a Universally Unique Identifier (UUID), a 128-bit number, in hexadecimal something similar to BAE0D0C0B0A00095570605040302011.
Service Discovery Protocol (SDP)
SDP determines the URL given a service UUID.
Server
- advertises the UUID
- waits for a connection
Client
- runs a discovery agent that searches for the service UUID,
- agent reports the URLs that provide that service,
- client connects to a URL
Bluetooth Stack ![]()
Microsoft Vista provides a Bluetooth stack that can enabled through the Control Panel.
Broadcom devices, such as those in LF115, are also supported by the more reliable and robust Widcomm drivers.
Note that real devices must be set to discovery mode.
J2ME JSR 82 Bluetooth programming - Emulation
JSR 82 can be used with J2SE or J2ME.
One key difference is that J2ME programs are often executed in an emulator, so the Bluetooth address must be either that of the real Bluetooth on the host or an arbitrary one generated by the emulator.
The following serves as an introduction to Bluetooth programming and how to determine the Bluetooth device address when emulating.
The following displays the local Bluetooth device address when run in an emulator. Running another copy of the program in the emulator generates a different address, this allows emulation of multiple devices.
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.bluetooth.*; public class BlueToothAddress extends MIDlet implements CommandListener { private Command exitCommand = new Command("Exit", Command.EXIT,2); private Display display = null; private Form form = null; public void startApp() { form = new Form("Bluetooth Device Address"); form.addCommand(exitCommand); form.setCommandListener(this); display = Display.getDisplay(this); display.setCurrent(form); try { form.append( LocalDevice.getLocalDevice().getBluetoothAddress() ); } catch(Exception e) { form.append("Failed"); } } public void commandAction(Command c, Displayable d) { destroyApp(false); } public void pauseApp() {} public void destroyApp(boolean b) { notifyDestroyed(); } }
Windows JSR 82 Bluetooth programming
Install/Compile/Execute
BlueCove - The JSR 82 implementation, BlueCove, can be downloaded (see above) as a jar file and placed in the classpath.
A Java program can be compiled and executed at the command prompt in Windows, assuming the current directory contains the Java program and the BlueCove jar file, by:
javac -classpath .;bluecove-2.1.0.jar test.java
java -classpath .;bluecove-2.1.0.jar test
RFCOMM service
Because RFCOMM is a connection-oriented protocol, similar to TCP, one process acts as a server accepting connections, another process acts as the client requesting the connection.
Below are basic client and server scripts, to keep things simple, we manually entered the Bluetooth device address (0012D25ABDE4) of the server (much like with the Internet) and a port; in the following, the client connects to the server on port 3.
Note that JSR 82 assigns server ports dynamically, so the client assumes to magically know port 3 is the correct port. More later.
Client
|
Server
|
||
received: Hello world |
The server defines the service name helloService with a UUID of 0x1101 as an identification when discovering services. Note that Python generally uses the name while Java uses the UUID. Note also that in Java the server port cannot be defined but is selected dynamically, requiring service discovery by the client.
Defining a server connection is a 3 step process:
- Define the URL which includes:
- Device address, usually localhost
- a UUID, the ID number of the service
- name of the service
String url = "btspp://localhost:" + new UUID( 0x1101 ).toString() +";name=helloService";
- Open the URL.
StreamConnectionNotifier service = (StreamConnectionNotifier) Connector.open( url );
- Wait for a client connection.
StreamConnection conn = (StreamConnection) service.acceptAndOpen();
Try
- Copy the two above files as:
- RFCOMMClient.java
- RFCOMMServer.java
- Download the BlueCove jar file to the same directory.
- Compile the Java files by:
- javac -classpath bluecove-2.1.0.jar RFCOMMClient.java
- javac -classpath bluecove-2.1.0.jar RFCOMMServer.java
- Execute by:
- java -classpath .;bluecove-2.1.0.jar RFCOMMServer
- java -classpath .;bluecove-2.1.0.jar RFCOMMClient
- We will use these later after examining device and service discovery below.
Service Discovery Protocol (SDP) - When any server will do
The above example required that the client know the server's Bluetooth device address and port.
Services can be identified by a UUID.
The
UUIDClassIn Bluetooth, each service and service attribute is uniquely identified by a Universally Unique Identifier (UUID). As its name suggests, each such identifier is guaranteed to be unique across all time and space. The
UUIDclass can represent short (16- or 32-bit) and long (128-bit) UUIDs. It provides constructors to create a UUID from aStringor from a 16- or 32-bit value, a method to compare two UUIDs (if both are 128-bit), and a method to covert aUUIDinto aString.UUIDinstances are immutable, and only services identified by UUIDs are discoverable.You can generate a UUID value with a command:
uuidgen -tif you're running Linux,uuidgenif you're running Windows. The UUID will look something like this:2d266186-01fb-47c2-8d9f-10b8ec891363. When using the generated UUID to build aUUIDobject, you remove the hyphens.The UUID is part of the parameter for opening a server port.
UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
Connector.open( "btspp://localhost:" + HELLOSERVICE_ID.toString() +";name=helloService");
Hello World Client and Server protocol
Hello World Server Client
- Set device discoverable
- Open service
- Wait for client connection to service
- Write Hello World to connection
- Close connection
- Create service discovery agent
- Discover server providing service
- Connect to server
- Read message
- Display message
- Close connection
The client and server use a known service UUID (BAE0D0C0B0A00095570605040302011) to identify the Hello service.
The client, using the UUID, discovers the Bluetooth address and port of the service provider, the server.
Client SDP essentials
UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
DiscoveryAgent mDiscoveryAgent = LocalDevice.getLocalDevice().getDiscoveryAgent();
String url = mDiscoveryAgent.selectService(HELLOSERVICE_ID,
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
StreamConnection conn = (StreamConnection) Connector.open( url );
DataInputStream is = new DataInputStream(conn.openInputStream());
String message = is.readUTF();Server SDP essentials
UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
String url = "btspp://localhost:" + HELLOSERVICE_ID.toString() +";name=helloService";
LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC);
StreamConnectionNotifier service = (StreamConnectionNotifier) Connector.open( url );
StreamConnection conn = (StreamConnection) service.acceptAndOpen();
DataOutputStream os = new DataOutputStream(conn.openOutputStream());
os.writeUTF("Hello world");
In the complete example below, note that the client discovers and then displays the Bluetooth address and port of the service provider.
![]()
![]()
Client
import java.io.*; import javax.microedition.io.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.bluetooth.*; public class SDPHelloClient extends MIDlet implements CommandListener { private Command exitCommand = new Command("Exit", Command.EXIT,2); private Display display = null; private Form form = null; public void startApp() { form = new Form("SDPHelloClient"); form.addCommand(exitCommand); form.setCommandListener(this); display = Display.getDisplay(this); display.setCurrent(form); UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false); DiscoveryAgent mDiscoveryAgent; String url=null; try {
mDiscoveryAgent = LocalDevice.getLocalDevice().getDiscoveryAgent();
url = mDiscoveryAgent.selectService(HELLOSERVICE_ID,
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);if( url == null) { form.append("Connection failed: "+HELLOSERVICE_ID+"\n"); return; } form.append("Connected:"+url+"\n"); StreamConnection conn = (StreamConnection) Connector.open( url ); DataInputStream is = new DataInputStream(conn.openInputStream()); String message = is.readUTF(); form.append(message+"\n"); conn.close(); form.append("Closed\n"); } catch ( Exception e ) { System.out.print(e.toString()); } } public void commandAction(Command c, Displayable d) { destroyApp(false); } public void pauseApp() {} public void destroyApp(boolean b) { notifyDestroyed(); } }Server
The server defines the service name helloService with a UUID of BAE0D0C0B0A00095570605040302011 as an identification when discovering services. Note that Python generally uses the name while Java uses the UUID. Note also that in Java the server port cannot be defined but is selected dynamically, requiring service discovery by the client.
import java.io.*; import javax.microedition.io.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.bluetooth.*; public class SDPHelloServer extends MIDlet implements CommandListener { private Command exitCommand = new Command("Exit", Command.EXIT,2); private Display display = null; private Form form = null; private StreamConnection conn = null; private static final UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false); private static final String url = "btspp://localhost:" + HELLOSERVICE_ID.toString() +";name=helloService"; public SDPHelloServer() { form = new Form("Hello Service"); form.addCommand(exitCommand); form.setCommandListener(this); display = Display.getDisplay(this); display.setCurrent(form); try {
LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC);
StreamConnectionNotifier service = (StreamConnectionNotifier) Connector.open( url );
conn = (StreamConnection) service.acceptAndOpen();form.append("Waiting\n"); DataOutputStream os = new DataOutputStream(conn.openOutputStream()); form.append("Connected\n"); os.writeUTF("Hello world"); os.flush(); conn.close(); form.append("Closed\n"); } catch ( IOException e ) { form.append(e.toString()); } } public void startApp() {} public void commandAction(Command c, Displayable d) { destroyApp(false); } public void destroyApp(boolean b) { notifyDestroyed(); } public void pauseApp() {} }
Service discovery - When the service device matters
The above client discovered a device providing a service, appropriate if only one device provides the service or any device is acceptable.
When multiple devices provide the same service UUID and the device matters, we need to discover devices that provide the service. The UUID for common services such as email or an Internet gateway could be the same but only
some specific device would be appropriate.
Remember that the client must ultimately determine the server address and the service port in order to make a connection by something similar to:
Connector.open("btspp://0012D25ABDE4:3")
The process is to first discover all the devices in range, then discover whether the service is provided by each device. If the desired service is discovered on a device, then the connection can be made.
JSR 82 does not provide a convenience method for combined device and service discovery, hence the following.
Device Discovery
The address and name of enabled devices within range can be discovered by other Bluetooth devices, discovery can take some time (multiple seconds) to complete, given that radio communications is unreliable and devices may miss transmissions.
The following displays address and name of all enabled devices nearby. Note that:
- devicesDiscovered is a Vector that contains all devices discovered; this will be used in another program to discover available services on each device in the Vector.
- inquiryCompletedEvent object is used for synchronization between the discovery agent and the executing thread.
DiscoveryListener
JSR 82 provides a search service agent that reports device discoveries through a DiscoveryListener interface. The listener must implement the following methods:
- public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { }
- public void inquiryCompleted(int discType) { }
- public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { }
- public void serviceSearchCompleted(int transID, int respCode) {}
For device discovery, only 1 and 2 need to be functional.
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.*;
public class RemoteDeviceDiscovery {
public static final Vector <RemoteDevice> devicesDiscovered = new Vector();
public static void main(String[] args) throws IOException, InterruptedException {
final Object inquiryCompletedEvent = new Object();
devicesDiscovered.clear();
DiscoveryListener listener = new DiscoveryListener() {
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
System.out.println("Device " + btDevice.getBluetoothAddress() + " found");
devicesDiscovered.addElement( btDevice );
try {
System.out.println(" name " + btDevice.getFriendlyName(false));
} catch (IOException cantGetDeviceName) {
}
}
public void inquiryCompleted(int discType) {
System.out.println("Device Inquiry completed!");
synchronized(inquiryCompletedEvent){
inquiryCompletedEvent.notifyAll();
}
}
public void serviceSearchCompleted(int transID, int respCode) {}
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { }
};synchronized(inquiryCompletedEvent) {
boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, listener);
if (started) {
System.out.println("wait for device inquiry to complete...");
inquiryCompletedEvent.wait();
System.out.println(devicesDiscovered.size() + " device(s) found");
}
}
}
}Output
wait for device inquiry to complete...
Device 0012D25ABDE4 found
name DELL01
Device Inquiry completed!
1 device(s) found
Try
- Save the above file as:
RemoteDeviceDiscovery.java
- Compile and execute by:
javac -classpath bluecove-2.1.0.jar RemoteDeviceDiscovery.java
java -classpath .;bluecove-2.1.0.jar RemoteDeviceDiscovery
- If the search takes too long (more than a minute or two), terminate by Ctrl C.
Service Discovery
Service discovery first requires device discovery, the RemoteDeviceDiscovery is reused to fill the devicesDiscovered Vector with devices.
Using JSR 82, in principle one needs to know the UUID of the service rather than the service name for discovery; here the helloService is assigned the UUID of 0x1101. searchServices() method searches each device for advertised services, using a list of UUIDs supplied; in this case UUID of 0x1101 is the helloService number. In fact, BlueCove seems to search for any publicly advertised service and all are returned whether or not a UUID list is supplied.
The following displays address and name of all enabled devices nearby. Note that:
- serviceFound is a Vector that contains the URL of services discovered.
- serviceSearchCompletedEvent object is used for synchronization between the discovery agent and the executing thread.
DiscoveryListener
JSR 82 provides a search service agent that reports service discoveries through a DiscoveryListener interface. The listener must implement the following methods:
- public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { }
- public void inquiryCompleted(int discType) { }
- public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { }
- public void serviceSearchCompleted(int transID, int respCode) {}
For service discovery, only 3 and 4 need to be functional.
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.bluetooth.*;
public class ServicesSearch {
static final UUID HELLO_SERVICE = new UUID(0x1101);
public static final Vector<String> serviceFound = new Vector();
public static void main(String[] args) throws IOException, InterruptedException {UUID[] searchUuidSet = new UUID[] { HELLO_SERVICE };
int[] attrIDs = new int[] { 0x0100 }; // Service name
RemoteDeviceDiscovery.main(null); // First run RemoteDeviceDiscovery and use discovered devices
serviceFound.clear();
final Object serviceSearchCompletedEvent = new Object();
DiscoveryListener listener = new DiscoveryListener() {
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { }
public void inquiryCompleted(int discType) { }
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
for (int i = 0; i < servRecord.length; i++) {
String url = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
if (url == null) { continue; }
serviceFound.add(url);
DataElement serviceName = servRecord[i].getAttributeValue(0x0100);
if (serviceName != null) {
System.out.println("service " + serviceName.getValue() + " found " + url);
} else {
System.out.println("service found " + url);
}
}
}
public void serviceSearchCompleted(int transID, int respCode) {
System.out.println("service search completed!");
synchronized( serviceSearchCompletedEvent ){
serviceSearchCompletedEvent.notifyAll();
}
}
};
for(Enumeration en = RemoteDeviceDiscovery.devicesDiscovered.elements(); en.hasMoreElements(); ) {
RemoteDevice btDevice = (RemoteDevice)en.nextElement();
synchronized( serviceSearchCompletedEvent ) {
System.out.println("search services on " + btDevice.getBluetoothAddress() + " " + btDevice.getFriendlyName(false));
LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(attrIDs, searchUuidSet, btDevice, listener);
serviceSearchCompletedEvent.wait();
}
}
}
}Output
wait for device inquiry to complete...
Device 0012D25ABDE4 found
name DELL01
Device Inquiry completed!
1 device(s) foundsearch services on 0012D25ABDE4 DELL01
service helloService found btspp://0012D25ABDE4:3;authenticate=false;encrypt=false;master=false
service Bluetooth Serial Port found btspp://0018F8898BA5:1;authenticate=false;encrypt=false;master=false
service search completed!
Try
- Copy the above file to:
- ServicesSearch.java
- Compile by:
javac -classpath bluecove-2.1.0.jar ServicesSearch.java
- Execute on two different machines by:
java -classpath .;bluecove-2.1.0.jar RFCOMMServer
java -classpath .;bluecove-2.1.0.jar ServicesSearch
- The helloService should be displayed. Locate the address and port used by the service.
- Edit RFCOMMClient.java line:
String url = "btspp://0012D25ABDE4:3";
changing the 0012D25ABDE4:3 to the address and port used by the helloService.
- Compile and execute RFCOMMClient.
J2ME Bluetooth programming
JSR 82
Below example implements device and service discovery for J2ME devices.
The MIDlet security requires that permission be granted to a MIDlet to access a Bluetooth device, requiring two steps.
- In the Wireless Tool Kit, is to add settings with permissions for javax.microedition.io.Connector.bluetooth.server and javax.microedition.io.Connector.bluetooth.client; and also in settings, include Bluetooth/OBEX for J2ME (JSR 82).
- When running the MIDlet on a mobile device, because the MIDlet is self-signed, you will be asked to allow access to connections.
SDP and RFCOMM
Below are basic client and server implementing a helloService. In the following, the client first discovers the service ID BAE0D0C0B0A00095570605040302011 and then connects to the server using the address and serial port number obtained in the URL.
The complete server project with appropriate settings for permissions and API is available here.
RFCOMM Client
The following client does not perform service discovery but connects to an already known device and port URL.
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.bluetooth.*;
public class SymbianHelloClient extends MIDlet implements CommandListener {
private Command exitCommand = new Command("Exit", Command.EXIT,2);
private Display display = null;
private Form form = null;
private String url = "btspp://0018f8898ba5:12";
public void startApp() {
form = new Form("Hello Service");
form.addCommand(exitCommand);
form.setCommandListener(this);
display = Display.getDisplay(this);
display.setCurrent(form);
byte b=0;
try {
form.append("Trying to connect.\n");
StreamConnection conn = (StreamConnection) Connector.open(url); form.append("Connected:"+url+"\n");
DataInputStream in = new DataInputStream(conn.openInputStream());
while (( b = in.readByte()) != 13) // Terminate on character 13 \n
form.append((char)b +"");
conn.close();
} catch ( Exception e ) { form.append(e.toString()); }
}public void destroyApp(boolean b) { notifyDestroyed(); }
public void commandAction(Command c, Displayable d) { destroyApp(false); }
public void pauseApp() {}
}Server
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.bluetooth.*;
public class SymbianSDPHelloServer extends MIDlet implements CommandListener {
private Command exitCommand = new Command("Exit", Command.EXIT,2);
private Display display = null;
private Form form = null;
private StreamConnection conn = null;
private static final UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
private static final String url = "btspp://localhost:" + HELLOSERVICE_ID.toString() +";name=helloService";
public void startApp() {
form = new Form("Hello Service");
form.addCommand(exitCommand);
form.setCommandListener(this);
display = Display.getDisplay(this);
display.setCurrent(form);
try {
form.append("Waiting\n");
LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC);
StreamConnectionNotifier service = (StreamConnectionNotifier) Connector.open( url );
conn = (StreamConnection) service.acceptAndOpen();PrintStream out = new PrintStream(conn.openOutputStream());
form.append("Connected\n");
out.println("Hello world");
out.flush();
conn.close();
} catch ( IOException e ) { form.append(e.toString()); }
destroyApp(true);
}public void destroyApp(boolean b) { notifyDestroyed(); }
public void commandAction(Command c, Displayable d) { destroyApp(false); }
public void pauseApp() {}
}SDP Server key elements
UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
String url = "btspp://localhost:" + HELLOSERVICE_ID.toString() +";name=helloService";
LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC);
StreamConnectionNotifier service = (StreamConnectionNotifier) Connector.open( url );
StreamConnection conn = (StreamConnection) service.acceptAndOpen();SDP Client key elements
UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
String url = ServiceDiscovery.discoverService("helloService", HELLOSERVICE_ID);
StreamConnection conn = (StreamConnection) Connector.open( url );The URL of a service can be determined by a device discovery followed by a service search, similar to the earlier example of displaying all visible devices and public services.
Below is a client that calls the static method ServiceDiscovery.discoverService("helloService", HELLOSERVICE_ID); to obtain the URL of the helloService.
Client using SDP to discover URL given UUID of helloService
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.bluetooth.*;
public class SymbianHelloServiceClient extends MIDlet implements CommandListener {
private Command exitCommand = new Command("Exit", Command.EXIT,2);
private Display display = null;
private Form form = null;
private static final UUID HELLOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302011", false);
public void startApp() {
form = new Form("Hello Service");
form.addCommand(exitCommand);
form.setCommandListener(this);
display = Display.getDisplay(this);
display.setCurrent(form);
String url = null;
byte b;
try {
form.append("Trying to connect.\n");
url = ServiceDiscovery.discoverService("helloService", HELLOSERVICE_ID);
StreamConnection conn = (StreamConnection) Connector.open( url );form.append("Connected:"+url+"\n");
DataInputStream in = new DataInputStream(conn.openInputStream());
while (( b = in.readByte()) != 13)
form.append((char)b +"");
conn.close();
} catch ( Exception e ) { form.append(e.toString()); }
}public void commandAction(Command c, Displayable d) { destroyApp(false); }
public void pauseApp() {}
public void destroyApp(boolean b) { notifyDestroyed(); }
}
Device Discovery
Discovering local devices is the first step in the general service search. The following is the same as the previous device discovery example except that it returns a vector of all local devices discovered when method DeviceDiscovery.discoverDevices() is called.
import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.*;
public class DeviceDiscovery {
private static final Vector devicesDiscovered = new Vector();
public static Vector discoverDevices() throws IOException, InterruptedException {
final Object inquiryCompletedEvent = new Object();
DiscoveryListener listener = new DiscoveryListener() {
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
System.out.println("Device " + btDevice.getBluetoothAddress() + " found");
devicesDiscovered.addElement(btDevice);
try {
System.out.println(" name " + btDevice.getFriendlyName(false));
} catch (IOException cantGetDeviceName) {}
}
public void inquiryCompleted(int discType) {
System.out.println("Device Inquiry completed!");
synchronized(inquiryCompletedEvent){
inquiryCompletedEvent.notifyAll();
}
}
public void serviceSearchCompleted(int transID, int respCode) {}
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { }
};synchronized(inquiryCompletedEvent) {
boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, listener);
if (started) {
System.out.println("wait for device inquiry to complete...");
inquiryCompletedEvent.wait();
System.out.println(devicesDiscovered.size() + " device(s) found");
}
}
return devicesDiscovered;
}
}Service Search
Service search begins by discovering all local devices by a call to method DeviceDiscovery.discoverDevices() which returns a vector of devices. The devices are then searched until the desired service is located or all devices have been searched.
Note that URL of the first device supporting the service is returned.
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.bluetooth.*;
public class ServiceDiscovery {
private static String serviceURL=null;
private static Vector devicesDiscovered = null;
public static String discoverService(final String serviceNAME, final UUID serviceID)
throws IOException, InterruptedException {
UUID[] searchUuidSet = new UUID[] { serviceID };
int[] attrIDs = new int[] { 0x0100 };
devicesDiscovered = DeviceDiscovery.discoverDevices(); // Discover devices
final Object serviceSearchCompletedEvent = new Object();
DiscoveryListener listener = new DiscoveryListener() {
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { }
public void inquiryCompleted(int discType) { }
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
for (int i = 0; i < servRecord.length; i++) {
String url = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
if (url == null) { continue; }
DataElement serviceName = servRecord[i].getAttributeValue(0x0100);
if (serviceName != null) {
System.out.println("Checking service " + serviceName.getValue() + " found " + url);
if(serviceName.getValue().toString().equals(serviceNAME)) {
serviceURL=url;
return;
}
} else System.out.println("Checking service found " + url);
}
}
public void serviceSearchCompleted(int transID, int respCode) {
System.out.println("service search completed!");
synchronized(serviceSearchCompletedEvent){
serviceSearchCompletedEvent.notifyAll();
}
}
};for(Enumeration en = devicesDiscovered.elements(); en.hasMoreElements(); ) {
RemoteDevice btDevice = (RemoteDevice)en.nextElement();
synchronized(serviceSearchCompletedEvent) {
System.out.println("search services on " +
btDevice.getBluetoothAddress() + " " + btDevice.getFriendlyName(false));
LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(attrIDs, searchUuidSet, btDevice, listener);
serviceSearchCompletedEvent.wait();
if( serviceURL != null) break; // Exit on first service match found
}
}
return serviceURL;
}
}
Try
Service Discovery Protocol (SDP) on J2SE
echoService is similar to the earlier helloService for J2ME except that data is exchanged bidirectionally, we again find the service using a known UUID. The main difference is between J2ME and J2SE InputStream and OutputStream classes.
We have seen that Bluetooth devices can locate other nearby devices, those that are in discovery mode, and discover services. Rather than connecting by the address and port of a device and application, devices can discover the address and port by the service ID number. The following perform service discovery using SDP to implement an echoService in which the server echoes whatever is typed at the client.
On the server-side, the primary use of SDP is to advertise the echoService as a serial port or RFCOMM protocol associated with a unique (to local devices) UUID or service ID.
Using SDP in this manner, the client must know the echoService UUID number advertised by the server.
Example: Echo client and server
// SDPEchoClient.java import java.io.*; import javax.microedition.io.*; import javax.bluetooth.*; public class SDPEchoClient { public static void main( String args[] ) { UUID ECHOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302010", false); DiscoveryAgent mDiscoveryAgent; String mConnect=null; try { mDiscoveryAgent = LocalDevice.getLocalDevice().getDiscoveryAgent(); mConnect = mDiscoveryAgent.selectService(ECHOSERVICE_ID, ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); if( mConnect == null) { System.out.println("Connection failed: "+ECHOSERVICE_ID); return; } System.out.println("Connected:"+mConnect); StreamConnection conn = (StreamConnection) Connector.open(mConnect); BufferedReader in = new BufferedReader( new InputStreamReader( conn.openInputStream())); PrintStream out = new PrintStream(conn.openOutputStream()); BufferedReader keyboard = new BufferedReader( new InputStreamReader(System.in)); while(true) { String message=keyboard.readLine(); if (message==null) break; out.println(message); String data = in.readLine(); // Read echo System.out.println( "Received: " + data ); } conn.close(); } catch ( Exception e ) { System.out.print(e.toString()); } } }
// SDPEchoServer.java import java.io.*; import javax.microedition.io.*; import javax.bluetooth.*; public class SDPEchoServer { public static void main( String args[] ) { try { UUID ECHOSERVICE_ID = new UUID("BAE0D0C0B0A00095570605040302010", false); String url = "btspp://localhost:" + ECHOSERVICE_ID.toString() +";name=echoService"; LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC); StreamConnectionNotifier service = (StreamConnectionNotifier) Connector.open( url ); StreamConnection conn = (StreamConnection) service.acceptAndOpen(); System.out.println("Connected"); BufferedReader in = new BufferedReader( new InputStreamReader( conn.openInputStream())); PrintStream out = new PrintStream(conn.openOutputStream()); while(true) { System.out.println("Waiting"); String message=in.readLine(); if (message==null) break; out.println(message); // Echo System.out.println( "Received: " + message ); } conn.close(); } catch ( IOException e ) { System.out.print(e.toString()); } } }
Try
- Download the BlueCove Bluetooth implementation.
- Copy the BlueCove jar (bluecove-2.1.0.jar) file and the above files to the same directory.
- Compile
- javac -classpath .;bluecove-2.1.0.jar SDPEchoServer.java
- javac -classpath .;bluecove-2.1.0.jar SDPEchoClient.java
- Create the file Manifest.txt with the following.
Class-Path: bluecove-2.1.0.jar
- Jar the files (for convenience).
jar cvfem SDPEchoServer.jar SDPEchoServer Manifest.txt SDPEchoServer.class
jar cvfem SDPEchoClient.jar SDPEchoClient Manifest.txt SDPEchoClient.class
- Execute on two different PC machines
- java -jar SDPEchoServer.jar
- java -jar SDPEchoClient.jar
After the client connects, whatever is typed should be sent to the server which echoes and is displayed on the client.