The ExchangeListener
is a callback interface. By
means of the interface methods, the OBEX communication module signals to a
listener about various networking events. This interface is implemented by
the BCExchangerMIDlet
To create the class:
Create the ExchangeListener
class
file.
Assign the
class to the Comm
package and import the required classes.
package bcexchanger.comm; import java.util.Vector;
Implement
the ExchangeListener
class functionality.
/** * * 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 * <p> * 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 * <p> * 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 * <p> * 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 * <p> * 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(); }
This interface contains methods representing the functionality of the
OBEX communication module to other classes of the application. The OBEX communication
module implemented by the classes from the bcexchanger.comm
package
realizes the exchange of the business card over OBEX API (JSR-82). The control
of the module is abstracted in this interface.
The BCExchangerMIDlet
calls the methods of this
interface implemented by the ExchangerCommImpl
class
to control OBEX communication.
To create the class:
Create the ExchangerComm
class
file.
Assign the
class to the Comm
package.
package bcexchanger.comm;
Implement the interface.
/** * * 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;
Initiate the process of sending a business card. Set methods that start and stop listening for incoming connections.
/** * Intitiate whole procedure of sending business card to a remote * device * <p> * 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 * <p> * 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 * <p> * 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 * <p> * The method stops listening for incomming connection if * startWaiting() was called before. Otherwise method does nothing. * */ public void cancelWaiting(); }
This class is the central class in the OBEX communication module (package bcexchanger.comm
).
The class implements the interface ExchangerComm
and
realizes the methods for controlling the process of sending and receiving
business cards. It is also a parent for states of the communication state
machine. It keeps the current state of the state machine and implements the
interface ExchangerStateParent
. Using this interface,
state classes can access the required functionality of the parent.
This class waits for an incoming Bluetooth connection in a separate
thread and therefore implements the Runnable
interface.
The class also works as an OBEX server. In order to serve OBEX requests, it
extends the ServerRequestHandler
class
and overrides some of its methods.
To create the class:
Create the ExchangerCommImpl
class
file.
Assign the
class to the Comm
package and import the required classes.
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;
Set the class
to extend the ServerRequestHandler
class
of the javax.bluetooth
package in order to serve OBEX requests and override some
of its methods.
In order to create a server connection, set instance
variables for this purpose. This is done by identifying the btgoep
protocol,
the server URL, and the service UUID. This will be
used later in this class when the Connector.open()
method
is called.
/** * * 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;
Set a listener for the module event communication.
/** * Constructor * <p> * 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 } }
Use the run
method to initialize the stack. Make the device discoverable by using the setDiscoverable()
method of the LocalDevice
class
in the javax.bluetooth
package.
Call the Connector.open()
method
to return a SessionNotifier
object. Use the method acceptAndOpen()
of
the SessionNotifier
interface to start waiting for incoming transport layer connections.
After the transport layer connection is established, call the acceptAndOpen()
method
to return a Connection
object, which represents the connection
to a single client.
The server communicates with the remote peer
by two means: the Connection
object and the ServerRequestHandler
callback methods that are called on incoming OBEX requests.
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; } } }
Implement the OBEX server related methods.
The ServerRequestHandler
class
methods onGet()
and onPut()
are
called when a GET or PUT method is received. These methods receive an Operation
object
as a parameter. The application uses the Operation
object
to read and write data. After the request is handled, the method must return
a response code. To see all the available response codes, see the ResponseCodes
class.
/* * 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; } }
This is the abstract base class for all the states of the communication state machine. Each state implements only those methods which can be executed in that particular case.
Classes IdleState
, InquiryState
, ServiceDiscoveryState
, ReceiveState
, and SendState
inherit from this class.
To create the class:
Create the ExchangerState
class
file.
Assign the
class to the Comm
package and import the required class.
package bcexchanger.comm; import javax.obex.Operation;
Implement the class. Implement the way the server handles the OBEX GET and PUT methods.
/** * * 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 * <p> * 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 * <p> * 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 * <p> * 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 * <p> * 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; } }
This interface contains the function used by the state to address the
functionality of the parent class ExchangerCommImpl
.
It contains the methods which the states of the communication state machine
use to get certain functionality from the parent.
This interface is implemented by the ExchangerCommImpl
class.
To create the class:
Create the ExchangerStateParent
class
file.
Assign the
class to the Comm
package.
package bcexchanger.comm;
Implement the methods to be used by the communication state machine.
/** * * 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 * <p> * Sets the current state in the parent * * @param state - * state of communication machine */ public void setState(ExchangerState state); /** * Listener getter * <p> * Returns the communication event listener which is stored in the * parent * * @return communication event listener interface */ public ExchangeListener getListener(); /** * Communication state getter * <p> * 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(); }
This class implements the idle state of the communication state machine.
In idle state, the machine waits for external events, such as incoming Bluetooth
connections or a user command to start business card exchange. The IdleState
class
extends the ExchangerState
abstract class.
To create the class:
Create the IdleState
class
file.
Assign the
class to the Comm
package and import the required classes.
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;
Implement the idle state. Set up a listener for external events.
The ServerRequestHandler
class methods onGet()
and onPut()
are called when a GET or PUT method is received. These methods
receive an Operation
object as a parameter. The application uses the Operation
object
to read and write data. After the request is handled, the method must return
a response code. All response codes are defined in the ResponseCodes
class.
/** * * 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 } }
This class represents the state of running inquiry. In this state an
active device discovery is performed. No OBEX server commands are handled.
This class extends the ExchangerState
abstract class.
This class also implements the DiscoveryListener
interface
to receive Bluetooth inquiry callbacks.
To create the class:
Create the InquiryState
class
file.
Assign the
class to the Comm
package and import the required classes.
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;
Set the class
to extend ExchangerState
and to implement DiscoveryListener
and
implement the constructor.
Use the method startInquiry()
to start an inquiry for devices. Devices that respond to the inquiry are
returned to the application via the method deviceDiscovered()
of the interface DiscoveryListener
.
Call the cancelInquiry()
method to stop the inquiry.
/** * * 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); }
The method deviceDiscovered()
is called when a device
is found during an inquiry.
The method inquiryCompleted()
is
called when the inquiry is completed. After that, the inquiry completion codes
are converted into application completion codes. For more information on inquiry
completion, see the interface DiscoveryListener
.
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"); }
The ServerRequestHandler
class methods onGet()
and onPut()
are called when a GET or PUT method is received. These methods
receive an Operation
object as a parameter. The application uses the Operation
object
to read and write data. After the request is handled, the method must return
a response code. All response codes are defined in the ResponseCodes
class.
/* * 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; } }
This class represents the state of the running service discovery. In this state an active service discovery is performed and no OBEX server commands are handled.
This class extends the ExchangerState
abstract
class. It also implements the DiscoveryListener
interface
to receive Bluetooth service discovery callbacks. Service discovery procedures
are run from a separate thread and therefore the class implements the Runnable
interface.
To create the class:
Create the InquiryState
class
file.
Assign the
class to the Comm
package and import the required classes.
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;
Set the class
to extend ExchangerState
and to implement DiscoveryListener
and Runnable
. Set up instance variables.
Initiate a separate thread for starting service discovery.
/** * * 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(); }
Set the functionality for canceling the service discovery process and handling inquiry callbacks at this state.
The method servicesDiscovered()
is called when service(s) are found during a service search.
The method serviceSearchCompleted()
is called when
a service search is completed or was terminated because of an error.
/* * 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(); }
Use the Run()
method
to implement the logic of the service discovery process. Set the search for
all available services unless the search is canceled. Use the method searchServices()
to look for services
on remote devices that have all the UUIDs specified in uuidSet. The method
returns the transaction ID of the service search. For more information on
UUIDs, see the class UUID.
If one service is found, connect to that service. However, if more than one service is found, display a list of those services and let user choose the service to connect to. Set listeners for handling user actions.
/* * (non-Javadoc) * * This methods implements logic of the service discovery process * * @see java.lang.Runnable#run() */ public synchronized void run() { Enumeration e = devices.elements(); // Business card exchanger service UUID UUID[] uuids = new UUID[1]; uuids[0] = new UUID(parent.getUUID(), false); // proceeded with all devices if not canceled while (e.hasMoreElements() && !canceled) { RemoteDevice device = (RemoteDevice) e.nextElement(); try { serviceDiscoveryID = agent.searchServices(null, uuids, device, this); } catch (Exception e0) { // signal error to the MIDlet parent.getListener().onServiceDiscoveryComplete( ExchangerComm.ERROR); parent.setState(new IdleState(parent)); return; } try { wait(); // wait until service search is done on this device } catch (Exception e1) { // Ignore } } if (canceled) { // handle user's 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) { } } } }
Implement
the method, used in the previous step, for creating a list of devices that
contains the searched services. In case of an exception in getting the name
of a device, use the method getBluetoothAddress()
to
retrieve the Bluetooth address of the device.
/* * This method creates a list of the friendly names of devices that contain * the servicies * * @param _services - vector of ServiceRecord objects representing found * services @return - vectors of strings containing friendly names of * devices */ private Vector createFriendlyNamesList(Vector _services) { Vector friendlyNames = new Vector(); Enumeration e = _services.elements(); while (e.hasMoreElements()) { ServiceRecord serviceRecord = (ServiceRecord) e.nextElement(); RemoteDevice device = serviceRecord.getHostDevice(); try { friendlyNames.addElement(device.getFriendlyName(false)); } catch (IOException e1) { // If there is an excption getting the friendly name // use the address friendlyNames.addElement(device.getBluetoothAddress()); } } return friendlyNames; } /* * 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; } }
This class represents the state when a business card is received from the remote device. In this state the client OBEX GET operation is performed. No OBEX server commands are handled.
To create the class:
Create the InquiryState
class
file.
Assign the
class to the Comm
package and import the required classes.
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;
Set the class
to extend the ExchangerState
class. Set the instance
variables for ClientSession
and Operation
.
/** * * 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 * <p> * 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 } }
Implement the logic for receiving a business card.
The getResponseCode()
method
of the HeaderSet
interface returns the response code received from the server. To see all
the available response codes, see the ResponseCodes
class.
/* * This method implements the logic for * receiving 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; } }
This class represents the state when a business card is sent to the
remote phone. In this state the client OBEX PUT operation is performed. No
OBEX server commands are handled. This class extends the ExchangerState
abstract
class.
To create the class:
Create the InquiryState
class
file.
Assign the
class to the Comm
package and import the required classes.
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;
Set the class
to extend the ExchangerState
class. Set the instance
variables.
/** * 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 } }
Implement the logic for sending a business card.
The getResponseCode()
method
of the HeaderSet
interface returns the response code received from the server. To see all
the available response codes, see the ResponseCodes
class.
/* * 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; } }