Bluetooth connections - BCExchanger MIDlet

This section provides the source code for the BCExchanger. For the complete Eclipse project ZIP file, see Forum Nokia.

The example includes the following classes:

BCEchangerMIDlet

package bcexchanger;

import java.util.Vector;

import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import bcexchanger.comm.ExchangeListener;
import bcexchanger.comm.ExchangerComm;
import bcexchanger.comm.ExchangerCommImpl;
import bcexchanger.ui.AddressBookScreen;
import bcexchanger.ui.AlertMessage;
import bcexchanger.ui.MainScreen;
import bcexchanger.ui.ProgressScreen;
import bcexchanger.ui.Screen;
import bcexchanger.ui.ServiceListScreen;

/**
 * 
 * This is the main application class which extends MIDlet class. It
 * serves as a parent to UI states (package example.BCExchanger.ui)
 * and listens to communicational events by implementing
 * ExchangeListener interface
 * 
 * @see example.BCExchanger.comm.ExchangeListener
 */
public class BCExchangerMIDlet extends MIDlet
    implements ExchangeListener {

  private boolean commIsInitialized = false;
  private ExchangerComm comm;
  private int selectedServiceIndex;
  private MainScreen scr = new MainScreen(this);
  
  /**
   * Constructor
   */
  public BCExchangerMIDlet() { }

  /**
   * Changes the current UI screen
   * 
   * This method is used to display and make active a UI screen
   * 
   * @param screen -
   *          UI screen to display
   * @see example.BCExchanger.ui.Screen
   */
  public void changeScreen(Screen screen) {
    screen.makeActive();
  }

  /**
   * Called to force termination of MIDlet
   */
  public void quit() {
    if (comm != null) {
      comm.cancelWaiting();
    }

    try {
      destroyApp(true);
      notifyDestroyed();
    } catch (MIDletStateChangeException e) {
      // Ignore, we are closing anyways
    }
  }

  /**
   * Saves the index of the service of choice
   * 
   * This method is used in case several services are found during
   * service discovery In this case the ServiceListScreen is displayed
   * and on user selection this method is called to save the index of
   * the service of choice.
   * 
   * @param index -
   *          index of the serivice chosen by the user
   */
  public void serviceSelected(int index) {
    selectedServiceIndex = index;
  }

  /**
   * Intitializes the communication module
   */
  public void initComm() 
  {
    if (!commIsInitialized) { // avoid double initializing
      comm = new ExchangerCommImpl(this);
      commIsInitialized = true;
    }
  }

  /**
   * Getter method for Exchanger - class representing communication
   * module
   * 
   * @return a class implementing ExchangerComm interface,
   *         representing communication module
   */
  public ExchangerComm getExchanger() {
    return comm;
  }

  protected void destroyApp(boolean arg0)
      throws MIDletStateChangeException {}

  protected void startApp() throws MIDletStateChangeException {
    // ensure that we have own business card selected
    String uid = null;
    try {
      uid = Storage.getBCUID();
    } catch (Exception e) {
      changeScreen(new AlertMessage(this,
          "Cannot use the persistent storage. Application will exit"));
      return;
    }

    if (uid == null) {
      // if the MIDlet has not been started before
      // and own business card has not been chosen
      changeScreen(new AddressBookScreen(this));
    } else {
      changeScreen(scr);
    }
  }

  protected void pauseApp() {}

  public void onInquiryComplete(int code) {
    if (code == ExchangerComm.DONE) {
      String text = "Searching for Business Card Exchange service...";
      changeScreen(new ProgressScreen(this, text));
    } else if (code == ExchangerComm.ERROR) {
      changeScreen(new AlertMessage(this, "Errors during inquiry",
         scr));
    } else if (code == ExchangerComm.CANCELED) {
      changeScreen(new AlertMessage(this, "Inquiry is canceled",
          scr));
    } else {
      // unknwon error
      throw new RuntimeException("Internal error #26");
    }

  }

  public void onServiceDiscoveryComplete(int code) 
  {
    if (code == ExchangerComm.DONE) 
    {
      String text = "Sending own business card...";
      changeScreen(new ProgressScreen(this, text));
    }
    else if (code == ExchangerComm.NO_RECORDS) {
      changeScreen(new AlertMessage(
          this,
          "Cannot find Business card exchange service on the devices",
          scr));
    } else if (code == ExchangerComm.ERROR) {
      changeScreen(new AlertMessage(this,
          "Errors during service discovery", scr));
    } else if (code == ExchangerComm.CANCELED) {
      changeScreen(new AlertMessage(this,
          "Service discovery is canceled",scr));
    } else {
      // unknown error
      throw new RuntimeException("Internal error #27");
    }
  }

  public synchronized int resolveMultipleServices(Vector friendlyNames) throws InterruptedException {
    changeScreen(new ServiceListScreen(this, friendlyNames));

    wait(); // wait until the choise is made

    return selectedServiceIndex;

  }

  public byte[] getOwnBC() throws Exception {
    byte[] vCard = null;
    String uid;
    try {
      uid = Storage.getBCUID();
      vCard = AddressBook.getVCard(uid);
    } catch (Exception e) 
    {
      throw new Exception("getOwnBC did not succeed");
    }
    return vCard;
  }

  public void onSendComplete(int code) {

    if (code == ExchangerComm.DONE) {
      String text = "Send was successful!";
      changeScreen(new AlertMessage(this, text,
              scr));
    } else if (code == ExchangerComm.CANCELED) {
      changeScreen(new AlertMessage(this, "Send is canceled ",
          scr));
    } else { // error
    	
      changeScreen(new AlertMessage(this, "Errors during sending ",
    		 scr ));
    }
  }


  public void onReceiveComplete(int code) {
    if (code == ExchangerComm.DONE) {
      changeScreen(new AlertMessage(this, "Receiving was successful!",
          scr));    	
    } else if (code == ExchangerComm.CANCELED) {
      changeScreen(new AlertMessage(this, "Receiving is canceled ",
          scr));
    } else { // error
      changeScreen(new AlertMessage(this, "Errors during receiving ",
          scr));
    }
  }

  public void onReceiveBC(byte[] vCard) throws Exception {
   AddressBook.setVCard(vCard);
  }

  /**
   * This is a helper methods which unblocks waiting code in
   * resolveMultipleServices()
   */
  public synchronized void unblock() {
    notify(); // let resolveMultipleServices complete
  }

  public void onGetComplete(int code) {

  }

  public void onPutComplete(int code) {

  }

  /**
   * Called in case of problems with Bluetooth OBEX server accepting connection
   * 
   */
  public void onServerError() {
    changeScreen(new AlertMessage(this,
    "Fatal Bluetooth error. Application will exit"));
    return;
  }

}

AddressBook

package bcexchanger;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.pim.Contact;
import javax.microedition.pim.ContactList;
import javax.microedition.pim.PIM;
import javax.microedition.pim.PIMItem;

/**
 * 
 * This class provides an abstraction of the address book to
 * application. It implements the access to the phone address book and
 * provides information in the form the application needs
 */
