Of course, theres only so much you can do with just the local device. Soon you e going to want to find out what other devices are out there. This is the purpose of the DiscoveryAgent class. There is one DiscoveryAgent per LocalDevice, and since theres exactly one LocalDevice, theres exactly one DiscoveryAgent. This is retrieved by the geTDiscoveryAgent( ) method in LocalDevice:
public DiscoveryAgent getDiscoveryAgent( )
For example:
DiscoveryAgent agent = LocalDevice.getLocalDevice().getDiscoveryAgent( );
The startInquiry( ) method scans the airwaves for discoverable remote devices:
public boolean startInquiry(int accessCode, DiscoveryListener listener) throws BluetoothStateException
This search can take about a minute. To avoid blocking and tying up the user interface or other important operations, this scan can run asynchronously. When the local device finds a remote device, it tells the DiscoveryListener passed as the second argument.
The first argument, accessCode, controls the type of the inquiry. It is either DiscoveryAgent.GIAC (General/Unlimited Inquiry Access Code) or DiscoveryAgent.LIAC (Limited Dedicated Inquiry Access Code). Most of the time, you should use DiscoveryAgent.GIAC. Some implementations do not support LIAC mode.
You can prematurely terminate an inquiry by passing the listener to the cancelInquiry( ) method:
public boolean cancelInquiry(DiscoveryListener listener)
The retrieveDevices( ) method returns a list of the Bluetooth devices the agent already knows about (that is, it does not find any newly added devices):
public RemoteDevice[] retrieveDevices(int option)
The option argument should be DiscoveryAgent.CACHED or DiscoveryAgent.PREKNOWN. Cached devices are those discovered in previous inquiries. Preknown devices are specially configured before the application starts up. If none of the requested devices exists, this method returns null. If any devices are preknown or cached, retrieving them is quite a bit faster than launching a new inquiry over the air.
The DiscoveryListener interface has four callback methods that are invoked to signal a device. It actually supports two kinds of searches, one for devices and one for services. Which methods are called back depends on what type of search it is.
The deviceDiscovered( ) method is called when the search uncovers a new device:
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod)
When the agent has given up on finding new devices, it calls inquiryCompleted( ):
public void inquiryCompleted(int discoveryType)
The discoveryType argument indicates how the search completed. It is one of three constants: DiscoveryListener.INQUIRY_COMPLETED, DiscoveryListener.INQUIRY_TERMINATED, or DiscoveryListener.INQUIRY_ERROR.
The servicesDiscovered( ) method is called when the search uncovers one or more new services on a device:
public void servicesDiscovered(int transactionID, ServiceRecord[] serviceRecord)
The TRansactionID argument identifies the search that found the service. The service records provide details about what the device can do and how it operates.
When the agent has given up on finding new services, it calls serviceSearchCompleted( ):
public void serviceSearchCompleted(int transactionID, int responseCode)
This search has five possible responses:
However, a search started by startInquiry( ) won find any services just yet, so you can implement these methods as do-nothings if you e looking for devices. Once youve found a remote device, you can search it for services. Ill have more to say about that shortly.
Example 25-3 is a simple program that searches for and enumerates all the Bluetooth devices it can find. For each device, it prints the name; the address; the major, minor, and service classes; and the combined 3-byte class identifier, printed in both hexadecimal and binary. This information is useful when you e first trying to figure out how to talk to an undocumented device.
import java.io.IOException; import javax.bluetooth.*; public class BluetoothSearch implements DiscoveryListener { private DiscoveryAgent agent; public static void main(String[] args) throws Exception { BluetoothSearch search = new BluetoothSearch( ); search.agent = LocalDevice.getLocalDevice().getDiscoveryAgent( ); search.agent.startInquiry(DiscoveryAgent.GIAC, search); } public void deviceDiscovered(RemoteDevice device, DeviceClass type) { int major = type.getMajorDeviceClass( ); int minor = type.getMinorDeviceClass( ); int services = type.getServiceClasses( ); int classIdentifier = major | minor | services; try { System.out.println("Found " + device.getFriendlyName(false) + " at " + device.getBluetoothAddress( )); } catch (IOException ex) { System.out.println("Found unnamed device " + " at " + device.getBluetoothAddress( )); } System.out.println(" Major class: 0x" + Integer.toHexString(major)); System.out.println(" Minor class: 0x" + Integer.toHexString(minor)); System.out.println(" Service classes: 0x" + Integer.toHexString(services)); System.out.println(" Class identifier: 0x" + Integer.toHexString(classIdentifier)); System.out.println(" Class identifier: " + Integer.toBinaryString(classIdentifier)); } public void inquiryCompleted(int discoveryType) { switch (discoveryType) { case DiscoveryListener.INQUIRY_TERMINATED: System.out.println("Search cancelled"); break; case DiscoveryListener.INQUIRY_ERROR: System.out.println("Bluetooth error"); break; case DiscoveryListener.INQUIRY_COMPLETED: System.out.println("Device search complete");; break; default: System.out.println("Unanticipated result: " + discoveryType); } System.exit(0); } // This search is only looking for devices and won discover any services, // but we have to implement these methods to fulfill the interface public void servicesDiscovered(int transactionID, ServiceRecord[] record) {} public void serviceSearchCompleted(int transactionID, int arg1) {} } |
For this program to find a device, the device must be turned on, be in discoverable mode, and not already have been grabbed by the host operating system. Otherwise, you may not see it. Heres the output from running this on my PowerMac G5:
$ java -classpath .:avetanaBluetooth.jar BluetoothSearch Found WACOM Pen Tablet at 0013C2000D23 Major class: 0x500 Minor class: 0x80 Service classes: 0x1 Class identifier: 0x581 Class identifier: 10110000001 Found elharos mouse at 000A95095A59 Major class: 0x500 Minor class: 0x80 Service classes: 0x1 Class identifier: 0x581 Class identifier: 10110000001 Found Earthmate Blue Logger GPS at 00904B2A88D6 Major class: 0x1f00 Minor class: 0x0 Service classes: 0x0 Class identifier: 0x1f00 Class identifier: 1111100000000 Found elharos keyboard at 000A953AFB0B Major class: 0x500 Minor class: 0x40 Service classes: 0x1 Class identifier: 0x541 Class identifier: 10101000001 Device search complete
From this we can see that this system has four devices in discoverable mode: a mouse, an Earthmate Blue Logger GPS unit, a WACOM tablet, and an unspecified keyboard. The keyboard and the mouse have the same major class but different minor classes. The graphics tablet and the mouse have the same major, minor, and service classes. The GPS unit has the uncategorized major class 0x1F00, since the Bluetooth SIG hasn gotten around to defining an appropriate major class for this sort of device.