Code Search for Developers
 
 
  

BTService.java from GridBlocks at Krugle


Show BTService.java syntax highlighted

/*
 * Copyright (c) 2005 
 * Helsinki Institute of Physics
 * see LICENSE file for details 
 * 
 * BTService.java
 * Created on Mar 7, 2004
 */

package fi.hip.gb.bluetooth;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
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.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

/**
 * This is the main class for handling bluetooth connectivity and the device/service
 * discovery process. This class does many things, including:
 * <ul>
 * <li>search for bluetooth devices {@link BTService#query()}
 * <li>create a local bluetooth server and register it with bluetooth 
 * {@link BTService#startServer()}
 * <li>search for remote services automatically after device discovery
 * <li>handle incoming connection request from remote devices
 * </ul>
 * 
 * <p>
 * The service runs on any JSR-82 Bluetooth API compatible device, 
 * for example, mobile phones with Bluetooth support. Nokia 6600 has been
 * tested. It also works nicely with 
 * <a href=http://bluecove.sourceforge.net/>Bluecove</a> API for J2SE
 * platform.
 */
public class BTService implements Runnable {
    public final static int SIGNAL_HANDSHAKE = 0;
    public final static int SIGNAL_MESSAGE = 1;
    public final static int SIGNAL_TERMINATE = 3;
    public final static int SIGNAL_HANDSHAKE_ACK = 4;
    public final static int SIGNAL_TERMINATE_ACK = 5;
    public final static int SIGNAL_DISPATCH = 6;

    // BlueChat specific service UUID
    // note: this UUID must be a string of 32 char
    // do not use the 0x???? constructor because it won't
    // work. not sure if it is a N6600 bug or not
    private final static UUID uuid = new UUID(
            "102030405060708090A0B0C0D0E0F010", false);

    /** reference to bluetooth server */
    private static BTService instance;

    public final static int SERVICE_OBJECT_TRANSFER = 1048576; //0x100000;

    /** reference to local bluetooth device singleton */
    private LocalDevice localDevice = null;

    /** reference to local discovery agent singleton */
    private DiscoveryAgent agent = null;

    /** local server object */
    private StreamConnectionNotifier server;

    /** instances of <code>BTListener</code> implementation listening
     * for events */
    private Vector listeners = new Vector();

    /** set to true when server should be shut down */
    boolean done = false;

    /** name of the local bluetooth device */
    public static String localName = "BTAgent";

    /** 
     * A list of active EndPoints. all messages will be sent to all
     * active EndPoints
     */
    private Vector endPoints = new Vector();

    /** 
     * A list of pending EndPoints. This is used to keep track of
     * discovered devices waiting for service discovery. When all the near-by
     * BlueChat service has been discovered, this list will be cleared until the
     * next inquiry.
     */
    private Vector pendingEndPoints = new Vector();

    /** timer to schedule task to do service discovery, see inquiryCompleted */
    private Timer timer = new Timer();

    /** synchronization lock, see DoServiceDiscovery and serviceSearchCompleted */
    private Object lock = new Object();

    /** max number of service searches that can occur at any one time */
    private int maxServiceSearches = 0;

    /** number of service searches that are presently in progress */
    private int serviceSearchCount;

    /**
     * Call {@link BTService#getInstance()} to get the instance.
     */
    private BTService() {
    }