public class AddressBook {

  /**
   * This methods get all names from the address book and
   * corresponding uids.
   * 
   * This method retrieves full names from the address book and
   * corresponding uinque identifiers (uids)
   * 
   * @return two vectors: first is vector of names, second is the
   *         vector of corresponding UIDs
   * @exception Exception -
   *              in case any errors occur while working with address
   *              book
   */
  static public Vector[] getNamesUIDLists() throws Exception {
	  Vector[] namesUIDs = new Vector[2];
    namesUIDs[0] = new Vector();
    namesUIDs[1] = new Vector();
  
    // get the name of the address book
    PIM pim = PIM.getInstance();
    String listNames[] = pim.listPIMLists(PIM.CONTACT_LIST);
    if (listNames.length == 0) {
      throw new RuntimeException("No available Contact lists");
    }

    // opent the address book
    ContactList contactList = (ContactList) pim.openPIMList(
        PIM.CONTACT_LIST, PIM.READ_ONLY, listNames[0]);

    // veryfy the that the required list are supported
    if (!contactList.isSupportedField(Contact.UID)) {
      throw new RuntimeException(
          "Required field (UID) is not supported by the address book");
    }
    
    // list of preferd order of the name fields
    Vector fieldOrder = new Vector();
    fieldOrder.addElement(new Integer(Contact.FORMATTED_NAME)); // For Series40
    fieldOrder.addElement(new Integer(Contact.NAME_FAMILY));
    fieldOrder.addElement(new Integer(Contact.NAME_GIVEN));
    fieldOrder.addElement(new Integer(Contact.NAME)); // fix
    
    Vector supportedFieldOrder = new Vector();
    // create the supported list
    for(int i=0; i < fieldOrder.size(); i++) {

    	Integer field = (Integer) fieldOrder.elementAt(i);
    	if(contactList.isSupportedField(field.intValue())) {
    		supportedFieldOrder.addElement(field);
    	}
    }

    // in case none of the names is supported
    if(supportedFieldOrder.size() == 0) {
        throw new RuntimeException(
        "Required name field is not supported by the address book");
    }
    
    // get the list of contacts and uids
    Enumeration e = contactList.items();
    while (e.hasMoreElements()) {      
    	Contact contact = (Contact) e.nextElement();
      try {      
      	
      	// try to obtain any supported name
      	String name = null;
      	Enumeration e2 = supportedFieldOrder.elements();
      	while(e2.hasMoreElements()) {
      	  try {
      	  	int field = ((Integer) e2.nextElement()).intValue();

      	  	if (field == Contact.NAME)
      	  	{
      	  		String[] nameArray = contact.getStringArray(field,0);
      	  		if (nameArray != null) name = nameArray[0];
      	  	}
            else
            {
          	  	name = contact.getString(field, 0);
            }

      	  	if(name != null && name != "") {
      	  		break;
      	  	} else {
      	  		name = null;
      	  	}
      	  } catch (Exception exception) {
      	  	name = null;
      	  }
      	}
        
      	// this contact does not have a suitable name, continue with the next
      	if(name == null) {
      		continue;
      	}
        String uid = contact.getString(Contact.UID, 0);
  
        namesUIDs[0].addElement(name);
        namesUIDs[1].addElement(uid);
      } catch (IndexOutOfBoundsException ex) {
        // Ignore, this is a malformed contact and we'll omit it
      }
    }
    
    return namesUIDs;
  }
  
  
 
  /**
   * Get vCard of the address book entry with the particular uid
   * 
   * Longer description
   * 
   * @param uid -
   *          unique identifier of the requested address book entry
   * @return byte array which contains a vCard
   * @exception Exception -
   *              in case any error occur
   */
  static public byte[] getVCard(String uid) throws Exception {
    ByteArrayOutputStream os = new ByteArrayOutputStream();

    // create a search pattern (based on uid)
    ContactList contactList = (ContactList) PIM.getInstance()
        .openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY);
    Contact searchPattern = contactList.createContact();
    searchPattern.addString(Contact.UID, PIMItem.ATTR_NONE, uid);

    Enumeration e = contactList.items(searchPattern);

    // get the first found contact
    if (e.hasMoreElements()) 
    {
      Contact contact = (Contact) e.nextElement();
      PIM.getInstance().toSerialFormat(contact, os, "UTF-8",
          "VCARD/2.1");
    } else 
    {
      throw new RuntimeException("No contacts found");
    }

    return os.toByteArray();
  }

  /**
   * Saves a vCard to the address book
   * 
   * Creates a new entry based on the infromation from the vCard in
   * the phones address book
   * 
   * @param vCard -
   *          byte array which contains a vCard
   * @exception Exception -
   *              in case any errors occur
   */
  static public void setVCard(byte[] vCard) throws Exception {
    ByteArrayInputStream in = new ByteArrayInputStream(vCard);

    // get address book entry from the input stream
    PIMItem[] items = PIM.getInstance().fromSerialFormat(in, "UTF-8");

    // takes the first one discards the others
    ContactList contactList = (ContactList) PIM.getInstance()
        .openPIMList(PIM.CONTACT_LIST, /* PIM.WRITE_ONLY */
        PIM.READ_WRITE);
    Contact newContact = contactList
        .importContact((Contact) items[0]);
    
    newContact.commit();
    
  }

}

Storage

package bcexchanger;

import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;

/**
 * 
 * This class abstracts an RMS system for storing the uid of the
 * contact entry from the address book, selected by user as own entry.
 * 
 * @version 1.0 29.09.2005
 */
public class Storage {

  /**
   * Gets the stored uid
   * 
   * @return string containing uid of the the contact entry from the
   *         address book, selected by user as own entry or null if no
   *         contact is previously selected
   * @exception Exception -
   *              in case of errors occur
   */
  static public String getBCUID() throws Exception {
    String uid = null;
    RecordStore db = RecordStore.openRecordStore("uid", true);

    RecordEnumeration e = db.enumerateRecords(null, null, false);

    if (!e.hasNextElement()) {
      uid = null;
    } else {
      byte[] record = e.nextRecord();
      if (record == null) {
        uid = null;
      } else {
        uid = new String(record);
      }
    }
    db.closeRecordStore();

    return uid;
  }

  /**
   * Stores the uid
   * 
   * @param uid -
   *          string containing uid of the the contact entry from the
   *          address book, selected by user as own entry
   * @exception Exception -
   *              in case of errors occur
   */
  static public void setBCUID(String uid) throws Exception {
    RecordStore db = RecordStore.openRecordStore("uid", true);

    // delete all records
    if (db.getNumRecords() != 0) {
      db.closeRecordStore();
      RecordStore.deleteRecordStore("uid");
      db = RecordStore.openRecordStore("uid", true);
    }

    byte[] record = uid.getBytes();
    db.addRecord(record, 0, record.length);

    db.closeRecordStore();
  }

}

ExchangeListener

package bcexchanger.comm;

import java.util.Vector;

