To implement the service discovery state:
Create the ServiceDiscoveryState.java
class
Import the required classes.
import java.io.IOException; import java.util.Enumeration; import java.util.Vector; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; import javax.obex.Operation; import javax.obex.ResponseCodes;
Set ServiceDiscoveryState
to extend ExchangerState
and implement DiscoveryListener
and Runnable
. MIDlets use the DiscoveryListener
interface to receive device and service discovery events, and the Runnable
interface to use threads to execute class instances.
public class ServiceDiscoveryState extends ExchangerState implements DiscoveryListener, Runnable {
Create the required
variables and the ServiceDiscoveryState
class constructor.
The constructor initiates a separate thread for starting the service
discovery.
// Instance variables private Thread serviceDiscoveryThread; private Vector services; private DiscoveryAgent agent; private int serviceDiscoveryID; private Vector devices; private boolean canceled = false; public static int GET = 0; public static int PUT = 1; /** * Constructor * * @param _parent - * the class which is nesting the current state of the state * machine * @param _devices - * a vector of RemoteDevice object representing devices found * during inquiry */ public ServiceDiscoveryState(ExchangerStateParent _parent, Vector _devices, int oper) throws IOException { super(_parent); canceled = false; services = new Vector(); devices = _devices; // initiate Bluetooth LocalDevice local = LocalDevice.getLocalDevice(); agent = local.getDiscoveryAgent(); serviceDiscoveryThread = new Thread(this); serviceDiscoveryThread.start(); }
Create methods for handling sending.
/* * ServiceDiscoveryState does not allow to start any other business card * exchange process. * * @see bcexchanger.comm.ExchangerState#startSending() */ public void startSending(int op) throws Exception { throw new IOException( "Service discovery is in progress. Service discovery has to be canceled before starting new sending process"); } /* * ServiceDiscoveryState allows to cancel discovery process. * * @see bcexchanger.comm.ExchangerState#cancelSending() */ public void cancelSending() { canceled = true; agent.cancelServiceSearch(serviceDiscoveryID); }
The MIDlet calls
the servicesDiscovered
method when services are found
during a service search, and the serviceSearchCompleted
method when a service search is completed or was terminated because
of an error.
/* * * Inquiry callbacks are not handled and not supposed to occur, since * inquiry process is not started * * @see javax.bluetooth.DiscoveryListener#deviceDiscovered(javax.bluetooth.RemoteDevice, * javax.bluetooth.DeviceClass) */ public void deviceDiscovered(RemoteDevice arg0, DeviceClass arg1) { throw new RuntimeException( "Internal error #8: ServiceDiscoveryState.deviceDiscovered() should not be called"); } /* * * Inquiry callbacks are not handled and not supposed to occur, since * inquiry process is not started * * @see javax.bluetooth.DiscoveryListener#inquiryCompleted(int) */ public void inquiryCompleted(int arg0) { throw new RuntimeException( "Internal error #9: ServiceDiscoveryState.inquiryCompleted() should not be called"); } public void servicesDiscovered(int id, ServiceRecord[] _services) { for (int i = 0; i < _services.length; i++) { services.addElement(_services[i]); } } public synchronized void serviceSearchCompleted(int arg0, int arg1) { notify(); }
Use the run
method to implement the logic of the service discovery
process.
Set the search for all available services,
unless the search is canceled. Use the searchServices
method to look for services on remote devices that have all the
UUIDs specified in the uuidSet
method.
The uuidSet
method returns the transaction ID of the service
search. For more information about UUIDs, see the UUID
reference.
If one service is found, connect to that service. If more than one service is found, display a list of those services and let the user select which service to connect to. Set listeners for handling user actions.
/* * (non-Javadoc) * * This methods implements logic of the service discovery process * * @see java.lang.Runnable#run() */ public synchronized void run() { Enumeration e = devices.elements(); // Business card exchanger service UUID UUID[] uuids = new UUID[1]; uuids[0] = new UUID(parent.getUUID(), false); // proceeded with all devices if not canceled while (e.hasMoreElements() && !canceled) { RemoteDevice device = (RemoteDevice) e.nextElement(); try { serviceDiscoveryID = agent.searchServices(null, uuids, device, this); } catch (Exception e0) { // signal error to the MIDlet parent.getListener().onServiceDiscoveryComplete( ExchangerComm.ERROR); parent.setState(new IdleState(parent)); return; } try { wait(); // wait until service search is done on this device } catch (Exception e1) { // Ignore } } if (canceled) { // handle user's cancellation try { parent.getListener().onServiceDiscoveryComplete( ExchangerComm.CANCELED); parent.setState(new IdleState(parent)); } catch (Exception e1) { throw new RuntimeException( "Internal error #10: ServiceDicoveryState.run()"); } } else { // not canceled if (services.isEmpty()) { // no services on devices try { parent.getListener().onServiceDiscoveryComplete( ExchangerComm.NO_RECORDS); parent.setState(new IdleState(parent)); } catch (Exception e1) { } } else if (services.size() == 1) { // one service found, // connect to it try { ServiceRecord serviceRecord = (ServiceRecord) services.firstElement(); parent.getListener().onServiceDiscoveryComplete( ExchangerComm.DONE); SendState state = new SendState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(state); state.doSend(); state = null; Thread.sleep(new Long(5000).longValue()); ReceiveState rstate = new ReceiveState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(rstate); rstate.doGet(); rstate = null; Thread.sleep(new Long(5000).longValue()); } catch (Exception e2) { } } else { // several services found, let user select try { // list of friendly names of devices which contain services Vector friendlyNames = createFriendlyNamesList(services); int index = parent.getListener().resolveMultipleServices( friendlyNames); if (!canceled) { // if not canceled during resolving ServiceRecord serviceRecord = (ServiceRecord) services.elementAt(index); parent.getListener().onServiceDiscoveryComplete( ExchangerComm.DONE); SendState state = new SendState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(state); state.doSend(); state = null; Thread.sleep(new Long(5000).longValue()); ReceiveState rstate = new ReceiveState(parent, serviceRecord.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(rstate); rstate.doGet(); rstate = null; Thread.sleep(new Long(5000).longValue()); } else { // if canceled during resolving parent.getListener().onServiceDiscoveryComplete( ExchangerComm.CANCELED); parent.setState(new IdleState(parent)); } } catch (Exception e1) { } } } }
Use a createFriendlyNamesList
method to create a list of devices
that contain the searched services. In case of an exception in getting
the name of a device, use the getBluetoothAddress
method to retrieve the Bluetooth address of the device.
/* * This method creates a list of the friendly names of devices that contain * the servicies * * @param _services - vector of ServiceRecord objects representing found * services @return - vectors of strings containing friendly names of * devices */ private Vector createFriendlyNamesList(Vector _services) { Vector friendlyNames = new Vector(); Enumeration e = _services.elements(); while (e.hasMoreElements()) { ServiceRecord serviceRecord = (ServiceRecord) e.nextElement(); RemoteDevice device = serviceRecord.getHostDevice(); try { friendlyNames.addElement(device.getFriendlyName(false)); } catch (IOException e1) { // If there is an excption getting the friendly name // use the address friendlyNames.addElement(device.getBluetoothAddress()); } } return friendlyNames; }
Create empty methods for sending and receiving business cards.
/* * Server OBEX GET command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation) */ public int onGet(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } /* * Server OBEX PUT command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation) */ public int onPut(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } }
Now that you have implemented the service discovery state, implement the send and receive states.