    /**
     * Gets the instance of the bluetooth service.
     * <p>
     * To start up the server use {@link BTService#startServer()} and later 
     * to shut it down use {@link BTService#stopServer()} method.
     * 
     * @return instance of the BT-service
     */
    public static BTService getInstance() {
        if (BTService.instance == null) {
            BTService.instance = new BTService();
            // initialize the JABWT stack
            try {
                instance.localDevice = LocalDevice.getLocalDevice();
            } catch(UnsatisfiedLinkError ule) {
                instance.log("Bluecove works only in Windows XP");
                BTService.instance = null;
                return null;
            } catch(NoClassDefFoundError ncdfe) {
                instance.log("Bluecove works only in Windows XP");
                BTService.instance = null;
                return null;
            } catch(Exception e) {
                instance.log("failed to initialize local BT-device");
                BTService.instance = null;
                return null;
            }
            instance.agent = instance.localDevice.getDiscoveryAgent();

            // retrieve the name of the local Bluetooth device
            BTService.localName = instance.localDevice.getFriendlyName();
            
            /*
             * Retrieve the max number of concurrent service searches that can exist
             * at any one time. N6600 is capable to only one simultaneous
             * connection.
             */
            try {
                instance.maxServiceSearches = 1;
                //Integer.parseInt(LocalDevice.getProperty("bluetooth.sd.trans.max"));
                //log("maxServiceSearches = " + maxServiceSearches);
            } catch (NumberFormatException e) {
                instance.log("NumberFormatException: " + e.getMessage());
            }
        }
        return instance;
    }

    /**
     * Add a listener class for bluetooth events.
     * 
     * @param callbackListener
     *            listener to be added
     */
    public void addListener(BTListener callbackListener) {
        this.listeners.addElement(callbackListener);
    }
    /**
     * Remove a listener class from bluetooth events.
     * 
     * @param callbackListener
     *            listener to be removed
     */
    public void removeListener(BTListener callbackListener) {
        this.listeners.removeElement(callbackListener);
    }
    
    /**
     * Makes the bluetooth device visible and starts a 
     * server in a new thread.
     * 
     * @throws javax.bluetooth.BluetoothStateException
     */
    public void startServer() throws BluetoothStateException {
        this.localDevice.setDiscoverable(DiscoveryAgent.GIAC);
        new Thread(this).start();
    }

    /**
     * Stops the bluetooth server, sends terminate commands to all connected
     * clients and removes them from the list.
     */
    public void stopServer() {
        log("invoke disconnect()");

        // do not accept client connections any more
        this.done = true;
        try {
            // this close will interrupt server.acceptAndOpen()
            // wake it up to exit
            if (this.server != null)
                this.server.close();
        } catch (Exception ex) {
        }
        // clean up connected endpoints
        for (int i = 0; i < endPoints.size(); i++) { 
            EndPoint endPoint = (EndPoint) endPoints.elementAt(i);
            cleanupRemoteEndPoint(endPoint); 
        }
    }

    /**
     * Gets the status of the server.
     * 
     * @return true if bluetooth server is running and waiting new connections, 
     * false if its stopping or stopped.
     */
    public boolean isListening() {
        return this.done;
    }

    /**
     * Removes the local device from inquiry mode and cancels all service
     * searches that are presently being performed.
     * <b>
     * NOTE! DiscoveryAgent#cancelServiceSearch(int) is not implemented
     * by BlueCove yet and doesn't work.
     */
    public void cancel() {
        // cancel the device inquiry if in progress
        this.agent.cancelInquiry(new Listener());

        // cancel all service inquiries
        for (int i = 0; i < this.pendingEndPoints.size(); i++) { 
            EndPoint endpt = 
                (EndPoint) this.pendingEndPoints.elementAt(i); 
            if(endpt.transId != -1) {
                this.agent.cancelServiceSearch(endpt.transId);
            }
        }
    }

    /**
     * Searches all available bluetooth devices and adds them to the GUI
     * through {@link BTListener} interface.
     * <p>
     * Before searching new devices, clean up all known connections, list of
     * devices and services.
     * 
     * @throws javax.bluetooth.BluetoothStateException
     *             if cannot inquery the bluetooth network
     */
    public void query() throws BluetoothStateException {
        // remove known endpoints
        for (int i = 0; i < this.endPoints.size(); i++) {
            EndPoint endpt = (EndPoint) this.endPoints.elementAt(i);
            cleanupRemoteEndPoint(endpt);
        }

        // Although JSR-82 provides the ability to lookup
        // cached and preknown devices, we intentionally by-pass
        // them and go to discovery mode directly.
        // This allow us to retrieve the latest active parties.
        this.agent.startInquiry(DiscoveryAgent.GIAC, new Listener());
    }