/**
 * 
 * ExchangeListner is an callback interface. By means of the interface
 * function OBEX communication module signal to a listener about
 * various networking events.
 * 
 * This interface is implemented by the BCExchangerMIDlet and OBEX
 * communication classes located in the package bcexchanger.comm use
 * it for signaling
 * 
 * @version 1.0 27.09.2005
 */
public interface ExchangeListener {

  /**
   * Called when the OBEX communication module completes the inquiry
   * 
   * @param code -
   *          inquiry completion code
   */
  public void onInquiryComplete(int code);

  /**
   * Called when the OBEX communication module completes service
   * discoveries
   * 
   * This method is called when service discovery on all devices is
   * complete
   * 
   * @param
   * @param code -
   *          service discovery completion code
   */
  public void onServiceDiscoveryComplete(int code);

  /**
   * This method is called when OBEX communication module needs to
   * resolve services
   * 
   * If several services are found, the OBEX communcation module asks
   * the MIDlet to resolve the services and choose the only one to
   * connect to
   * 
   * @param friendlyNames -
   *          list of friendly names of devices which has the service
   * @return index of the chosen device/service from the vector
   * @throws InterruptedException
   */
  public int resolveMultipleServices(Vector friendlyNames) throws InterruptedException;

  /**
   * This method returns own business card serialized to vCard/2.1
   * format
   *
   * When OBEX module needs to send the own business card to a remote
   * device it uses this method to request own business card from the
   * MIDlet
   * 
   * @return byte array containing own business card serialized to
   *         vCard/2.1 format null if there were errors or cancelation
   * @throws Exception
   */
  public byte[] getOwnBC() throws Exception;

  /**
   * Called when business card send (Client OBEX PUT) is finished
   * 
   * @param code -
   *          completion code
   */
  public void onSendComplete(int code);

  /**
   * Called when business card receive (Client OBEX GET) is finished
   * 
   * @param code -
   *          completion code
   */
  public void onReceiveComplete(int code);

  /**
   * Called when Server OBEX GET operation is finished
   * 
   * @param code -
   *          completion code
   */
  public void onGetComplete(int code);

  /**
   * Called when Server OBEX PUT operation is finished
   * 
   * @param code -
   *          completion code
   */
  public void onPutComplete(int code);

  /**
   * Called when a business card from a remote device is received
   *
   * This method allows MIDlet to save received business card.
   * 
   * @param vCard -
   *          byte array containing receive business card serialized
   *          to vCard/2.1 format
   * @throws Exception -
   *           if saving of the business card fails
   */
  public void onReceiveBC(byte[] vCard) throws Exception;

  /**
   * Called in case of problems with Bluetooth OBEX server accepting connection
   * 
   */
  public void onServerError();
    
  
}

ExchangerComm

package bcexchanger.comm;

/**
 * 
 * This is an interface to OBEX communication module. OBEX
 * communication module implemented by the classes from
 * bcexchanger.comm package realize exhange of the business card over
 * JSR82 OBEX. The controls of the module is abstracted in this
 * interface.
 * 
 * BCExchanger is calling the methods of this interface implemented by
 * ExchangerCommImpl class to control OBEX communication.
 * 
 * @version 1.0 27.09.2005
 * 
 */
public interface ExchangerComm {

  // Constant field declaration
  static final int DONE = 0;
  static final int CANCELED = 1;
  static final int ERROR = 2;
  static final int NO_RECORDS = 3;

  /**
   * Intitiate whole procedure of seding business card to a remote
   * device
   * 
   * The method start the procedure of inquiry, service discovery,
   * tranfering own card and receiving the card from the remote
   * device. This method is asynchrounous and it returns immediately
   * after operation is started.
   * 
   * @exception Exception -
   *              if any immediate errors occur while the process is
   *              started. In practice it is thrown when inquiry
   *              cannot start or if this method is called while
   *              sending process is in progress
   */
  public void startSending() throws Exception;

  /**
   * Cancels the process of sending
   * 
   * The method cancels the current operation of the sending: inquiry,
   * service discovery, OBEX send or receive and returns to the idle
   * state.
   * 
   */
  public void cancelSending();

  /**
   * Starts to listen for incomming connections
   * 
   * The method start listening for incomming connections. This method
   * is asynchronous and it returns immediately after the connection
   * listening is started.
   * 
   */
  public void startWaiting();

  /**
   * This method stops listening for incomming connection
   * 
   * The method stops listening for incomming connection if
   * startWaiting() was called before. Otherwise method does nothing.
   * 
   */
  public void cancelWaiting();

}

ExchangerCommImpl

package bcexchanger.comm;

import java.io.IOException;

import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.obex.HeaderSet;
import javax.obex.Operation;
import javax.obex.ServerRequestHandler;
import javax.obex.SessionNotifier;

/**
 * 
 * This class is the central class in the OBEX communication module
 * (package bcexchanger.comm).
 * 
 * Class implements interface ExchangerComm and realizes the methods
 * for controling process of sending and receiving business cards. It
 * also is a parent for states of the BCExchanger communication state
 * machine. It keeps the current state of the state machine and
 * implements interface ExchangerStateParent. Using this interface
 * state classes access to the required functionality of the parent.
 * 
 * This class waits for Bluetooth incomming connection in the separate
 * thread and therefore it implments Runnable interface.
 * 
 * This class also works as a OBEX server. In order to serve OBEX
 * requests it extends the ServerRequestHandler class and overides
 * some of its methods.
 * 
 * @version 1.0 29.09.2005
 * @see bcexchanger.comm.ExchangerStateParent
 *      bcexchanger.comm.ExchangerComm javax.obex.ServerRequestHandler
 *      Design patterns: State
 * 
 */
