Discovering Devices

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:

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:

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.

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:

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.