    /**
     * Finds endpoint by the name.
     * @param endptName name of the device
     * @return endpoint object, or null if no match was found.
     */
    public EndPoint findEndPointByName(String endptName) {
        for (int i = 0; i < this.endPoints.size(); i++) {
            EndPoint endpt = (EndPoint) this.endPoints.elementAt(i);
            if (endpt.getName().equals(endptName)) {
                return endpt;
            }
        }
        return null;
    }
    
    /**
     * Finds endpoint by the remote device.
     * @param rdev remote device object
     * @return endpoint object, or null if no match was found.
     */
    private EndPoint findEndPointByRemoteDevice(RemoteDevice rdev) {
        for (int i = 0; i < this.endPoints.size(); i++) {
            EndPoint endpt = (EndPoint) this.endPoints.elementAt(i);
            if (endpt.getRemoteDevice().equals(rdev)) {
                return endpt;
            }
        }
        return null;
    }

    /**
     * Finds endpoint by the discovery id.
     * @param id id of the device
     * @return endpoint object, or null if no match was found.
     */
    private EndPoint findEndPointByTransId(int id) {
        for (int i = 0; i < this.pendingEndPoints.size(); i++) {
            EndPoint endpt = (EndPoint) this.pendingEndPoints.elementAt(i);
            if (endpt.transId == id) {
                return endpt;
            }
        }
        return null;
    }
    
    /**
     * Sends an event to listening {@link BTListener} implementations.
     * @param eventType type of the event
     * @param source endpoint where the event originates
     * @param payload payload data depending on the eventType
     */
    protected void fireEvent(String eventType, EndPoint source, Object payload) {
        for(Enumeration e = this.listeners.elements(); e.hasMoreElements();) {
            ((BTListener)e.nextElement()).handleAction(eventType, source, payload);
        }
    }

    /**
     * Closes the connection and removes the endpoint.
     * <p>
     * Use {@link Service#closeConnection()} to close the connection
     * only, this method will also remove the device from the list of known
     * devices.
     * 
     * @param endpt endpoint to be removed
     */
    public void cleanupRemoteEndPoint(EndPoint endpt) {
        log("cleaning " + endpt);
        
        // close all service connections
        Service[] services = endpt.getConnections();
        for(int i=0; i < services.length; i++) {
            services[i].closeConnection();
        }

        // remove this end point from the active end point list
        this.endPoints.removeElement(endpt);

        // emit LOST event to BTListener implementation
        fireEvent(BTListener.EVENT_LOST, endpt, null);
    }