public class ExchangerCommImpl extends ServerRequestHandler
    implements ExchangerStateParent, ExchangerComm, Runnable {

  // Instance variables
  final private String uuid = "ed495afe28ed11da94d900e08161165f";
  final private String serverURL = "btgoep://localhost:" + uuid;

  private boolean cancelWaitingInvoked = false; // becomes true if
                                                // cancelWaiting() is
                                                // called

  private Thread waitingThread;

  private ExchangeListener listener;
  private ExchangerState currentState;

  private SessionNotifier notifier = null;
  private Connection con = null;

  /**
   * Constructor
   * 
   * description
   * 
   * @param _listener -
   *          listener of the communication module events
   * @exception
   * @see
   */
  public ExchangerCommImpl(ExchangeListener _listener) 
  {
	listener = _listener;

    startWaiting();
    setState(new IdleState(this));
  }

  public void setState(ExchangerState state) {
	  
    currentState = state;
  }

  public ExchangeListener getListener() {
    return listener;
  }

  public ExchangerState getState() {
    return currentState;
  }

  public void startSending(int oper) throws Exception {
    getState().startSending(oper);
  }
  
  public void startSending() throws Exception {
	    getState().startSending(0);
	  }
  

  public void cancelSending()
  {
    getState().cancelSending();
  }

  public void startWaiting() {
    cancelWaitingInvoked = false;

    waitingThread = new Thread(this);
    waitingThread.start();
  }

  public void cancelWaiting() 
  {
    cancelWaitingInvoked = true;

    try 
    {
      notifier.close(); // indicate to acceptAndOpen that it is
                        // canceled
    } catch (IOException e) {
      // Ignore, we're closing anyways
    }
  }

  public synchronized void run() {

  	// initialize stack and make the device discoverable
	try 
	{
      LocalDevice local = LocalDevice.getLocalDevice();
	  local.setDiscoverable(DiscoveryAgent.GIAC); 
	} catch (Exception e) 
	{
	  // catching notifier exception
	  listener.onServerError();
	  return;
	}
	try 
	{
		notifier = (SessionNotifier) Connector.open(serverURL);
	} 
	catch (IOException e2) 
	{
		
	}
    // the cycle stops only if cancelWaiting() was called
    while (!cancelWaitingInvoked) 
    {
      try 
      {      

        try
        {
          con = notifier.acceptAndOpen(this);
          wait(); // wait until the remote peer disconnects
          try 
          {
        	con.close();
          } 
          catch (IOException e0) 
          { }
        } 
        catch (Exception e1) 
        {
        	listener.onServerError();
        	return;
        }
      } 
      catch (Exception e) 
      {
        listener.onServerError();
        return;
      }

    }

  }

  /*
   * This method is related to OBEX server functionality. This method
   * is delegating this execution to the current state
   * 
   * @see javax.obex.ServerRequestHandler#onGet()
   */
  public int onGet(Operation op) {
    return getState().onGet(op);
  }

  /*
   * This method is related to OBEX server functionality. This method
   * is delegating this execution to the current state
   * 
   * @see javax.obex.ServerRequestHandler#onPut()
   */
  public int onPut(Operation op) {
    return getState().onPut(op);

  }

  /*
   * This method is related to OBEX server functionality. This method
   * handles OBEX DISCONNECT command from the remote device.
   * 
   * @see javax.obex.ServerRequestHandler#onDisconnect()
   */
  public synchronized void onDisconnect(HeaderSet request,
      HeaderSet reply) {
    super.onDisconnect(request, reply);
    notify();// stops waiting in run()

  }

  public String getUUID() {
    return uuid;
  }

}

ExchangerState

package bcexchanger.comm;

import javax.obex.Operation;

/**
 * 
 * This is the abstract base class for the all states of the
 * communication state machine. Each state is implementing methods
 * which are can be executed in that particular case.
 * 
 * Classes IdleState, InquiryState, ServiceDiscoveryState,
 * ReceiveState and SendState inherit from this class
 * 
 * @see Design patterns: State
 * 
 */
public abstract class ExchangerState {

  protected ExchangerStateParent parent;

  /**
   * Implements OBEX GET command
   * 
   * Implements server handling of the OBEX GET command
   * 
   * @param op -
   *          OBEX operation class
   * @return - OBEX response code
   * @see javax.obex.ServerRequestHandler
   */
  abstract public int onGet(Operation op);

  /**
   * Implements OBEX PUT command
   * 
   * Implements server handling of the OBEX PUT command
   * 
   * @param op -
   *          OBEX operation class
   * @return - OBEX response code
   * @see javax.obex.ServerRequestHandler
   */
  abstract public int onPut(Operation op);

  /**
   * Implements actions related to starting sending process
   * 
   * Implements reaction of the state on command to start sending
   * process
   * 
   * @exception Exception -
   *              if any immediate error occur
   * @see example.BCExchanger.comm.ExchangerComm
   */
  abstract public void startSending(int oper) throws Exception;

  /**
   * Implements actions related to canceling sending process
   * 
   * Implements reaction of the state on command to cancel sending
   * process
   * 
   * @see example.BCExchanger.comm.ExchangerComm
   */
  abstract public void cancelSending();

  /**
   * Constructor
   * 
   * @param -
   *          _parent - the class which nests the current state of the
   *          communication machine
   */
  public ExchangerState(ExchangerStateParent _parent) {
    parent = _parent;
  }

}

ExchangerStateParent

package bcexchanger.comm;

/**
 * 
 * This interface contains the methods which the states of the
 * communication state machine use to get needed functionality from
 * the parent
 * 
 * This interface is implemented by ExchangerCommImpl class.
 * 
 * @version 1.0 29.09.2005
 * 
 */
public interface ExchangerStateParent {

  /**
   * Current state setter
   * 
   * Sets the current state in the parent
   * 
   * @param state -
   *          state of communication machine
   */
  public void setState(ExchangerState state);

  /**
   * Listener getter
   *
   * Returns the communication event listener which is stored in the
   * parent
   * 
   * @return communication event listener interface
   */
  public ExchangeListener getListener();

  /**
   * Communication state getter
   * 
   * Returns current state of the communication machine which is kept
   * in the parent
   * 
   * @return current state of the communication machine
   */
  public ExchangerState getState();

  /**
   * Returns "Business Card Exchanger Service" UUID
   * 
   * @return string containing UUID of the Bluetooth OBEX server
   *         connection
   */
  public String getUUID();

}

IdleState

package bcexchanger.comm;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javax.obex.Operation;
import javax.obex.ResponseCodes;

/**
 * 
 * This class implements idle state of the communication state machine. In idle
 * state machine is waiting for external events (like incomming Bluetooth
 * connections or user command to start business card exchange)
 * 
 * This class extends ExchangerState abstract class.
 * 
 * @version 1.0 29.09.2005
 * @see bcexchanger.comm.ExchangerState Design Patterns: State
 * 
 */
public class IdleState extends ExchangerState {
	/**
	 * Constructor
	 * 
	 * @param _parent -
	 *            the class which is nesting the current state of the state
	 *            machine
	 */
	public IdleState(ExchangerStateParent _parent) {
		super(_parent);

	}

	public int onGet(Operation op) {

		try {

			OutputStream out = op.openOutputStream();
			byte[] vCard = parent.getListener().getOwnBC(); // getting the
			// own card

			int vlen = vCard.length;
			byte[] tmpBuf = new byte[vlen + 4];
			System.arraycopy(vCard, 0, tmpBuf, 4, vlen);
			tmpBuf[0] = (byte) ((vlen >>> 24) & 0xff);
			tmpBuf[1] = (byte) ((vlen >>> 16) & 0xff);
			tmpBuf[2] = (byte) ((vlen >>> 8) & 0xff);
			tmpBuf[3] = (byte) ((vlen >>> 0) & 0xff);

			out.write(tmpBuf); // sending data

			op.close();

			return ResponseCodes.OBEX_HTTP_OK;

		} catch (Exception e) {

			return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
		}

	}

