Implementing the service discovery state

To implement the service discovery state:

  1. Create the ServiceDiscoveryState.java class

  2. 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;
  3. 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 {
  4. 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();
        }
  5. 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);
        }
  6. 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();
        }
  7. 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) {
                    }
                }
            }
        }
  8. 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;
        }
  9. 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.