    /**
     * Bluetooth server run in this loop. The server is first initialised then
     * client connections are waited until {@link BTService#stopServer()} method is
     * called. 
     * <br>
     * All new connections are passed to new {@link EndPoint} object that 
     * handles the communication with the remote service.
     */
    public void run() {
        try {
            // Create a server connection object, using a
            // Serial Port Profile URL syntax and our specific UUID
            // and set the service name to BlueChatApp
            server = (StreamConnectionNotifier) 
            	Connector.open("btspp://localhost:" + uuid.toString()
                            + ";name=GBAgent");
            // Retrieve the service record template
            ServiceRecord rec = localDevice.getRecord(server);
            // set ServiceRecrod ServiceAvailability (0x0008) attribute to
            // indicate our service is available, 0xFF indicate fully available status
            rec.setAttributeValue(0x0008, new DataElement(DataElement.U_INT_1,
                    0xFF));
            // print the service record, which contains some default values
            // Util.printServiceRecord(rec);

            // Set the Major Service Classes flag in Bluetooth stack.
            rec.setDeviceServiceClasses(SERVICE_OBJECT_TRANSFER);
        } catch (Exception e) {
            System.out.println("cannot start the bluetooth server: "
                    + e.toString());
            this.done = true;
        }
        while (!this.done) {
            // connection to remote device
            StreamConnection c = null;
            try {
                // this message is to inform user that the server is up and ready
                log("Ready to accept connection...");

                // start accepting client connection.
                // This method will block until a client connected
                c = server.acceptAndOpen();
                log("connection accepted");

                // retrieve the remote device object
                RemoteDevice rdev = RemoteDevice.getRemoteDevice(c);

                // check to see if the EndPoint already exist
                EndPoint endpt = findEndPointByRemoteDevice(rdev);
                if (endpt != null) {
                    // this is a safe guard to assure that this client
                    // has not been connected before
                    log("client connection end point already exist.. ignore this connection");
                } else {
                    // create a new EndPoint object and open the connection
                    endpt = new EndPoint(rdev, null);
                    endpt.addService(c).openConnection();

                    // add this EndPoint to the active list and inform UI
                    this.endPoints.addElement(endpt);
                    fireEvent(BTListener.EVENT_COMPATIBLE, endpt, null);

                    log("a new active EndPoint is established. name="
                            + endpt.getName());
                }
            } catch (Exception e) {
                System.out.println("bluetooth connection failed "
                        + e.toString());
                // if any exception happen, we assume this connection is
                // failed and close it. closing the connection will cause
                // the reader and sender thread to exit (because they will got
                // exception as well).
                if (c != null) {
                    try {
                        c.close();
                    } catch (IOException e2) {
                    }
                }
            } finally {
                // nothing to be done here
            }
        }
    }

    public void log(String s) {
        System.out.println(s);
        //((BTListener)this.listeners.get(0)).log("N " + s);
    }

    /**
     * Listener class for device and service discovery.
     */
    class Listener implements DiscoveryListener {
        /**
         * A device is discovered. Create a EndPoint for the device discovered
         * and put it on the pending list. A service search will happen when all
         * the qualifying devices are discovered.
         * 
         * @param remoteDevice
         * @param deviceClass
         */
        public void deviceDiscovered(RemoteDevice remoteDevice,
                DeviceClass deviceClass) {
            if (findEndPointByRemoteDevice(remoteDevice) != null) {
                log("discovered device " 
                        + remoteDevice.getBluetoothAddress() 
                        + " already exists");
                return;
            }
            //log("invoke deviceDiscovered name=" + name);

            //int SERVICE_ALL = 0x100000;
            //if ((deviceClass.getServiceClasses() /*&
            // SERVICE_OBJECT_TRANSFER*/) != 0) {
            // create a inactive EndPoint and put it on the pending list
            EndPoint endpt = new EndPoint(remoteDevice, deviceClass);
            BTService.this.fireEvent(BTListener.EVENT_DISCOVERED, endpt, null);
            BTService.this.pendingEndPoints.addElement(endpt);
            //} else {
            //	log("found device of wrong type, ignore this device...");
            //}
        }

        /**
         * Device discovery completed. After device inquery completed, we start
         * to search for GBAgent services. We loop through all the pending
         * EndPoints and request agent.searchServices on each of the remote
         * device.
         * 
         * @param transId
         */
        public void inquiryCompleted(int transId) {
            log("invoke inqueryCompleted " + BTService.this.pendingEndPoints.size());
            
            // start doing service discovery after a while
            fireEvent(BTListener.EVENT_SERVICES, null, null);
            timer.schedule(new DoServiceDiscovery(), 100);
        }