	public int onPut(Operation op) {

		try 
		{
			InputStream in = op.openInputStream();
			byte[] fullResult = null;
			{
				byte[] buf = new byte[256];
				ByteArrayOutputStream bout = new ByteArrayOutputStream(2048);
				for (int len = in.read(buf); len >= 0; len = in.read(buf))
					bout.write(buf, 0, len);
				fullResult = bout.toByteArray();
			}
			ByteArrayInputStream bin = new ByteArrayInputStream(fullResult);
			DataInputStream din = new DataInputStream(bin);
			int size = din.readInt();
			byte[] vCard = new byte[size];
			din.read(vCard);
			// card is received

			op.close();
			parent.getListener().onReceiveBC(vCard);

			return ResponseCodes.OBEX_HTTP_OK;

		} 
		catch (Exception e) {

			return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
		}

	}

	public void startSending(int oper) throws Exception {
		parent.setState(new InquiryState(parent, oper));

	}

	public void cancelSending() {
		// Internal error, but not fatal
	}

}

InquiryState

package bcexchanger.comm;

import java.io.IOException;
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.obex.Operation;
import javax.obex.ResponseCodes;

/**
 * 
 * This class implements inquiry state. In inquiry state an active
 * device discovery is peformed. No OBEX server commands are served.
 * 
 * This class extends ExchangerState abstract class. This class also
 * implments DiscoveryListener interface to receive Bluetooth inquiry
 * callbacks.
 * 
 * @version 1.0 29.09.2005
 * @see bcexchanger.comm.ExchangerState
 *      javax.bluetooth.DiscoveryListener Design Patterns: State
 * 
 */
public class InquiryState extends ExchangerState
    implements DiscoveryListener {

  private DiscoveryAgent agent;
  private Vector remoteDevices; // vector of found devices
  private int operation = ServiceDiscoveryState.GET;
  
  /**
   * Constructor
   * 
   * @param _parent -
   *          the class which is nesting the current state of the
   *          state machine
   */
  public InquiryState(ExchangerStateParent _parent,int oper)
      throws IOException {
    super(_parent);
    operation = oper;
    remoteDevices = new Vector();
    // initiate Bluetooth
    LocalDevice local = LocalDevice.getLocalDevice();
    agent = local.getDiscoveryAgent();
    // start Bluetooth inquiry
    agent.startInquiry(DiscoveryAgent.GIAC, this);

  }

  /*
   * Inquiry state does not allow to start any other business card
   * exchange process.
   * 
   * @see bcexchanger.comm.ExchangerState#startSending()
   */
  public void startSending(int op) throws IOException {
    throw new IOException(
        "Inquiry is in progress. Inquiry has to be canceled before starting new sending process");
  }

  /*
   * InquiryState allows to cancel inquiry process.
   * 
   * @see bcexchanger.comm.ExchangerState#cancelSending()
   */
  public void cancelSending() {
    agent.cancelInquiry(this);
  }

  public void deviceDiscovered(RemoteDevice dev, DeviceClass devClass) 
  {
    remoteDevices.addElement(dev);
  }

  public void inquiryCompleted(int code) {
    try 
    {
    	int completionCode = ExchangerComm.ERROR;

      // convert the inquiry completion code to application completion
      // code
      switch (code) {
        case DiscoveryListener.INQUIRY_COMPLETED:
          completionCode = ExchangerComm.DONE;
          break;
        case DiscoveryListener.INQUIRY_TERMINATED:
          completionCode = ExchangerComm.CANCELED;
          break;
        case DiscoveryListener.INQUIRY_ERROR:
          completionCode = ExchangerComm.ERROR;
          break;
      }
      parent.getListener().onInquiryComplete(completionCode); // signal
                                                              // that
                                                              // inquiry
                                                              // is
                                                              // done

      if (code == DiscoveryListener.INQUIRY_COMPLETED) { // no errors
    	                                                 // or
    	                                                 // cancelations
        parent.setState(new ServiceDiscoveryState(parent,
            remoteDevices,operation));

      } else {
        parent.setState(new IdleState(parent));
      }

    } catch (Exception e) {
    	parent.setState(new IdleState(parent));
    }
  }

  /*
   * Service discovery callbacks are not handled and not supposed to
   * occur, since service discovery process is not started
   * 
   * @see javax.bluetooth.DiscoveryListener#servicesDiscovered(int,
   *      javax.bluetooth.ServiceRecord[])
   */
  public void servicesDiscovered(int arg0, ServiceRecord[] arg1) {
    throw new RuntimeException(
        "Internal error #4: InquiryState.servicesDiscovered() should not be called");

  }

  /*
   * Service discovery callbacks are not handled and not supposed to
   * occur, since service discovery process is not started
   * 
   * @see javax.bluetooth.DiscoveryListener#serviceSearchCompleted(int,
   *      int)
   */
  public void serviceSearchCompleted(int arg0, int arg1) {
    throw new RuntimeException(
        "Internal error #5: InquiryState.serviceSearchCompleted() should not be called");

  }

  /*
   * Serving OBEX GET operation is supported only in IdleState
   * 
   * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation)
   */
  public int onGet(Operation op) {

    return ResponseCodes.OBEX_HTTP_CONFLICT;
  }

  /*
   * Serving OBEX GET operation is supported only in
   * IdleState
   * 
   * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation)
   */
  public int onPut(Operation op) {
    // onPut is supported only in IdleState
    return ResponseCodes.OBEX_HTTP_CONFLICT;
  }

}

ReceiveState

package bcexchanger.comm;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.obex.ClientSession;
import javax.obex.HeaderSet;
import javax.obex.Operation;
import javax.obex.ResponseCodes;

/**
 * 
 * This class implements business card receiving state. In this state
 * an client OBEX GET operation is peformed. No OBEX server commands
 * are served.
 * 
 * This class extends ExchangerState abstract class.
 * 
 * @version 1.0 29.09.2005
 * @see bcexchanger.comm.ExchangerState Design Patterns: State
 */
public class ReceiveState extends ExchangerState{

  // Instance variables
  private ClientSession connection = null;
  private Operation operation = null;
  String url = null;

  boolean cancelInvoked = false; // true after cancelSending is
                                  // called

  /**
   * Constructor
   * 
   * description
   * 
   * @param _parent -
   *          the class which is nesting the current state of the
   *          state machine
	 * @param _url -
	 *            a URL of the remote OBEX service. This state is responsible
	 *            for establishin the connection
   * @exception
   * @see
   */
  public ReceiveState(ExchangerStateParent _parent,
      String url) 
  {
    super(_parent);
    this.url = url;
  }

  /*
   * ReceiveState does not allow to start any other business card
   * exchange process.
   * 
   * @see bcexchanger.comm.ExchangerState#startSending()
   */
  public void startSending(int to) throws IOException {

    throw new IOException(
        "Receiving is in progress. Receiving has to be canceled before starting new sending process");

  }

  /*
   * ReceiveState allows to cancel receiving process.
   * 
   * @see bcexchanger.comm.ExchangerState#cancelSending()
   */
  public void cancelSending() {
    cancelInvoked = true;

    try { // cancel any active operation running
      operation.abort();
    } catch (IOException e) {
      // Ignore, aborting operation anyway
    }

    try { // send DISCONNECT command to the remote peer
      connection.disconnect(null);
    } catch (IOException e1) {
        // Ignore, disconnecting anyway
    }

  }