        /**
         * A service is discovered from a remote device.
         * <p>
         * Note: we know the found service is wanted service because we search
         * on specific UUID,
         * 
         * @param transId
         * @param svcRec
         */
        public void servicesDiscovered(int transId, ServiceRecord[] svcRec) {
            log("invoke servicesDiscovered:" + transId + "," + svcRec.length);
            EndPoint endpt = findEndPointByTransId(transId);
            for (int i = 0; i < svcRec.length; i++) {
                // Util.printServiceRecord(svcRec[i]);
                
                //EndPoint endpt =
                // findEndPointByRemoteDevice(svcRec[i].getHostDevice());
                /**
                 * TODO: There might be multiple instances of services on the
                 * device, we assume that the last one is the valid one. The
                 * device is always the same, but the URL might be different, so
                 * we update only the URL.
                 */
                /*
                 * EndPoint existing =
                 * findEndPointByRemoteDevice(svcRec[i].getHostDevice()); if
                 * (existing != null) { existing.url =
                 * svcRec[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT,
                 * false); log("skipping " + endpt.getName()); } else {
                 */
                fireEvent(BTListener.EVENT_COMPATIBLE, endpt, null);
                endpt.addService(svcRec[i]);
                
                log("a valid service added name=" + endpt.getName() + " : "
                        + endpt.getName());
                //}
            }
            BTService.this.endPoints.addElement(endpt);
        }

        /**
         * Service discovery is completed.
         * 
         * @param transID
         * @param respCode
         */
        public void serviceSearchCompleted(int transID, int respCode) {
            // print response code
            if (respCode == SERVICE_SEARCH_COMPLETED)
                log("SERVICE_SEARCH_COMPLETED");
            else if (respCode == SERVICE_SEARCH_TERMINATED)
                log("SERVICE_SEARCH_TERMINATED");
            else if (respCode == SERVICE_SEARCH_ERROR)
                log("SERVICE_SEARCH_ERROR");
            else if (respCode == SERVICE_SEARCH_NO_RECORDS)
                log("SERVICE_SEARCH_NO_RECORDS");
            else if (respCode == SERVICE_SEARCH_DEVICE_NOT_REACHABLE)
                log("SERVICE_SEARCH_DEVICE_NOT_REACHABLE");
            else
                log("invoke serviceSearchCompleted: " + transID);

            // removes the transaction ID
            findEndPointByTransId(transID).transId = -1;
            serviceSearchCount--;

            synchronized (lock) {
                // unlock to proceed to service search on next device
                // see DoServiceDiscovery.run()
                lock.notifyAll();
            }
        }
    }

    /**
     * Service discovery is initialised here after a small delay.
     */
    class DoServiceDiscovery extends TimerTask {
        public void run() {
            //log("searching services");
            // for each EndPoint, we search for services
            for (int i = 0; i < BTService.this.pendingEndPoints.size(); i++) {
                EndPoint endpt = (EndPoint) BTService.this.pendingEndPoints.elementAt(i);
                try {
                    log("searching service from " + endpt.getName());

                    // searchServices return a transaction id, which we will used to
                    // identify which remote device the service is found
                    endpt.transId = agent.searchServices(null, // retrieve default attributes
                            new UUID[] { new UUID(0x0100) }, //search criteria, 0x0100 = L2CAP
                            endpt.getRemoteDevice(), new Listener());

                    /*
                     * Determine if another search can be started.  If not, wait for
                     * a service search to end.
                     */
                    synchronized (lock) {
                        serviceSearchCount++;
                        if (serviceSearchCount == maxServiceSearches) {
                            try {
                                lock.wait();
                            } catch (InterruptedException ie) {
                            }
                        }
                    }
                } catch (BluetoothStateException bse) {
                    System.out.println("failed to discover services "
                            + bse.toString());
                }
            }
            // no more service to discovery. so any pending EndPoints
            // will be ignored and removed
            BTService.this.pendingEndPoints.removeAllElements();
            // this message is to inform user that chatting can start
            fireEvent(BTListener.EVENT_FINISHED, null, null);
        }
    }
}



See more files for this project here

GridBlocks

GridBlocks builds a grid application framework via easy-to-use building blocks in distributed environment. The framework offers components for Grid security, distributed storage, computing, and Portlet web interfaces.

Project homepage: http://sourceforge.net/projects/gridblocks
Programming language(s): Java,JSP,XML
License: other

  coordconv/
    LatitudeLongitude.java
  util/
    Properties.java
    StringTokenizer.java
  AgentService.java
  BTListener.java
  BTService.java
  EndPoint.java
  GPSResult.java
  NmeaService.java
  Service.java
  Util.java