  /*
   * This method implements the logic for
   * receving a business card
   * 
   */
  public void doGet()
  {
	  try 
	  {
		  connection = (ClientSession) Connector.open(url);
		  HeaderSet response = connection.connect(null);
		  operation = connection.get(null);
		  InputStream in = operation.openInputStream();
		  byte[] fullResult = null; 
		  {
			  byte[] buf = new byte[256];
			  ByteArrayOutputStream bout = new ByteArrayOutputStream(2048);
			  for (int len = in.read(buf); len >= 0; len = in.read(buf))
    		  {	
    		  	bout.write(buf, 0, len);
    		  } 
			  fullResult = bout.toByteArray();
		  }   
		  ByteArrayInputStream bin = new ByteArrayInputStream(fullResult);
		  DataInputStream din = new DataInputStream(bin);
		  int size = din.readInt();    
		  byte[] vCard = new byte[size];
		  din.read(vCard);
		  
		  parent.getListener().onReceiveBC(vCard);
		 
		  // End the transaction
		  in.close();
		  int responseCode = operation.getResponseCode();
	
		  operation.close();
		  
		  if (cancelInvoked) {
			  throw new Exception(
			  "Cancel did not invoke any exception; throw exception then");
		  }

		  // receive is done
		  try 
		  
		  {
			  parent.setState(new IdleState(parent));
			  parent.getListener().onReceiveComplete(ExchangerComm.DONE);
		  } 
		  catch (Exception e) 
		  {
		  }

    } 
	catch (Exception e1) 
	{
      if (cancelInvoked)
      { // if exception was caused by canceling
    	  parent.setState(new IdleState(parent));
        parent.getListener()
            .onReceiveComplete(ExchangerComm.CANCELED);
      } 
      else 
      { // if exception was caused by error
    	  
    	parent.setState(new IdleState(parent));
        parent.getListener().onReceiveComplete(ExchangerComm.ERROR);
      }
    } 
	finally 
	{
      try { // finalizing operation
        operation.close();
      } catch (IOException e3) 
      {
      }

      try { // sending DISCONNECT command
        connection.disconnect(null);
      } catch (IOException e2) 
      {
        // Ignore, disconnecting anyway
      }
      

    }
  }
  
  /*
   * 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;
  }


}

SendState

package bcexchanger.comm;

import java.io.IOException;
import java.io.OutputStream;

import javax.microedition.io.Connector;
import javax.obex.ClientSession;
import javax.obex.HeaderSet;
import javax.obex.Operation;
import javax.obex.ResponseCodes;

/**
 * This class implements business card sending state. In this state a client
 * OBEX PUT operation is peformed. No OBEX server commands are served.
 * 
 * This class extends ExchangerState abstract class. 
 * 
 * @version 1.0 29.09.2005
 * @see bcexchanger.comm.ExchangerState Design Patterns: State
 */
public class SendState extends ExchangerState {

	// Instance variables

	private ClientSession connection = null;

	private String url = null;

	private Operation operation = null;

	private boolean cancelInvoked = false;

	/**
	 * Constructor
	 * 
	 * @param _parent -
	 *            the class which is nesting the current state of the state
	 *            machine
	 * @param _url -
	 *            a URL of the remote OBEX service. This state is responsible
	 *            for establishin the connection
	 */
	public SendState(ExchangerStateParent _parent, String _url) {
		super(_parent);

		url = _url;
	}

	/*
	 * SendState does not allow to start any other business card exchange
	 * process.
	 * 
	 * @see bcexchanger.comm.ExchangerState#startSending()
	 */
	public void startSending(int o) throws IOException {
		throw new IOException(
				"Sending is in progress. Sending has to be canceled before starting new sending process");

	}

	/*
	 * SendState allows to cancel sending process.
	 * 
	 * @see bcexchanger.comm.ExchangerState#cancelSending()
	 */
	public void cancelSending() {

		cancelInvoked = true;

		try { // cancel any active OBEX operation
			operation.abort();
		} catch (Exception e) { // catch NullPointer exception also
			// Ignore, aborting anyway
		}

		try { // send DISCONNECT to the remote peer

			connection.disconnect(null);
		} catch (Exception e1) { // catch NullPointer exception also
			// Ignore, disconnecting anyway
		}

		try { // close the connection
			connection.close(); // 

		} catch (Exception e2) { // catch NullPointer exception also
			// Ignore, closing anyway
		}

	}

	/*
	 * This method implements the logic for sending a
	 * business card
	 * 
	 */	
	public void doSend()
	{
		try 
		{
			connection = (ClientSession) Connector.open(url);
			HeaderSet response = connection.connect(null);

			// Initiate the PUT request
			operation = connection.put(null);
			OutputStream out = operation.openOutputStream();
			//getting the own card
			byte[] vCard = parent.getListener().getOwnBC(); 
			int vlen = vCard.length;
			byte[] tmpBuf = new byte[vlen + 4];
			System.arraycopy(vCard, 0, tmpBuf, 4, vlen);
			tmpBuf[0] = (byte) ((vlen >>> 24) & 0xff);
			tmpBuf[1] = (byte) ((vlen >>> 16) & 0xff);
			tmpBuf[2] = (byte) ((vlen >>> 8) & 0xff);
			tmpBuf[3] = (byte) ((vlen >>> 0) & 0xff);
			//sending data
			out.write(tmpBuf); 
			out.close();
			int responseCode = operation.getResponseCode();

            operation.close();

			if (cancelInvoked) {
				throw new Exception(
						"Cancel did not invoke any exception; throw exception then");
			}

			// send is done
			try {				
				parent.setState(new IdleState(parent));
			} catch (Exception e) 
			{
			}
		} 
		catch (Exception e1) 
		{
			parent.setState(new IdleState(parent));
			if (cancelInvoked) 
			{ // if exception is caused by cancelation
				parent.getListener().onSendComplete(ExchangerComm.CANCELED);
			} else 
			{ // if exception is caused by error
				parent.setState(new IdleState(parent));
				parent.getListener().onSendComplete(ExchangerComm.ERROR);
			}
		}
			finally 
			{
		      try { // finalizing operation
		        operation.close();
		      } 
		      catch (IOException e3) {
		    	
		      }

		      try { // sending DISCONNECT command
		        connection.disconnect(null);
		      } 
		      catch (IOException e2) 
		      {		    	  
		        // Ignore, disconnecting anyway
		      }
		    }
	}

	/*
	 * 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;
	}


}

ServiceDiscoveryState

package bcexchanger.comm;

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;

/**
 * 
 * This class implements service discovery state. In this state an active
 * service discovery is peformed. No OBEX server commands are served.
 * 
 * This class extends ExchangerState abstract class. This class also implments
 * DiscoveryListener interface to receive Bluetooth service discovery callbacks.
 * Service discoveries are run from the separate thread therefore the class
 * implements Runnable interface
 * 
 * @version 1.0 29.09.2005
 * @see bcexchanger.comm.ExchangerState javax.bluetooth.DiscoveryListener Design
 *      Patterns: State
 * 
 */
public class ServiceDiscoveryState extends ExchangerState implements
		DiscoveryListener, Runnable {

	// 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 vecotor 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();

	}

	/*
	 * 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);

	}

	/*
	 * 
	 * 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();

	}

	/*
	 * (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 cancelation
			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 choose
				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) {
					}

			}

		}
	}

	/*
	 * 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;
	}

	/*
	 * 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;
	}
}

AddressBookScreen

package bcexchanger.ui;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.List;

import bcexchanger.*;

/**
 * 
 * This class represents a screen displaying the list of contacts of
 * the address book. The user can either choose the one contact or
 * exit the application
 * 
 * This class peforms the reading of address book entries in the 
 * separate thread, because reading the long address book might
 * take significant time
 * 
 * The class is extending an example.BCExchanger.ui.Screen class and
 * implements Runnable interface.
 * 
 * @see example.BCExchanger.ui.Screen Design patterns: State
 */
public class AddressBookScreen extends Screen implements Runnable {

  private Vector names;
  private Vector uids;
  private List list;
  private Command okCommand;
  private Command exitCommand;

  private Gauge gauge;
  private Form progressBar;
  private Thread thread;
  private Command progressExitCommand;
  
  /**
   * Constructor
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   */
  public AddressBookScreen(BCExchangerMIDlet _midlet) {
    super(_midlet);
   
    // create and add commands to ui list
    progressBar = new Form("Please wait");

    gauge = new Gauge("Reading contacts", false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING);
    gauge.setLayout(Item.LAYOUT_CENTER);
    gauge.setLayout(Item.LAYOUT_VCENTER);
    progressBar.append(gauge);
    
    progressExitCommand = new Command("Exit", Command.EXIT, 1);
    progressBar.addCommand(progressExitCommand);

    progressBar.setCommandListener(this);

    displayable = progressBar;


    // start the thread reading the address book entries
    thread = new Thread(this);
    thread.start();
    
  }

  public void commandAction(Command command, Displayable _displayable) {

    // in case this callback is called from wrong displayble
    if (_displayable != list && _displayable != progressBar) {
      throw new RuntimeException(
          "Internal error #14: AddressBookScreen.commandAction()");
    }

    if (command == okCommand) {

      String uid = null;

      // determine the index
      try {
        uid = (String) uids.elementAt(list.getSelectedIndex());
      } catch (Exception e) {
        throw new RuntimeException("Internal error #15");
      }

      // save UID of the chosen card
      try {
        Storage.setBCUID(uid);
        midlet.changeScreen(new MainScreen(midlet));
      } catch (Exception e1) {
        midlet.changeScreen(new AlertMessage(midlet,
            "Cannot save own business card", null));
      }
    } else if (command == exitCommand || command == progressExitCommand) {
      midlet.quit();
    } else {
      // unknown command
      throw new RuntimeException("Internal error #20");
    }
  }

  /* (non-Javadoc)
   * @see java.lang.Runnable#run()
   */
  public void run() {
    
    // get the list of names and UIDs
    Vector[] v;
    try {
      v = AddressBook.getNamesUIDLists();
      names = v[0];
      uids = v[1];
    } catch (Exception e) {
      midlet.changeScreen(new AlertMessage(midlet,
          "Cannot read contact list data. Application will exit")); // quit
                                                                    // after
                                                                    // this
                                                                    // alert
      return;
    }

    // create a UI list of names
    list = new List("Choose own business card", List.EXCLUSIVE);

    Enumeration e = names.elements();
    if (!e.hasMoreElements()) { // names list is empty
      midlet
          .changeScreen(new AlertMessage(
              midlet,
              "Contact list is empty. Please create own contact. Application will exit")); // quit
                                                                                            // after
                                                                                            // this
                                                                                            // alert

      return;
    } else {
      while (e.hasMoreElements()) {
        // browse through names list and
        // add them to ui list
        list.append((String) e.nextElement(), null);
      }

      // create and add commands to ui list
      okCommand = new Command("Ok", Command.OK, 1);
      exitCommand = new Command("Exit", Command.EXIT, 1);

      list.addCommand(okCommand);
      list.addCommand(exitCommand);

      list.setCommandListener(this);

      displayable = list;

      midlet.changeScreen(this);
	
    }
    
  }
}

AlertMessage

package bcexchanger.ui;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import bcexchanger.*;

/**
 * 
 * This class represents a screen displaying an alert message. The
 * class is extending an example.BCExchanger.ui.Screen class
 * 
 * @version 1.0 20.09.2005
 * @see example.BCExchanger.ui.Screen Design patterns: State
 */
public class AlertMessage extends Screen {

  Alert alert;
  Screen nextScreen = null;
  boolean quitAfterDismiss = false;

  /**
   * Constructor
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   * @param message -
   *          text to display
   * @param _nextScreen -
   *          UI screen should be displayed after the alarm message is
   *          dismissed
   */
  public AlertMessage(BCExchangerMIDlet _midlet, String message,
      Screen _nextScreen) {
    super(_midlet);

    nextScreen = _nextScreen;
    init(message);
  }

  /**
   * Constructor
   * 
   * If this constructor is used, the application will exit after the
   * alert is dismissed
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   * @param message -
   *          text to display
   */
  public AlertMessage(BCExchangerMIDlet _midlet, String message) {
    super(_midlet);

    //quitAfterDismiss = true;
    init(message);
  }

  /**
   * Initialise the displayable
   * 
   * @param message -
   *          text to display
   */
  private void init(String message) {

    alert = new Alert("", message, null, null);
    alert.setCommandListener(this);

    displayable = alert;
  }

  public void commandAction(Command command, Displayable _displayable) {
    if (_displayable != alert) {
      // wrong displayable
      throw new RuntimeException("Internal error #28");
    }

    if (command == Alert.DISMISS_COMMAND) {
      if (quitAfterDismiss) {
        midlet.quit();
      } else {
        if (nextScreen != null) {
          midlet.changeScreen(nextScreen);
        }
      }
    } else {
      // unknown command
      throw new RuntimeException("Internal error #29");
    }
  }

}

MainScreen

package bcexchanger.ui;

import java.io.IOException;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import bcexchanger.*;
import bcexchanger.comm.ExchangerCommImpl;
import bcexchanger.comm.ServiceDiscoveryState;

/**
 * 
 * This class implements the main screen of the application. Using
 * this screen user can initiate an exchange of business cards, change
 * own business card and exit the application
 * 
 * The class is extending an example.BCExchanger.ui.Screen class
 * 
 * @version 1.0 29.09.2005
 * @see example.BCExchanger.ui.Screen Design patterns: State
 */
public class MainScreen extends Screen {

  private Command changeBCCommand;
  private Command exitCommand;
  private Command sendBCCommand;
  private TextBox textBox;
  
  private int typeOfOperation = ServiceDiscoveryState.PUT;

  /**
   * Constructor
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   */
  public MainScreen(BCExchangerMIDlet _midlet) 
  {
    super(_midlet);
    midlet.initComm(); // init happens only the first time MainScreen
                        // is created
    String text = "Business Card Exchanger allows you to define your own business card "
        + "and exhange it with business cards of other users in vicinity "
        + "running the same application. The exchange is done over Bluetooth "
        + "and free of charge.\n\n"
        + "Use 'Change own card' to choose own card from the address book.\n"
        + "Use 'Exchange card' to initate the search and exchange of the cards.\n\n"
        + "The application is always waiting for connections from remote devices.";
   
    textBox = new TextBox("Business Card Exchanger", text, 999, TextField.UNEDITABLE);

    changeBCCommand = new Command("Change own card", Command.ITEM, 1);
    exitCommand = new Command("Exit", Command.EXIT, 1);
    sendBCCommand = new Command("Exchange cards", Command.ITEM, 1);

    textBox.addCommand(changeBCCommand);
    textBox.addCommand(exitCommand);
    textBox.addCommand(sendBCCommand);
    textBox.setCommandListener(this);

    displayable = textBox;
  }

  public void commandAction(Command command, Displayable _displayable) {

    if (_displayable != textBox) {
      throw new RuntimeException("Internal error #16");
    }
    if (command == sendBCCommand) {
      try 
      {    	  
    	  setTypeOfOperation(ServiceDiscoveryState.PUT);
    	  ((ExchangerCommImpl)midlet.getExchanger()).startSending(ServiceDiscoveryState.PUT);
    	  String text = "Searching for devices...";
    	  midlet.changeScreen(new ProgressScreen(midlet, text));
      } 
      catch (IOException e0) {
        midlet.changeScreen(new AlertMessage(midlet,
            "Cannot start inquiry "+e0, null));
      } catch (Exception e1) {
      }
    } 
   
    else if (command == changeBCCommand) {

      midlet.changeScreen(new AddressBookScreen(midlet));
    } else if (command == exitCommand) {
      midlet.quit();
    } else {
      // unknown command
      throw new RuntimeException("Internal error #18");
    }
  }

  public int getTypeOfOperation() {
	return typeOfOperation;
  }

  public void setTypeOfOperation(int typeOfOperation) {
	this.typeOfOperation = typeOfOperation;
  }
}

ProgressScreen

package bcexchanger.ui;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Item;
import bcexchanger.*;

/**
 * 
 * This class implements the screen which indicates that some
 * operation is in progress. For example when inquiry, service
 * discovery, business card sending and receiving is running this
 * screen is displayed. Using this screen user can cancel the processs
 * 
 * The class is extending an example.BCExchanger.ui.Screen class
 * 
 * @version 1.0 29.09.2005
 * @see example.BCExchanger.ui.Screen Design patterns: State
 */
public class ProgressScreen extends Screen {

  private Command cancelCommand;

  private Gauge gauge;
  private Form progressBar;
  
  /**
   * Constructor
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   * @param message -
   *          text message to be displayed
   */
  public ProgressScreen(BCExchangerMIDlet _midlet, String message) {
    super(_midlet);

    progressBar = new Form("Please wait");

    gauge = new Gauge(message, false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING);
    gauge.setLayout(Item.LAYOUT_CENTER);
    gauge.setLayout(Item.LAYOUT_VCENTER);
    progressBar.append(gauge);
    
    cancelCommand = new Command("Cancel", Command.CANCEL, 1);
    progressBar.addCommand(cancelCommand);

    progressBar.setCommandListener(this);
   
    displayable = progressBar;
    
  }

  public void commandAction(Command command, Displayable _displayable) {

    if (_displayable != progressBar) {
      // wrong displayable
      throw new RuntimeException("Internal error #21");
    }

    if (command == cancelCommand) {
      midlet.getExchanger().cancelSending();
    } else {
      // unknown command
      throw new RuntimeException("Internal error #22");
    }
  }

}

Screen

package bcexchanger.ui;

import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;

import bcexchanger.*;

/**
 * 
 * This class is an abstract base class for all the UI screen in this
 * application. This class implements CommandListner interface.
 * 
 * @version 1.0 29.09.2005
 * @see example.BCExchanger.ui.Screen Design patterns: State
 * 
 */
public abstract class Screen implements CommandListener {

  protected BCExchangerMIDlet midlet;
  protected Displayable displayable;

  /**
   * Constructor
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   */
  public Screen(BCExchangerMIDlet _midlet) {
    midlet = _midlet;
  }

  /**
   * Makes current screen active
   * 
   * Each class inherited from Screen has a displayable associated.
   * When this method is called the displayble is made visiable and
   * active
   * 
   */
  public void makeActive() {
    try {
      Display d = Display.getDisplay(midlet);

      if (d.getCurrent() != displayable) { // this prevents from
                                            // bringing application to
                                            // foreground in case is
                                            // application is in
                                            // background and the
                                            // current displayable is
                                            // the same as the one
                                            // made active
        d.setCurrent(displayable);
      }

    } catch (NullPointerException e) {
      throw new RuntimeException(
          "Internal error #2: Screen.midlet == null");
    }
  }

}

ServiceListScreen

package bcexchanger.ui;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.List;

import bcexchanger.*;

/**
 * 
 * This class implements the service list screen. This screen is used
 * to when several services are available in vicinity and allows user
 * to choose the desired one. Using this screen user can select the
 * service or cancel the processs
 * 
 * The class is extending an example.BCExchanger.ui.Screen class
 * 
 * @version 1.0 29.09.2005
 * @see example.BCExchanger.ui.Screen Design patterns: State
 * 
 */
public class ServiceListScreen extends Screen {

  private List list;
  private Command okCommand;
  private Command cancelCommand;

  /**
   * Constructor
   * 
   * @param _midlet -
   *          the parent class which keeps the current UI state
   * @param friendlyNames -
   *          vector of the friednly names of the devices which
   *          contain the services
   */
  public ServiceListScreen(BCExchangerMIDlet _midlet,
      Vector friendlyNames) {
    super(_midlet);

    list = new List("Choose service", Choice.EXCLUSIVE);

    if (friendlyNames.isEmpty()) {
      // list should not be empty
      throw new RuntimeException("Internal error #23");
    }

    Enumeration e = friendlyNames.elements();

    while (e.hasMoreElements()) {
      list.append((String) e.nextElement(), null);
    }

    okCommand = new Command("Ok", Command.OK, 1);
    cancelCommand = new Command("Cancel", Command.CANCEL, 1);

    list.addCommand(okCommand);
    list.addCommand(cancelCommand);

    list.setCommandListener(this);

    displayable = list;
  }

  public void commandAction(Command command, Displayable _displayable) {
    if (_displayable != list) {
      // wrong displayable
      throw new RuntimeException("Internal error #24");
    }

    if (command == okCommand) {
      midlet.serviceSelected(list.getSelectedIndex());
      midlet.unblock();
    } else if (command == cancelCommand) {
      midlet.getExchanger().cancelSending();
      midlet.unblock();
    } else {
      // unknown command
      throw new RuntimeException("Internal error #25");
    }

  }
}