Add javax.obex library, contributed by Motorola.

Based of a JSR-82 reference implementation.

This check-in is 'as-is' from the source provided June 25.

This code does not conform to Android style and quality guidelines. I will be
working with Motorola to improve it. If it does not see substantial improvement
in the Eclair timeframe it will be deleted.
diff --git a/obex/javax/obex/ApplicationParameter.java b/obex/javax/obex/ApplicationParameter.java
new file mode 100644
index 0000000..8ed4ca6
--- /dev/null
+++ b/obex/javax/obex/ApplicationParameter.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+public class ApplicationParameter {
+    private int max_length_ini = 1000;
+
+    private byte[] b_array;
+
+    private int length;
+
+    public static class TRIPLET_TAGID {
+        public static final byte ORDER_TAGID = 0x01;
+
+        public static final byte SEARCH_VALUE_TAGID = 0x02;
+
+        public static final byte SEARCH_ATTRIBUTE_TAGID = 0x03;
+
+        public static final byte MAXLISTCOUNT_TAGID = 0x04;//if equals to "0", PSE only reply number of contacts
+
+        public static final byte LISTSTARTOFFSET_TAGID = 0x05;
+
+        public static final byte FILTER_TAGID = 0x06;
+
+        public static final byte FORMAT_TAGID = 0x07;
+
+        public static final byte PHONEBOOKSIZE_TAGID = 0x08;//only used if max list count = 0
+
+        public static final byte NEWMISSEDCALLS_TAGID = 0x09;//only used in "mch" in response
+    }
+
+    public static class TRIPLET_VALUE {
+        public static class ORDER {
+            public static final byte ORDER_BY_INDEX = 0x00;
+
+            public static final byte ORDER_BY_ALPHANUMERIC = 0x01;
+
+            public static final byte ORDER_BY_PHONETIC = 0x02;
+        }
+
+        public static class SEARCHATTRIBUTE {
+            public static final byte SEARCH_BY_NAME = 0x00;
+
+            public static final byte SEARCH_BY_NUMBER = 0x01;
+
+            public static final byte SEARCH_BY_SOUND = 0x02;
+        }
+
+        public static class FORMAT {
+            public static final byte VCARD_VERSION_21 = 0x00;
+
+            public static final byte VCARD_VERSION_30 = 0x01;
+        }
+    }
+
+    public static class TRIPLET_LENGTH {
+        public static final byte ORDER_LENGTH = 1;
+
+        //public final byte SEARCH_VALUE_LENGTH = 0x02;
+        public static final byte SEARCH_ATTRIBUTE_LENGTH = 1;
+
+        public static final byte MAXLISTCOUNT_LENGTH = 2;
+
+        public static final byte LISTSTARTOFFSET_LENGTH = 2;
+
+        public static final byte FILTER_LENGTH = 8;
+
+        public static final byte FORMAT_LENGTH = 1;
+
+        public static final byte PHONEBOOKSIZE_LENGTH = 2;
+
+        public static final byte NEWMISSEDCALLS_LENGTH = 1;
+    }
+
+    /*
+    public class TRIPLET_STRUCTURE{
+        TRIPLET_TAGID id;
+        TRIPLET_LENGTH length;
+        byte[] value;
+    }
+    */
+    public ApplicationParameter() {
+        b_array = new byte[max_length_ini];
+        length = 0;
+    }
+
+    public void addAPPHeader(byte tag, byte len, byte[] value) {
+        if ((length + len + 2) > max_length_ini) {
+            byte[] array_tmp = new byte[length + 4 * len];
+            System.arraycopy(b_array, 0, array_tmp, 0, length);
+            b_array = array_tmp;
+            max_length_ini = length + 4 * len;
+        }
+        b_array[length++] = tag;
+        b_array[length++] = len;
+        System.arraycopy(value, 0, b_array, length, len);
+        length += len;
+    }
+
+    public byte[] getAPPparam() {
+        byte[] para = new byte[length];
+        System.arraycopy(b_array, 0, para, 0, length);
+        return para;
+    }
+}
diff --git a/obex/javax/obex/Authenticator.java b/obex/javax/obex/Authenticator.java
new file mode 100644
index 0000000..a1729f8
--- /dev/null
+++ b/obex/javax/obex/Authenticator.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This interface provides a way to respond to authentication challenge and
+ * authentication response headers.  When a client or server receives an
+ * authentication challenge or authentication response header, the
+ * <code>onAuthenticationChallenge()</code> or
+ * <code>onAuthenticationResponse()</code> will be called, respectively, by
+ * the implementation.
+ * <P>
+ * For more information on how the authentication procedure works in OBEX,
+ * please review the IrOBEX specification at
+ * <A HREF="http://www.irda.org">http://www.irda.org</A>.
+ * <P>
+ * <STRONG>Authentication Challenges</STRONG>
+ * <P>
+ * When a client or server receives an authentication challenge header, the
+ * <code>onAuthenticationChallenge()</code> method will be invoked by the
+ * OBEX API implementation.  The application will then return the user name
+ * (if needed) and password via a <code>PasswordAuthentication</code> object.
+ * The password in this object is not sent in the authentication response.
+ * Instead, the 16-byte challenge received in the authentication challenge is
+ * combined with the password returned from the
+ * <code>onAuthenticationChallenge()</code> method and passed through the MD5
+ * hash algorithm.  The resulting value is sent in the authentication response
+ * along with the user name if it was provided.
+ * <P>
+ * <STRONG>Authentication Responses</STRONG>
+ * <P>
+ * When a client or server receives an authentication response header, the
+ * <code>onAuthenticationResponse()</code> method is invoked by the API
+ * implementation with the user name received in the authentication response
+ * header.  (The user name will be <code>null</code> if no user name was
+ * provided in the authentication response header.)  The application must
+ * determine the correct password.  This value should be returned from the
+ * <code>onAuthenticationResponse()</code> method.  If the authentication
+ * request should fail without the implementation checking the password,
+ * <code>null</code> should
+ * be returned by the application.  (This is needed for reasons like not
+ * recognizing the user name, etc.)  If the returned value is not
+ * <code>null</code>, the OBEX API implementation will combine the password
+ * returned from the <code>onAuthenticationResponse()</code> method and
+ * challenge sent via the authentication challenge, apply the MD5 hash
+ * algorithm, and compare the result to the response hash received in the
+ * authentication response header.  If the values are not equal, an
+ * <code>IOException</code> will be thrown if the client requested authentication.
+ * If the server requested authentication, the
+ * <code>onAuthenticationFailure()</code> method will be called on the
+ * <code>ServerRequestHandler</code> that failed authentication.  The
+ * connection is <B>not</B> closed if authentication failed.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public interface Authenticator {
+
+    /**
+     * Called when a client or a server receives an authentication challenge
+     * header. It should respond to the challenge with a
+     * <code>PasswordAuthentication</code> that contains the correct user name
+     * and password for the challenge.
+     *
+     * @param description the description of which user name and password
+     * should be used; if no description is provided in the authentication
+     * challenge or the description is encoded in an encoding scheme that is
+     * not supported, an empty string will be provided
+     *
+     * @param isUserIdRequired <code>true</code> if the user ID is required;
+     * <code>false</code> if the user ID is not required
+     *
+     * @param isFullAccess <code>true</code> if full access to the server
+     * will be granted; <code>false</code> if read only access will be
+     * granted
+     *
+     * @return a <code>PasswordAuthentication</code> object containing the
+     * user name and password used for authentication
+     */
+    public PasswordAuthentication onAuthenticationChallenge(String description,
+            boolean isUserIdRequired, boolean isFullAccess);
+
+    /**
+     * Called when a client or server receives an authentication response
+     * header.  This method will provide the user name and expect the correct
+     * password to be returned.
+     *
+     * @param userName the user name provided in the authentication response;
+     * may be <code>null</code>
+     *
+     * @return the correct password for the user name provided; if
+     * <code>null</code> is returned then the authentication request failed
+     */
+    public byte[] onAuthenticationResponse(byte[] userName);
+}
diff --git a/obex/javax/obex/BaseStream.java b/obex/javax/obex/BaseStream.java
new file mode 100644
index 0000000..ffd0b63
--- /dev/null
+++ b/obex/javax/obex/BaseStream.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+
+/**
+ * This interface defines the methods needed by a parent that uses the
+ * PrivateInputStream and PrivateOutputStream objects defined in this package.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public interface BaseStream {
+
+    /**
+     * Verifies that this object is still open.
+     *
+     * @exception IOException if the object is closed
+     */
+    public void ensureOpen() throws IOException;
+
+    /**
+     * Verifies that additional information may be sent.  In other words, the
+     * operation is not done.
+     *
+     * @exception IOException if the operation is completed
+     */
+    public void ensureNotDone() throws IOException;
+
+    /**
+     * Continues the operation since there is no data to read.
+     *
+     * @param sendEmpty <code>true</code> if the operation should send an
+     * empty packet or not send anything if there is no data to send
+     * @param inStream  <code>true</code> if the stream is input stream or
+     * is output stream
+     * @return <code>true</code> if the operation was completed;
+     * <code>false</code> if no operation took place
+     *
+     * @exception IOException if an IO error occurs
+     */
+    public boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
+
+    /**
+     * Called when the output or input stream is closed.
+     *
+     * @param inStream <code>true</code> if the input stream is closed;
+     * <code>false</code> if the output stream is closed
+     *
+     * @exception IOException if an IO error occurs
+     */
+    public void streamClosed(boolean inStream) throws IOException;
+}
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
new file mode 100644
index 0000000..dc4e944
--- /dev/null
+++ b/obex/javax/obex/ClientOperation.java
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class implements the <code>Operation</code> interface.  It will read
+ * and write data via puts and gets.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class ClientOperation implements Operation, BaseStream {
+
+    /**
+     * Defines the basic packet length used by OBEX.  Event OBEX packet has the
+     * same basic format:<BR>
+     * Byte 0: Request or Response Code
+     * Byte 1&2: Length of the packet.
+     */
+    private static final int BASE_PACKET_LENGTH = 3;
+
+    private ClientSession parent;
+
+    private InputStream socketInput;
+
+    private PrivateInputStream privateInput;
+
+    private PrivateOutputStream privateOutput;
+
+    private boolean isClosed;
+
+    private String exceptionMessage;
+
+    private int maxPacketSize;
+
+    private boolean isDone;
+
+    private boolean isGet;
+
+    private HeaderSet requestHeaders;
+
+    private HeaderSet replyHeaders;
+
+    private boolean isEndOfBodySent;
+
+    private boolean inputStreamOpened;
+
+    private boolean outputStreamOpened;
+
+    private boolean isValidateConnected;
+
+    /** 
+     * Creates new OperationImpl to read and write data to a server
+     *
+     * @param in the input stream to read from
+     *
+     * @param maxSize the maximum packet size
+     *
+     * @param p the parent to this object
+     *
+     * @param headers the headers to set in the initial request
+     *
+     * @param type <code>true</code> if this is a get request;
+     * <code>false</code. if this is a put request
+     *
+     * @exception IOExcpetion if the an IO error occured
+     */
+    public ClientOperation(InputStream in, int maxSize, ClientSession p, HeaderSet header,
+            boolean type) throws IOException {
+
+        parent = p;
+        isEndOfBodySent = false;
+        socketInput = in;
+        isClosed = false;
+        isDone = false;
+        maxPacketSize = maxSize;
+        isGet = type;
+
+        inputStreamOpened = false;
+        outputStreamOpened = false;
+        isValidateConnected = false;
+
+        privateInput = null;
+        privateOutput = null;
+
+        replyHeaders = new HeaderSet();
+
+        requestHeaders = new HeaderSet();
+
+        int[] headerList = header.getHeaderList();
+
+        if (headerList != null) {
+
+            for (int i = 0; i < headerList.length; i++) {
+                requestHeaders.setHeader(headerList[i], header.getHeader(headerList[i]));
+            }
+        }
+
+        if ((header).authChall != null) {
+            requestHeaders.authChall = new byte[(header).authChall.length];
+            System.arraycopy((header).authChall, 0, requestHeaders.authChall, 0,
+                    (header).authChall.length);
+        }
+
+        if ((header).authResp != null) {
+            requestHeaders.authResp = new byte[(header).authResp.length];
+            System.arraycopy((header).authResp, 0, requestHeaders.authResp, 0,
+                    (header).authResp.length);
+
+        }
+        //        requestHeaders = (HeaderSet)header;
+    }
+
+    /**
+     * Sends an ABORT message to the server.  By calling this method, the
+     * corresponding input and output streams will be closed along with this
+     * object.
+     *
+     * @exception IOException if the transaction has already ended or if an
+     * OBEX server called this method
+     */
+    public synchronized void abort() throws IOException {
+        ensureOpen();
+        // need check again .
+        //	if(isDone) {
+        //	     throw new IOException("Operation has already ended");
+        //	}
+
+        //no compatible with sun-ri
+        if ((isDone) && (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE)) {
+            throw new IOException("Operation has already ended");
+        }
+
+        exceptionMessage = "Operation aborted";
+        if ((!isDone) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
+            isDone = true;
+            /*
+             * Since we are not sending any headers or returning any headers then
+             * we just need to write and read the same bytes
+             */
+            parent.sendRequest(0xFF, null, replyHeaders, null);
+
+            if (replyHeaders.responseCode != ResponseCodes.OBEX_HTTP_OK) {
+                throw new IOException("Invalid response code from server");
+            }
+
+            exceptionMessage = null;
+        }
+
+        close();
+    }
+
+    /**
+     * Retrieves the response code retrieved from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> interface.
+     *
+     * @return the response code retrieved from the server
+     *
+     * @exception IOException if an error occurred in the transport layer during
+     * the transaction; if this method is called on a <code>HeaderSet</code>
+     * object created by calling <code>createHeaderSet</code> in a
+     * <code>ClientSession</code> object
+     */
+    public synchronized int getResponseCode() throws IOException {
+        //avoid dup validateConnection
+        if ((replyHeaders.responseCode == -1)
+                || (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
+            validateConnection();
+        }
+
+        return replyHeaders.responseCode;
+    }
+
+    /**
+     * This method will always return <code>null</code>
+     *
+     * @return <code>null</code>
+     */
+    public String getEncoding() {
+        return null;
+    }
+
+    /**
+     * Returns the type of content that the resource connected to is providing.
+     * E.g. if the connection is via HTTP, then the value of the content-type
+     * header field is returned.
+     *
+     * @return the content type of the resource that the URL references, or
+     * <code>null</code> if not known
+     */
+    public String getType() {
+        try {
+            return (String)replyHeaders.getHeader(HeaderSet.TYPE);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the length of the content which is being provided. E.g. if the
+     * connection is via HTTP, then the value of the content-length header
+     * field is returned.
+     *
+     * @return the content length of the resource that this connection's URL
+     * references, or -1 if the content length is not known
+     */
+    public long getLength() {
+        try {
+            Long temp = (Long)replyHeaders.getHeader(HeaderSet.LENGTH);
+
+            if (temp == null) {
+                return -1;
+            } else {
+                return temp.longValue();
+            }
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Open and return an input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public InputStream openInputStream() throws IOException {
+        // TODO: this mode is not set yet.
+        // if ((parent.mode & Connector.READ) == 0)
+        // throw new IOException("write-only connection");
+
+        ensureOpen();
+
+        if (inputStreamOpened)
+            throw new IOException("no more input streams available");
+        if (isGet) {
+            // send the GET request here
+            validateConnection();
+            isValidateConnected = true;
+        } else {
+            if (privateInput == null) {
+                privateInput = new PrivateInputStream(this);
+            }
+        }
+
+        inputStreamOpened = true;
+
+        return privateInput;
+    }
+
+    /**8
+     * Open and return a data input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    /**
+     * Open and return an output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public OutputStream openOutputStream() throws IOException {
+        // TODO: this mode is not set yet.
+        //    	if ((parent.mode & Connector.WRITE) == 0)
+        //    		throw new IOException("read-only connection");
+        ensureOpen();
+        ensureNotDone();
+
+        if (outputStreamOpened)
+            throw new IOException("no more output streams available");
+
+        if (privateOutput == null) {
+            // there are 3 bytes operation headers and 3 bytes body headers //
+            privateOutput = new PrivateOutputStream(this, maxPacketSize - 6);
+        }
+
+        outputStreamOpened = true;
+
+        return privateOutput;
+    }
+
+    public int getMaxPacketSize() {
+        return maxPacketSize - 6;
+    }
+
+    /**
+     * Open and return a data output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    /**
+     * Closes the connection and ends the transaction
+     *
+     * @exception IOException if the operation has already ended or is closed
+     */
+    public void close() throws IOException {
+        isClosed = true;
+        inputStreamOpened = false;
+        outputStreamOpened = false;
+        parent.setInactive();
+    }
+
+    /**
+     * Returns the headers that have been received during the operation.
+     * Modifying the object returned has no effect on the headers that are
+     * sent or retrieved.
+     *
+     * @return the headers received during this <code>Operation</code>
+     *
+     * @exception IOException if this <code>Operation</code> has been closed
+     */
+    public HeaderSet getReceivedHeaders() throws IOException {
+        ensureOpen();
+
+        return replyHeaders;
+    }
+
+    /**
+     * Specifies the headers that should be sent in the next OBEX message that
+     * is sent.
+     *
+     * @param headers the headers to send in the next message
+     *
+     * @exception IOException  if this <code>Operation</code> has been closed
+     * or the transaction has ended and no further messages will be exchanged
+     *
+     * @exception IllegalArgumentException if <code>headers</code> was not created
+     * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+     *
+     * @exception NullPointerException if <code>headers</code> is <code>null</code>
+     */
+    public void sendHeaders(HeaderSet headers) throws IOException {
+        ensureOpen();
+        if (isDone) {
+            throw new IOException("Operation has already exchanged all data");
+        }
+
+        if (headers == null) {
+            throw new NullPointerException("Headers may not be null");
+        }
+
+        int[] headerList = headers.getHeaderList();
+        if (headerList != null) {
+            for (int i = 0; i < headerList.length; i++) {
+                requestHeaders.setHeader(headerList[i], headers.getHeader(headerList[i]));
+            }
+        }
+    }
+
+    /**
+     * Reads a response from the server.  It will populate the appropriate body
+     * and headers.
+     *
+     * @return <code>true</code> if the transaction should end;
+     * <code>false</code> if the transaction should not end
+     *
+     * @exception IOException if an IO error occurred
+     */
+    private boolean readResponse() throws IOException {
+        replyHeaders.responseCode = socketInput.read();
+        int packetLength = socketInput.read();
+        packetLength = (packetLength << 8) + socketInput.read();
+
+        if (packetLength > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            if (exceptionMessage != null) {
+                abort();
+            }
+            throw new IOException("Received a packet that was too big");
+        }
+
+        if (packetLength > BASE_PACKET_LENGTH) {
+            int dataLength = packetLength - BASE_PACKET_LENGTH;
+            byte[] data = new byte[dataLength];
+            int readLength = socketInput.read(data);
+            if (readLength != dataLength) {
+                throw new IOException("Received a packet without data as decalred length");
+            }
+            byte[] body = OBEXHelper.updateHeaderSet(replyHeaders, data);
+
+            if (body != null) {
+                privateInput.writeBytes(body, 1);
+
+                /*
+                 * Determine if a body (0x48) header or an end of body (0x49)
+                 * was received.  If we received an end of body and
+                 * a response code of OBEX_HTTP_OK, then the operation should
+                 * end.
+                 */
+                if ((body[0] == 0x49) && (replyHeaders.responseCode == ResponseCodes.OBEX_HTTP_OK)) {
+                    return false;
+                }
+            }
+        }
+
+        if (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Verifies that additional information may be sent.  In other words, the
+     * operation is not done.
+     *
+     * @exception IOException if the operation is completed
+     */
+    public void ensureNotDone() throws IOException {
+        if (isDone) {
+            throw new IOException("Operation has completed");
+        }
+    }
+
+    /**
+     * Verifies that the connection is open and no exceptions should be thrown.
+     *
+     * @exception IOException if an exception needs to be thrown
+     */
+    public void ensureOpen() throws IOException {
+        parent.ensureOpen();
+
+        if (exceptionMessage != null) {
+            throw new IOException(exceptionMessage);
+        }
+        if (isClosed) {
+            throw new IOException("Operation has already ended");
+        }
+    }
+
+    /**
+     * Verifies that the connection is open and the proper data has been read.
+     *
+     * @exception IOException if an IO error occurs
+     */
+    private void validateConnection() throws IOException {
+        ensureOpen();
+
+        // to sure only one privateInput object exist.
+        if (privateInput == null) {
+            startProcessing();
+        }
+    }
+
+    /**
+     * Sends a request to the client of the specified type
+     *
+     * @param response the response code to send back to the client
+     *
+     * @return <code>true</code> if there is more data to send;
+     * <code>false</code> if there is no more data to send
+     *
+     * @exception IOException if an IO error occurs
+     */
+    protected boolean sendRequest(int type) throws IOException {
+        boolean returnValue = false;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int bodyLength = -1;
+        byte[] headerArray = OBEXHelper.createHeader(requestHeaders, true);
+        if (privateOutput != null) {
+            bodyLength = privateOutput.size();
+        }
+
+        /*
+         * Determine if there is space to add a body request.  At present
+         * this method checks to see if there is room for at least a 17
+         * byte body header.  This number needs to be at least 6 so that
+         * there is room for the header ID and length and the reply ID and
+         * length, but it is a waste of resources if we can't send much of
+         * the body.
+         */
+        if ((BASE_PACKET_LENGTH + headerArray.length) > maxPacketSize) {
+            int end = 0;
+            int start = 0;
+            // split & send the headerArray in multiple packets.
+
+            while (end != headerArray.length) {
+                //split the headerArray
+                end = OBEXHelper.findHeaderEnd(headerArray, start, maxPacketSize
+                        - BASE_PACKET_LENGTH);
+                // can not split 
+                if (end == -1) {
+                    isDone = true;
+                    abort();
+                    // isDone = true;
+                    exceptionMessage = "Header larger then can be sent in a packet";
+                    isClosed = true;
+
+                    if (privateInput != null) {
+                        privateInput.close();
+                    }
+
+                    if (privateOutput != null) {
+                        privateOutput.close();
+                    }
+                    throw new IOException("OBEX Packet exceeds max packet size");
+                }
+
+                byte[] sendHeader = new byte[end - start];
+                System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+                if (!parent.sendRequest(type, sendHeader, replyHeaders, privateInput)) {
+                    return false;
+                }
+
+                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    return false;
+                }
+
+                start = end;
+            }
+
+            if (bodyLength > 0) {
+                return true;
+            } else {
+                return false;
+            }
+        } else {
+            out.write(headerArray);
+        }
+
+        if (bodyLength > 0) {
+            /*
+             * Determine if I can send the whole body or just part of
+             * the body.  Remember that there is the 3 bytes for the
+             * response message and 3 bytes for the header ID and length
+             */
+            if (bodyLength > (maxPacketSize - headerArray.length - 6)) {
+                returnValue = true;
+
+                bodyLength = maxPacketSize - headerArray.length - 6;
+            }
+
+            byte[] body = privateOutput.readBytes(bodyLength);
+
+            /*
+             * Since this is a put request if the final bit is set or
+             * the output stream is closed we need to send the 0x49
+             * (End of Body) otherwise, we need to send 0x48 (Body)
+             */
+            if ((privateOutput.isClosed()) && (!returnValue) && (!isEndOfBodySent)
+                    && ((type & 0x80) != 0)) {
+                out.write(0x49);
+                isEndOfBodySent = true;
+            } else {
+                out.write(0x48);
+            }
+
+            bodyLength += 3;
+            out.write((byte)(bodyLength >> 8));
+            out.write((byte)bodyLength);
+
+            if (body != null) {
+                out.write(body);
+            }
+        }
+
+        if (outputStreamOpened && bodyLength <= 0 && !isEndOfBodySent) {
+            // only 0x82 or 0x83 can send 0x49
+            if ((type & 0x80) == 0) {
+                out.write(0x48);
+            } else {
+                out.write(0x49);
+                isEndOfBodySent = true;
+
+            }
+
+            bodyLength = 3;
+            out.write((byte)(bodyLength >> 8));
+            out.write((byte)bodyLength);
+        }
+
+        if (out.size() == 0) {
+            if (!parent.sendRequest(type, null, replyHeaders, privateInput)) {
+                return false;
+            }
+            return returnValue;
+        }
+        if ((out.size() > 0)
+                && (!parent.sendRequest(type, out.toByteArray(), replyHeaders, privateInput))) {
+            return false;
+        }
+
+        // send all of the output data in 0x48, 
+        // send 0x49 with empty body
+        if ((privateOutput != null) && (privateOutput.size() > 0))
+            returnValue = true;
+
+        return returnValue;
+    }
+
+    /**
+     * This method starts the processing thread results.  It will send the
+     * initial request.  If the response takes more then one packet, a thread
+     * will be started to handle additional requests
+     *
+     * @exception IOException if an IO error occurs
+     */
+    private synchronized void startProcessing() throws IOException {
+
+        if (privateInput == null) {
+            privateInput = new PrivateInputStream(this);
+        }
+        boolean more = true;
+
+        if (isGet) {
+            if (!isDone) {
+                replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
+                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x03);
+                }
+
+                if (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                }
+                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    isDone = true;
+                }
+            }
+        } else {
+
+            if (!isDone) {
+                replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
+                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x02);
+
+                }
+            }
+
+            if (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
+                parent.sendRequest(0x82, null, replyHeaders, privateInput);
+            }
+
+            if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
+                isDone = true;
+            }
+        }
+    }
+
+    /**
+     * Continues the operation since there is no data to read.
+     *
+     * @param sendEmpty <code>true</code> if the operation should send an
+     * empty packet or not send anything if there is no data to send
+     * @param inStream  <code>true</code> if the stream is input stream or
+     * is output stream
+     * @exception IOException if an IO error occurs
+     */
+    public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+            throws IOException {
+
+        if (isGet) {
+            if ((inStream) && (!isDone)) {
+                // to deal with inputstream in get operation
+                parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                /*
+                  * Determine if that was not the last packet in the operation
+                  */
+                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    isDone = true;
+                }
+
+                return true;
+
+            } else if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in get operation
+
+                if (privateInput == null) {
+                    privateInput = new PrivateInputStream(this);
+                }
+                sendRequest(0x03);
+                return true;
+
+            } else if (isDone) {
+                return false;
+            }
+
+        } else {
+            if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in put operation
+                if (replyHeaders.responseCode == -1) {
+                    replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
+                }
+                sendRequest(0x02);
+                return true;
+            } else if ((inStream) && (!isDone)) {
+                // How to deal with inputstream  in put operation ?
+                return false;
+
+            } else if (isDone) {
+                return false;
+            }
+
+        }
+        return false;
+    }
+
+    /**
+     * Called when the output or input stream is closed.
+     *
+     * @param inStream <code>true</code> if the input stream is closed;
+     * <code>false</code> if the output stream is closed
+     *
+     * @exception IOException if an IO error occurs
+     */
+    public void streamClosed(boolean inStream) throws IOException {
+        if (!isGet) {
+            if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in put operation
+
+                boolean more = true;
+
+                if ((privateOutput != null) && (privateOutput.size() <= 0)) {
+                    byte[] headerArray = OBEXHelper.createHeader(requestHeaders, false);
+                    if (headerArray.length <= 0)
+                        more = false;
+                }
+                // If have not sent any data so send  all now
+                if (replyHeaders.responseCode == -1) {
+                    replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
+                }
+
+                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x02);
+                }
+
+                /*
+                 * According to the IrOBEX specification, after the final put, you
+                 * only have a single reply to send.  so we don't need the while
+                 * loop.
+                 */
+                while (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
+
+                    sendRequest(0x82);
+                }
+                isDone = true;
+            } else if ((inStream) && (isDone)) {
+                // how to deal with input stream in put stream ?
+                isDone = true;
+            }
+        } else {
+            isValidateConnected = false;
+            if ((inStream) && (!isDone)) {
+
+                // to deal with inputstream in get operation
+                // Have not sent any data so send it all now
+
+                if (replyHeaders.responseCode == -1) {
+                    replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
+                }
+
+                while (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    if (!sendRequest(0x83)) {
+                        break;
+                    }
+                }
+                while (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                }
+                isDone = true;
+            } else if ((!inStream) && (!isDone)) {
+                // to deal with outputstream in get operation
+                // part of the data may have been sent in continueOperation.
+
+                boolean more = true;
+
+                if ((privateOutput != null) && (privateOutput.size() <= 0)) {
+                    byte[] headerArray = OBEXHelper.createHeader(requestHeaders, false);
+                    if (headerArray.length <= 0)
+                        more = false;
+                }
+
+                if (privateInput == null) {
+                    privateInput = new PrivateInputStream(this);
+                }
+                if ((privateOutput != null) && (privateOutput.size() <= 0))
+                    more = false;
+
+                replyHeaders.responseCode = OBEXConstants.OBEX_HTTP_CONTINUE;
+                while ((more) && (replyHeaders.responseCode == OBEXConstants.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(0x03);
+                }
+                sendRequest(0x83);
+                //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
+                if (replyHeaders.responseCode != OBEXConstants.OBEX_HTTP_CONTINUE) {
+                    isDone = true;
+                }
+
+            }
+        }
+    }
+}
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
new file mode 100644
index 0000000..c624c30
--- /dev/null
+++ b/obex/javax/obex/ClientSession.java
@@ -0,0 +1,783 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class implements the <code>Operation</code> interface.  It will read
+ * and write data via puts and gets.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class ClientSession implements ObexSession {
+    //protected StreamConnection client;
+    protected Authenticator authenticator;
+
+    protected boolean connectionOpen = false;
+
+    protected boolean isConnected; // Determines if an OBEX layer connection
+
+    // has been established
+    private byte[] connectionID = null;
+
+    byte[] challengeDigest = null;
+
+    protected InputStream input = null;
+
+    protected OutputStream output = null;
+
+    protected ObexTransport trans = null;
+
+    public int mode;
+
+    /*
+    * The max Packet size must be at least 256 according to the OBEX
+    * specification.
+    */
+    private int maxPacketSize = 256;
+
+    protected boolean isActive;
+
+    /* public ClientSession() {
+              connectionOpen = false;
+    }*/
+
+    public ClientSession(ObexTransport trans) {
+        try {
+            this.trans = trans;
+            input = trans.openInputStream();
+            output = trans.openOutputStream();
+            connectionOpen = true;
+        } catch (IOException ioe) {
+        }
+
+    }
+
+    /* comment out as not used internally
+    public boolean isClosed() {
+      //  if (client instanceof BTConnection)
+      //      return ((BTConnection)client).isClosed();
+      //  else
+           return false;
+    }    
+    
+    public int getConnectionHandle() {
+       //if (client instanceof BTConnection)
+       //    return ((BTConnection)client).getConnectionHandle();
+       //else
+           return -1;
+    }
+
+    public RemoteDevice getRemoteDevice() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).getRemoteDevice();
+        else
+            return null;
+    }
+    */
+
+    public HeaderSet connect(HeaderSet header) throws java.io.IOException {
+        ensureOpen();
+        if (isConnected) {
+            throw new IOException("Already connected to server");
+        }
+        synchronized (this) {
+            if (isActive) {
+                throw new IOException("OBEX request is already being performed");
+            }
+            isActive = true;
+        }
+        int totalLength = 4;
+        byte[] head = null;
+
+        // Determine the header byte array
+        if (header != null) {
+            if ((header).nonce != null) {
+                challengeDigest = new byte[16];
+                System.arraycopy((header).nonce, 0, challengeDigest, 0, 16);
+            }
+            head = OBEXHelper.createHeader(header, false);
+            totalLength += head.length;
+        }
+        /*
+        * Write the OBEX CONNECT packet to the server.
+        * Byte 0: 0x80
+        * Byte 1&2: Connect Packet Length
+        * Byte 3: OBEX Version Number (Presently, 0x10)
+        * Byte 4: Flags (For TCP 0x00)
+        * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
+        * Byte 7 to n: headers
+        */
+        byte[] requestPacket = new byte[totalLength];
+        // We just need to start at  byte 3 since the sendRequest() method will
+        // handle the length and 0x80.
+        requestPacket[0] = (byte)0x10;
+        requestPacket[1] = (byte)0x00;
+        requestPacket[2] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT >> 8);
+        requestPacket[3] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT & 0xFF);
+        if (head != null) {
+            System.arraycopy(head, 0, requestPacket, 4, head.length);
+        }
+
+        // check with local max packet size
+        if ((requestPacket.length + 3) > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            throw new IOException("Packet size exceeds max packet size");
+        }
+
+        HeaderSet returnHeaderSet = new HeaderSet();
+        sendRequest(0x80, requestPacket, returnHeaderSet, null);
+
+        /*
+        * Read the response from the OBEX server.
+        * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
+        * Byte 1&2: Packet Length
+        * Byte 3: OBEX Version Number
+        * Byte 4: Flags3
+        * Byte 5&6: Max OBEX packet Length
+        * Byte 7 to n: Optional HeaderSet
+        */
+        if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
+            isConnected = true;
+        }
+        synchronized (this) {
+            isActive = false;
+        }
+
+        return returnHeaderSet;
+    }
+
+    public Operation get(HeaderSet header) throws java.io.IOException {
+
+        if (!isConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        synchronized (this) {
+            if (isActive) {
+                throw new IOException("OBEX request is already being performed");
+            }
+            isActive = true;
+        }
+        ensureOpen();
+
+        if (header == null) {
+            header = new HeaderSet();
+        } else {
+            if ((header).nonce != null) {
+                challengeDigest = new byte[16];
+                System.arraycopy((header).nonce, 0, challengeDigest, 0, 16);
+            }
+        }
+        // Add the connection ID if one exists
+        if (connectionID != null) {
+            (header).connectionID = new byte[4];
+            System.arraycopy(connectionID, 0, (header).connectionID, 0, 4);
+        }
+
+        //      this.mode = javax.microedition.io.Connector.READ ;
+        return new ClientOperation(input, maxPacketSize, this, header, true);
+    }
+
+    /**
+    *  0xCB Connection Id an identifier used for OBEX connection multiplexing
+    */
+    public void setConnectionID(long id) {
+        if ((id < 0) || (id > 0xFFFFFFFFL)) {
+            throw new IllegalArgumentException("Connection ID is not in a valid range");
+        }
+        connectionID = OBEXHelper.convertToByteArray(id);
+    }
+
+    public HeaderSet createHeaderSet() {
+        return new HeaderSet();
+    }
+
+    public HeaderSet delete(HeaderSet headers) throws java.io.IOException {
+
+        Operation op = put(headers);
+        op.getResponseCode();
+        HeaderSet returnValue = op.getReceivedHeaders();
+        op.close();
+
+        return returnValue;
+    }
+
+    public HeaderSet disconnect(HeaderSet header) throws java.io.IOException {
+        if (!isConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        synchronized (this) {
+            if (isActive) {
+                throw new IOException("OBEX request is already being performed");
+            }
+            isActive = true;
+        }
+        ensureOpen();
+        // Determine the header byte array
+        byte[] head = null;
+        if (header != null) {
+            if ((header).nonce != null) {
+                challengeDigest = new byte[16];
+                System.arraycopy((header).nonce, 0, challengeDigest, 0, 16);
+            }
+            // Add the connection ID if one exists
+            if (connectionID != null) {
+                (header).connectionID = new byte[4];
+                System.arraycopy(connectionID, 0, (header).connectionID, 0, 4);
+            }
+            head = OBEXHelper.createHeader(header, false);
+
+            if ((head.length + 3) > maxPacketSize) {
+                throw new IOException("Packet size exceeds max packet size");
+            }
+        } else {
+            // Add the connection ID if one exists
+            if (connectionID != null) {
+                head = new byte[5];
+                head[0] = (byte)0xCB;
+                System.arraycopy(connectionID, 0, head, 1, 4);
+            }
+        }
+
+        HeaderSet returnHeaderSet = new HeaderSet();
+        sendRequest(0x81, head, returnHeaderSet, null);
+
+        /*
+        * An OBEX DISCONNECT reply from the server:
+        * Byte 1: Response code
+        * Bytes 2 & 3: packet size
+        * Bytes 4 & up: headers
+        */
+
+        /* response code , and header are ignored
+         * */
+
+        synchronized (this) {
+            isConnected = false;
+            isActive = false;
+        }
+
+        return returnHeaderSet;
+    }
+
+    public long getConnectionID() {
+
+        if (connectionID == null) {
+            return -1;
+        }
+        return OBEXHelper.convertToLong(connectionID);
+    }
+
+    public Operation put(HeaderSet header) throws java.io.IOException {
+        if (!isConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        synchronized (this) {
+            if (isActive) {
+                throw new IOException("OBEX request is already being performed");
+            }
+            isActive = true;
+        }
+
+        ensureOpen();
+
+        if (header == null) {
+            header = new HeaderSet();
+        } else {
+            // when auth is initated by client ,save the digest 
+            if ((header).nonce != null) {
+                challengeDigest = new byte[16];
+                System.arraycopy((header).nonce, 0, challengeDigest, 0, 16);
+            }
+        }
+
+        // Add the connection ID if one exists
+        if (connectionID != null) {
+
+            (header).connectionID = new byte[4];
+            System.arraycopy(connectionID, 0, (header).connectionID, 0, 4);
+        }
+
+        //       this.mode = javax.microedition.io.Connector.WRITE ;
+        return new ClientOperation(input, maxPacketSize, this, header, false);
+    }
+
+    public void setAuthenticator(Authenticator auth) {
+        if (auth == null) {
+            throw new NullPointerException("Authenticator may not be null");
+        }
+        authenticator = auth;
+    }
+
+    public HeaderSet setPath(HeaderSet header, boolean backup, boolean create)
+            throws java.io.IOException {
+        if (!isConnected) {
+            throw new IOException("Not connected to the server");
+        }
+        synchronized (this) {
+            if (isActive) {
+                throw new IOException("OBEX request is already being performed");
+            }
+            isActive = true;
+        }
+
+        ensureOpen();
+
+        int totalLength = 2;
+        byte[] head = null;
+
+        if (header == null) {
+            header = new HeaderSet();
+        } else {
+            if ((header).nonce != null) {
+                challengeDigest = new byte[16];
+                System.arraycopy((header).nonce, 0, challengeDigest, 0, 16);
+            }
+        }
+
+        // when auth is initiated by client ,save the digest
+        if ((header).nonce != null) {
+            challengeDigest = new byte[16];
+            System.arraycopy((header).nonce, 0, challengeDigest, 0, 16);
+        }
+
+        // Add the connection ID if one exists
+        if (connectionID != null) {
+            (header).connectionID = new byte[4];
+            System.arraycopy(connectionID, 0, (header).connectionID, 0, 4);
+        }
+
+        head = OBEXHelper.createHeader(header, false);
+        totalLength += head.length;
+
+        if (totalLength > maxPacketSize) {
+            throw new IOException("Packet size exceeds max packet size");
+        }
+
+        int flags = 0;
+        /*
+         * The backup flag bit is bit 0 so if we add 1, this will set that bit
+         */
+        if (backup) {
+            flags++;
+        }
+        /*
+         * The create bit is bit 1 so if we or with 2 the bit will be set.
+         */
+        if (!create) {
+            flags |= 2;
+        }
+
+        /*
+         * An OBEX SETPATH packet to the server:
+         * Byte 1: 0x85
+         * Byte 2 & 3: packet size
+         * Byte 4: flags
+         * Byte 5: constants
+         * Byte 6 & up: headers
+         */
+        byte[] packet = new byte[totalLength];
+        packet[0] = (byte)flags;
+        packet[1] = (byte)0x00;
+        if (header != null) {
+            System.arraycopy(head, 0, packet, 2, head.length);
+        }
+
+        HeaderSet returnHeaderSet = new HeaderSet();
+        sendRequest(0x85, packet, returnHeaderSet, null);
+
+        /*
+         * An OBEX SETPATH reply from the server:
+         * Byte 1: Response code
+         * Bytes 2 & 3: packet size
+         * Bytes 4 & up: headers
+         */
+
+        synchronized (this) {
+            isActive = false;
+        }
+
+        return returnHeaderSet;
+    }
+
+    /**
+     * Verifies that the connection is open.
+     *
+     * @exception IOException if the connection is closed
+     */
+    public synchronized void ensureOpen() throws IOException {
+        if (!connectionOpen) {
+            throw new IOException("Connection closed");
+        }
+    }
+
+    /**
+     * Sets the active mode to off.  This allows Put and get operation objects
+     * to tell this object when they are done.
+     */
+    public void setInactive() {
+        synchronized (this) {
+            isActive = false;
+        }
+    }
+
+    /**
+     * Sends a standard request to the client.  It will then wait for the reply
+     * and update the header set object provided.  If any authentication
+     * headers (i.e. authentication challenge or authentication response) are
+     * received, they will be processed.
+     *
+     * @param code the type of request to send to the client
+     *
+     * @param head the headers to send to the server
+     *
+     * @param challenge the nonce that was sent in the authentication
+     * challenge header located in <code>head</code>; <code>null</code>
+     * if no authentication header is included in <code>head</code>
+     *
+     * @param headers the header object to update with the response
+     *
+     * @param input the input stream used by the Operation object; null if this
+     * is called on a CONNECT, SETPATH or DISCONNECT
+     *
+     * return <code>true</code> if the operation completed successfully;
+     * <code>false</code> if an authentication response failed to pass
+     *
+     * @exception IOException if an IO error occurs
+     */
+    public boolean sendRequest(int code, byte[] head, HeaderSet headers,
+            PrivateInputStream privateInput) throws IOException {
+        //check header length with local max size
+        if (head != null) {
+            if ((head.length + 3) > OBEXConstants.MAX_PACKET_SIZE_INT) {
+                throw new IOException("header too large ");
+            }
+        }
+        //byte[] nonce;
+        int bytesReceived;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write((byte)code);
+
+        // Determine if there are any headers to send
+        if (head == null) {
+            out.write(0x00);
+            out.write(0x03);
+        } else {
+            out.write((byte)((head.length + 3) >> 8));
+            out.write((byte)(head.length + 3));
+            out.write(head);
+        }
+
+        // Write the request to the output stream and flush the stream
+        output.write(out.toByteArray());
+        output.flush();
+
+        headers.responseCode = input.read();
+
+        int length = ((input.read() << 8) | (input.read()));
+
+        if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            throw new IOException("Packet received exceeds packet size limit");
+        }
+        if (length > 3) {
+            byte[] data = null;
+            if (code == 0x80) {
+                int version = input.read();
+                int flags = input.read();
+                maxPacketSize = (input.read() << 8) + input.read();
+
+                //check with local max size
+                if (maxPacketSize > OBEXConstants.MAX_PACKET_SIZE_INT) {
+                    maxPacketSize = OBEXConstants.MAX_PACKET_SIZE_INT;
+                }
+
+                if (length > 7) {
+                    data = new byte[length - 7];
+
+                    bytesReceived = input.read(data);
+                    while (bytesReceived != (length - 7)) {
+                        bytesReceived += input.read(data, bytesReceived, data.length
+                                - bytesReceived);
+                    }
+                } else {
+                    return true;
+                }
+            } else {
+                data = new byte[length - 3];
+                bytesReceived = input.read(data);
+
+                while (bytesReceived != (length - 3)) {
+                    bytesReceived += input.read(data, bytesReceived, data.length - bytesReceived);
+                }
+                if (code == 0xFF) {
+                    return true;
+                }
+            }
+
+            byte[] body = OBEXHelper.updateHeaderSet(headers, data);
+            if ((privateInput != null) && (body != null)) {
+                privateInput.writeBytes(body, 1);
+            }
+
+            if (headers.connectionID != null) {
+                connectionID = new byte[4];
+                System.arraycopy(headers.connectionID, 0, connectionID, 0, 4);
+            }
+
+            if (headers.authResp != null) {
+                if (!handleAuthResp(headers.authResp)) {
+                    setInactive();
+                    throw new IOException("Authentication Failed");
+                }
+            }
+
+            if ((headers.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
+                    && (headers.authChall != null)) {
+
+                if (handleAuthChall(headers)) {
+                    out.write((byte)0x4E);
+                    out.write((byte)((headers.authResp.length + 3) >> 8));
+                    out.write((byte)(headers.authResp.length + 3));
+                    out.write(headers.authResp);
+                    headers.authChall = null;
+                    headers.authResp = null;
+
+                    byte[] sendHeaders = new byte[out.size() - 3];
+                    System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
+
+                    return sendRequest(code, sendHeaders, headers, privateInput);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Called when the client received an authentication challenge header.  This
+     * will cause the authenticator to handle the authentication challenge.
+     *
+     * @param header the header with the authentication challenge
+     *
+     * @return <code>true</code> if the last request should be resent;
+     * <code>false</code> if the last request should not be resent
+     */
+    protected boolean handleAuthChall(HeaderSet header) {
+
+        if (authenticator == null) {
+            return false;
+        }
+
+        /*
+         * An authentication challenge is made up of one required and two
+         * optional tag length value triplets.  The tag 0x00 is required to be
+         * in the authentication challenge and it represents the challenge
+         * digest that was received.  The tag 0x01 is the options tag.  This
+         * tag tracks if user ID is required and if full access will be
+         * granted.  The tag 0x02 is the realm, which provides a description of
+         * which user name and password to use.
+         */
+        byte[] challenge = OBEXHelper.getTagValue((byte)0x00, header.authChall);
+        byte[] option = OBEXHelper.getTagValue((byte)0x01, header.authChall);
+        byte[] description = OBEXHelper.getTagValue((byte)0x02, header.authChall);
+
+        String realm = "";
+        if (description != null) {
+            byte[] realmString = new byte[description.length - 1];
+            System.arraycopy(description, 1, realmString, 0, realmString.length);
+
+            switch (description[0] & 0xFF) {
+
+                case 0x00:
+                    // ASCII encoding
+                    // Fall through
+                case 0x01:
+                    // ISO-8859-1 encoding
+                    try {
+                        realm = new String(realmString, "ISO8859_1");
+                    } catch (Exception e) {
+                        throw new RuntimeException("Unsupported Encoding Scheme");
+                    }
+                    break;
+
+                case 0xFF:
+                    // UNICODE Encoding
+                    realm = OBEXHelper.convertToUnicode(realmString, false);
+                    break;
+
+                case 0x02:
+                    // ISO-8859-2 encoding
+                    // Fall through
+                case 0x03:
+                    // ISO-8859-3 encoding
+                    // Fall through
+                case 0x04:
+                    // ISO-8859-4 encoding
+                    // Fall through
+                case 0x05:
+                    // ISO-8859-5 encoding
+                    // Fall through
+                case 0x06:
+                    // ISO-8859-6 encoding
+                    // Fall through
+                case 0x07:
+                    // ISO-8859-7 encoding
+                    // Fall through
+                case 0x08:
+                    // ISO-8859-8 encoding
+                    // Fall through
+                case 0x09:
+                    // ISO-8859-9 encoding
+                    // Fall through
+                default:
+                    throw new RuntimeException("Unsupported Encoding Scheme");
+            }
+        }
+
+        boolean isUserIDRequired = false;
+        boolean isFullAccess = true;
+        if (option != null) {
+            if ((option[0] & 0x01) != 0) {
+                isUserIDRequired = true;
+            }
+
+            if ((option[0] & 0x02) != 0) {
+                isFullAccess = false;
+            }
+        }
+
+        PasswordAuthentication result = null;
+        header.authChall = null;
+
+        try {
+            result = authenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
+        } catch (Exception e) {
+            return false;
+        }
+
+        /*
+         * If no password was provided then do not resend the request
+         */
+        if (result == null) {
+            return false;
+        }
+
+        byte[] password = result.getPassword();
+        if (password == null) {
+            return false;
+        }
+
+        byte[] userName = result.getUserName();
+
+        /*
+         * Create the authentication response header.  It includes 1 required
+         * and 2 option tag length value triples.  The required triple has a
+         * tag of 0x00 and is the response digest.  The first optional tag is
+         * 0x01 and represents the user ID.  If no user ID is provided, then
+         * no user ID will be sent.  The second optional tag is 0x02 and is the
+         * challenge that was received.  This will always be sent
+         */
+        if (userName != null) {
+            header.authResp = new byte[38 + userName.length];
+            header.authResp[36] = (byte)0x01;
+            header.authResp[37] = (byte)userName.length;
+            System.arraycopy(userName, 0, header.authResp, 38, userName.length);
+        } else {
+            header.authResp = new byte[36];
+        }
+
+        // Create the secret String
+        byte[] digest = new byte[challenge.length + password.length];
+        System.arraycopy(challenge, 0, digest, 0, challenge.length);
+        System.arraycopy(password, 0, digest, challenge.length, password.length);
+
+        // Add the Response Digest
+        header.authResp[0] = (byte)0x00;
+        header.authResp[1] = (byte)0x10;
+
+        byte[] responseDigest = OBEXHelper.computeMD5Hash(digest);
+        System.arraycopy(responseDigest, 0, header.authResp, 2, 16);
+
+        // Add the challenge
+        header.authResp[18] = (byte)0x02;
+        header.authResp[19] = (byte)0x10;
+        System.arraycopy(challenge, 0, header.authResp, 20, 16);
+
+        return true;
+    }
+
+    /**
+     * Called when the client received an authentication response header.  This
+     * will cause the authenticator to handle the authentication response.
+     *
+     * @param authResp the authentication response
+     *
+     * @return <code>true</code> if the response passed; <code>false</code> if
+     * the response failed
+     */
+    protected boolean handleAuthResp(byte[] authResp) {
+        if (authenticator == null) {
+            return false;
+        }
+
+        byte[] correctPassword = authenticator.onAuthenticationResponse(OBEXHelper.getTagValue(
+                (byte)0x01, authResp));
+        if (correctPassword == null) {
+            return false;
+        }
+
+        byte[] temp = new byte[correctPassword.length + 16];
+        System.arraycopy(challengeDigest, 0, temp, 0, 16);
+        System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
+
+        byte[] correctResponse = OBEXHelper.computeMD5Hash(temp);
+        byte[] actualResponse = OBEXHelper.getTagValue((byte)0x00, authResp);
+        for (int i = 0; i < 16; i++) {
+            if (correctResponse[i] != actualResponse[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public void close() throws IOException {
+        connectionOpen = false;
+        input.close();
+        output.close();
+        //client.close();
+    }
+}
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
new file mode 100644
index 0000000..07abe15
--- /dev/null
+++ b/obex/javax/obex/HeaderSet.java
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This class implements the javax.obex.HeaderSet interface for OBEX over
+ * RFCOMM.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public final class HeaderSet {
+
+    /**
+     * Represents the OBEX Count header.  This allows the connection statement
+     * to tell the server how many objects it plans to send or retrieve.
+     * <P>
+     * The value of <code>COUNT</code> is 0xC0 (192).
+     */
+    public static final int COUNT = 0xC0;
+
+    /**
+     * Represents the OBEX Name header.  This specifies the name of the object.
+     * <P>
+     * The value of <code>NAME</code> is 0x01 (1).
+     */
+    public static final int NAME = 0x01;
+
+    /**
+     * Represents the OBEX Type header.  This allows a request to specify the
+     * type of the object (e.g. text, html, binary, etc.).
+     * <P>
+     * The value of <code>TYPE</code> is 0x42 (66).
+     */
+    public static final int TYPE = 0x42;
+
+    /**
+     * Represents the OBEX Length header.  This is the length of the object in
+     * bytes.
+     * <P>
+     * The value of <code>LENGTH</code> is 0xC3 (195).
+     */
+    public static final int LENGTH = 0xC3;
+
+    /**
+     * Represents the OBEX Time header using the ISO 8601 standards.  This is
+     * the preferred time header.
+     * <P>
+     * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
+     */
+    public static final int TIME_ISO_8601 = 0x44;
+
+    /**
+     * Represents the OBEX Time header using the 4 byte representation.  This
+     * is only included for backwards compatibility.  It represents the number
+     * of seconds since January 1, 1970.
+     * <P>
+     * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
+     */
+    public static final int TIME_4_BYTE = 0xC4;
+
+    /**
+     * Represents the OBEX Description header.  This is a text description of
+     * the object.
+     * <P>
+     * The value of <code>DESCRIPTION</code> is 0x05 (5).
+     */
+    public static final int DESCRIPTION = 0x05;
+
+    /**
+     * Represents the OBEX Target header.  This is the name of the service an
+     * operation is targeted to.
+     * <P>
+     * The value of <code>TARGET</code> is 0x46 (70).
+     */
+    public static final int TARGET = 0x46;
+
+    /**
+     * Represents the OBEX HTTP header.  This allows an HTTP 1.X header to be
+     * included in a request or reply.
+     * <P>
+     * The value of <code>HTTP</code> is 0x47 (71).
+     */
+    public static final int HTTP = 0x47;
+
+    /**
+     * Represents the OBEX Who header.  Identifies the OBEX application to
+     * determine if the two peers are talking to each other.
+     * <P>
+     * The value of <code>WHO</code> is 0x4A (74).
+     */
+    public static final int WHO = 0x4A;
+
+    /**
+     * Represents the OBEX Object Class header.  This header specifies the
+     * OBEX object class of the object.
+     * <P>
+     * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
+     */
+    public static final int OBJECT_CLASS = 0x4F;
+
+    /**
+     * Represents the OBEX Application Parameter header.  This header specifies
+     * additional application request and response information.
+     * <P>
+     * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
+     */
+    public static final int APPLICATION_PARAMETER = 0x4C;
+
+    private Long count; // 4 byte unsigned integer
+
+    private String name; // null terminated Unicode text string
+
+    private String type; // null terminated ASCII text string
+
+    private Long length; // 4 byte unsigend integer
+
+    private Calendar isoTime; // String of the form YYYYMMDDTHHMMSSZ
+
+    private Calendar byteTime; // 4 byte unsigned integer
+
+    private String description; // null terminated Unicode text String
+
+    private byte[] target; // byte sequence
+
+    private byte[] http; // byte sequence
+
+    private byte[] who; // length prefixed byte sequence
+
+    private byte[] appParam; // byte sequence of the form tag length
+
+    //value
+    public byte[] authChall; // The authentication challenge header
+
+    public byte[] authResp; // The authentication response header
+
+    public byte[] connectionID; // THe connection ID
+
+    private byte[] objectClass; // byte sequence
+
+    private String[] unicodeUserDefined; //null terminated unicode string
+
+    private byte[][] sequenceUserDefined; // byte sequence user defined
+
+    private Byte[] byteUserDefined; // 1 byte
+
+    private Long[] integerUserDefined; // 4 byte unsigned integer
+
+    public int responseCode;
+
+    public byte[] nonce;
+
+    private Random random;
+
+    /**
+     * Creates new <code>HeaderSet</code> object.
+     *
+     * @param size the max packet size for this connection
+     */
+    public HeaderSet() {
+        unicodeUserDefined = new String[16];
+        sequenceUserDefined = new byte[16][];
+        byteUserDefined = new Byte[16];
+        integerUserDefined = new Long[16];
+        responseCode = -1;
+        random = new Random();
+    }
+
+    /**
+     * Sets the value of the header identifier to the value provided.  The type
+     * of object must correspond to the Java type defined in the description of
+     * this interface.  If <code>null</code> is passed as the
+     * <code>headerValue</code> then the header will be removed from the set of
+     * headers to include in the next request.
+     *
+     * @param headerID the identifier to include in the message
+     *
+     * @param headerValue the value of the header identifier
+     *
+     * @exception IllegalArgumentException if the header identifier provided is
+     * not one defined in this interface or a user-defined header; if the type of
+     * <code>headerValue</code> is not the correct Java type as defined in the
+     * description of this interface\
+     */
+    public void setHeader(int headerID, Object headerValue) {
+        long temp = -1;
+
+        switch (headerID) {
+            case COUNT:
+                if (!(headerValue instanceof Long)) {
+                    if (headerValue == null) {
+                        count = null;
+                        break;
+                    }
+                    throw new IllegalArgumentException("Count must be a Long");
+                }
+                temp = ((Long)headerValue).longValue();
+                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+                    throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
+                }
+                count = (Long)headerValue;
+                break;
+            case NAME:
+                if ((headerValue != null) && (!(headerValue instanceof String))) {
+                    throw new IllegalArgumentException("Name must be a String");
+                }
+                name = (String)headerValue;
+                break;
+            case TYPE:
+                if ((headerValue != null) && (!(headerValue instanceof String))) {
+                    throw new IllegalArgumentException("Type must be a String");
+                }
+                type = (String)headerValue;
+                break;
+            case LENGTH:
+                if (!(headerValue instanceof Long)) {
+                    if (headerValue == null) {
+                        length = null;
+                        break;
+                    }
+                    throw new IllegalArgumentException("Length must be a Long");
+                }
+                temp = ((Long)headerValue).longValue();
+                if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+                    throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
+                }
+                length = (Long)headerValue;
+                break;
+            case TIME_ISO_8601:
+                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
+                    throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
+                }
+                isoTime = (Calendar)headerValue;
+                break;
+            case TIME_4_BYTE:
+                if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
+                    throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
+                }
+                byteTime = (Calendar)headerValue;
+                break;
+            case DESCRIPTION:
+                if ((headerValue != null) && (!(headerValue instanceof String))) {
+                    throw new IllegalArgumentException("Description must be a String");
+                }
+                description = (String)headerValue;
+                break;
+            case TARGET:
+                if (headerValue == null) {
+                    target = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("Target must be a byte array");
+                    } else {
+                        target = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, target, 0, target.length);
+                    }
+                }
+                break;
+            case HTTP:
+                if (headerValue == null) {
+                    http = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("HTTP must be a byte array");
+                    } else {
+                        http = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, http, 0, http.length);
+                    }
+                }
+                break;
+            case WHO:
+                if (headerValue == null) {
+                    who = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("WHO must be a byte array");
+                    } else {
+                        who = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, who, 0, who.length);
+                    }
+                }
+                break;
+            case OBJECT_CLASS:
+                if (headerValue == null) {
+                    objectClass = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException("Object Class must be a byte array");
+                    } else {
+                        objectClass = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, objectClass, 0, objectClass.length);
+                    }
+                }
+                break;
+            case APPLICATION_PARAMETER:
+                if (headerValue == null) {
+                    appParam = null;
+                } else {
+                    if (!(headerValue instanceof byte[])) {
+                        throw new IllegalArgumentException(
+                                "Application Parameter must be a byte array");
+                    } else {
+                        appParam = new byte[((byte[])headerValue).length];
+                        System.arraycopy(headerValue, 0, appParam, 0, appParam.length);
+                    }
+                }
+                break;
+            default:
+                // Verify that it was not a Unicode String user Defined
+                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
+                    if ((headerValue != null) && (!(headerValue instanceof String))) {
+                        throw new IllegalArgumentException(
+                                "Unicode String User Defined must be a String");
+                    }
+                    unicodeUserDefined[headerID - 0x30] = (String)headerValue;
+
+                    break;
+                }
+                // Verify that it was not a byte sequence user defined value
+                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
+
+                    if (headerValue == null) {
+                        sequenceUserDefined[headerID - 0x70] = null;
+                    } else {
+                        if (!(headerValue instanceof byte[])) {
+                            throw new IllegalArgumentException(
+                                    "Byte Sequence User Defined must be a byte array");
+                        } else {
+                            sequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
+                            System.arraycopy(headerValue, 0, sequenceUserDefined[headerID - 0x70],
+                                    0, sequenceUserDefined[headerID - 0x70].length);
+                        }
+                    }
+                    break;
+                }
+                // Verify that it was not a Byte user Defined
+                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
+                    if ((headerValue != null) && (!(headerValue instanceof Byte))) {
+                        throw new IllegalArgumentException("ByteUser Defined must be a Byte");
+                    }
+                    byteUserDefined[headerID - 0xB0] = (Byte)headerValue;
+
+                    break;
+                }
+                // Verify that is was not the 4 byte unsigned integer user
+                // defined header
+                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
+                    if (!(headerValue instanceof Long)) {
+                        if (headerValue == null) {
+                            integerUserDefined[headerID - 0xF0] = null;
+                            break;
+                        }
+                        throw new IllegalArgumentException("Integer User Defined must be a Long");
+                    }
+                    temp = ((Long)headerValue).longValue();
+                    if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+                        throw new IllegalArgumentException(
+                                "Integer User Defined must be between 0 and 0xFFFFFFFF");
+                    }
+                    integerUserDefined[headerID - 0xF0] = (Long)headerValue;
+                    break;
+                }
+                throw new IllegalArgumentException("Invalid Header Identifier");
+        }
+    }
+
+    /**
+     * Retrieves the value of the header identifier provided.  The type of the
+     * Object returned is defined in the description of this interface.
+     *
+     * @param headerID the header identifier whose value is to be returned
+     *
+     * @return the value of the header provided or <code>null</code> if the
+     * header identifier specified is not part of this <code>HeaderSet</code>
+     * object
+     *
+     * @exception IllegalArgumentException if the <code>headerID</code> is not
+     * one defined in this interface or any of the user-defined headers
+     *
+     * @exception IOException if an error occurred in the transport layer during
+     * the operation or if the connection has been closed
+     */
+    public Object getHeader(int headerID) throws IOException {
+
+        switch (headerID) {
+            case COUNT:
+                return count;
+            case NAME:
+                return name;
+            case TYPE:
+                return type;
+            case LENGTH:
+                return length;
+            case TIME_ISO_8601:
+                return isoTime;
+            case TIME_4_BYTE:
+                return byteTime;
+            case DESCRIPTION:
+                return description;
+            case TARGET:
+                return target;
+            case HTTP:
+                return http;
+            case WHO:
+                return who;
+            case OBJECT_CLASS:
+                return objectClass;
+            case APPLICATION_PARAMETER:
+                return appParam;
+            default:
+                // Verify that it was not a Unicode String user Defined
+                if ((headerID >= 0x30) && (headerID <= 0x3F)) {
+                    return unicodeUserDefined[headerID - 0x30];
+                }
+                // Verify that it was not a byte sequence user defined header
+                if ((headerID >= 0x70) && (headerID <= 0x7F)) {
+                    return sequenceUserDefined[headerID - 0x70];
+                }
+                // Verify that it was not a byte user defined header
+                if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
+                    return byteUserDefined[headerID - 0xB0];
+                }
+                // Verify that it was not a itneger user defined header
+                if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
+                    return integerUserDefined[headerID - 0xF0];
+                }
+                throw new IllegalArgumentException("Invalid Header Identifier");
+        }
+    }
+
+    /**
+     * Retrieves the list of headers that may be retrieved via the
+     * <code>getHeader</code> method that will not return <code>null</code>.
+     * In other words, this method returns all the headers that are available
+     * in this object.
+     *
+     * @see #getHeader
+     *
+     * @return the array of headers that are set in this object or
+     * <code>null</code> if no headers are available
+     *
+     * @exception IOException if an error occurred in the transport layer during
+     * the operation or the connection has been closed
+     */
+    public int[] getHeaderList() throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        if (count != null) {
+            out.write(COUNT);
+        }
+        if (name != null) {
+            out.write(NAME);
+        }
+        if (type != null) {
+            out.write(TYPE);
+        }
+        if (length != null) {
+            out.write(LENGTH);
+        }
+        if (isoTime != null) {
+            out.write(TIME_ISO_8601);
+        }
+        if (byteTime != null) {
+            out.write(TIME_4_BYTE);
+        }
+        if (description != null) {
+            out.write(DESCRIPTION);
+        }
+        if (target != null) {
+            out.write(TARGET);
+        }
+        if (http != null) {
+            out.write(HTTP);
+        }
+        if (who != null) {
+            out.write(WHO);
+        }
+        if (appParam != null) {
+            out.write(APPLICATION_PARAMETER);
+        }
+        if (objectClass != null) {
+            out.write(OBJECT_CLASS);
+        }
+
+        for (int i = 0x30; i < 0x40; i++) {
+            if (unicodeUserDefined[i - 0x30] != null) {
+                out.write(i);
+            }
+        }
+
+        for (int i = 0x70; i < 0x80; i++) {
+            if (sequenceUserDefined[i - 0x70] != null) {
+                out.write(i);
+            }
+        }
+
+        for (int i = 0xB0; i < 0xC0; i++) {
+            if (byteUserDefined[i - 0xB0] != null) {
+                out.write(i);
+            }
+        }
+
+        for (int i = 0xF0; i < 0x100; i++) {
+            if (integerUserDefined[i - 0xF0] != null) {
+                out.write(i);
+            }
+        }
+
+        byte[] headers = out.toByteArray();
+        out.close();
+
+        if ((headers == null) || (headers.length == 0)) {
+            return null;
+        }
+
+        int[] result = new int[headers.length];
+        for (int i = 0; i < headers.length; i++) {
+            // Convert the byte to a positive integer.  That is, an integer
+            // between 0 and 256.
+            result[i] = headers[i] & 0xFF;
+        }
+
+        return result;
+    }
+
+    /**
+     * Sets the authentication challenge header.  The <code>realm</code> will
+     * be encoded based upon the default encoding scheme used by the
+     * implementation to encode strings.  Therefore, the encoding scheme used
+     * to encode the <code>realm</code> is application dependent.
+     *
+     * @param realm a short description that describes what password to use; if
+     * <code>null</code> no realm will be sent in the authentication challenge
+     * header
+     *
+     * @param userID if <code>true</code>, a user ID is required in the reply;
+     * if <code>false</code>, no user ID is required
+     *
+     * @param access if <code>true</code> then full access will be granted if
+     * successful; if <code>false</code> then read-only access will be granted
+     * if successful
+     */
+    public void createAuthenticationChallenge(String realm, boolean userID, boolean access) {
+
+        try {
+            nonce = new byte[16];
+            for (int i = 0; i < 16; i++) {
+                nonce[i] = (byte)random.nextInt();
+            }
+
+            authChall = OBEXHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
+        } catch (IOException e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    /**
+     * Returns the response code received from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> class.
+     *
+     * @see ResponseCodes
+     *
+     * @return the response code retrieved from the server
+     *
+     * @exception IOException if an error occurred in the transport layer during
+     * the transaction; if this method is called on a <code>HeaderSet</code>
+     * object created by calling <code>createHeaderSet()</code> in a
+     * <code>ClientSession</code> object; if this object was created by an OBEX
+     * server
+     */
+    public int getResponseCode() throws IOException {
+        if (responseCode == -1) {
+            throw new IOException("May not be called on a server");
+        } else {
+            return responseCode;
+        }
+    }
+}
diff --git a/obex/javax/obex/OBEXConstants.java b/obex/javax/obex/OBEXConstants.java
new file mode 100644
index 0000000..2da5f30
--- /dev/null
+++ b/obex/javax/obex/OBEXConstants.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This class implements the <code>Operation</code> interface.  It will read
+ * and write data via puts and gets.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class OBEXConstants {
+    /**
+     * Defines the OBEX CONTINUE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
+     */
+    public static final int OBEX_HTTP_CONTINUE = 0x90;
+
+    /**
+     * The maximum packet size for OBEX packets that this client can handle.
+     * At present, this must be changed for each port.
+     *
+     * OPTIMIZATION: The max packet size should be the Max incoming MTU minus
+     * OPTIMIZATION: L2CAP package headers and RFCOMM package headers.
+     *
+     * OPTIMIZATION: Retrieve the max incoming MTU from
+     * OPTIMIZATION: LocalDevice.getProperty().
+     */
+    /** android note
+     *  set as 0xFFFE to match remote MPS
+     */
+    //public static final byte[] MAX_PACKET_SIZE = {0x01, 0x00};  // To be removed
+    //public static final int MAX_PACKET_SIZE_INT = 667*6;//0x0100; 
+    public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
+
+    /**
+     * The number of server parser threads that may be active at one time.
+     * This should be changed for each port.
+     *
+     * OPTIMIZATION: Retrieve this value by a native call to the KOSI layer.
+     */
+    public static final int MAX_PARSER_THREADS = 5;
+
+}
diff --git a/obex/javax/obex/OBEXHelper.java b/obex/javax/obex/OBEXHelper.java
new file mode 100644
index 0000000..5c413af
--- /dev/null
+++ b/obex/javax/obex/OBEXHelper.java
@@ -0,0 +1,1326 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * This class defines a set of helper methods for the implementation of OBEX.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class OBEXHelper {
+
+    /**
+     * Creates new OBEXHelper
+     */
+    private OBEXHelper() {
+    }
+
+    private static final String TAG = "OBEXHelper";
+
+    /**
+     * Updates the HeaderSet with the headers received in the byte array
+     * provided.  Invalid headers are ignored.
+     * <P>
+     * The first two bits of an OBEX Header specifies the type of object that
+     * is being sent.  The table below specifies the meaning of the high
+     * bits.
+     * <TABLE>
+     * <TR><TH>Bits 8 and 7</TH><TH>Value</TH><TH>Description</TH></TR>
+     * <TR><TD>00</TD><TD>0x00</TD><TD>Null Terminated Unicode text, prefixed
+     * with 2 byte unsigned integer</TD></TR>
+     * <TR><TD>01</TD><TD>0x40</TD><TD>Byte Sequence, length prefixed with
+     * 2 byte unsigned integer</TD></TR>
+     * <TR><TD>10</TD><TD>0x80</TD><TD>1 byte quantity</TD></TR>
+     * <TR><TD>11</TD><TD>0xC0</TD><TD>4 byte quantity - transmitted in
+     * network byte order (high byte first</TD></TR>
+     * </TABLE>
+     * This method uses the information in this table to determine the type of
+     * Java object to create and passes that object with the full header
+     * to setHeader() to update the HeaderSet object.  Invalid headers will
+     * cause an exception to be thrown.  When it is thrown, it is ignored.
+     *
+     * @param header the HeaderSet to update
+     *
+     * @param headerArray the byte array containing headers
+     *
+     * @return the result of the last start body or end body header provided;
+     * the first byte in the result will specify if a body or end of body is
+     * received
+     *
+     * @exception IOException if an invalid header was found
+     */
+    public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
+        int index = 0;
+        int length = 0;
+        int headerID;
+        byte[] value = null;
+        byte[] body = null;
+        HeaderSet headerImpl = header;
+        try {
+            while (index < headerArray.length) {
+                headerID = 0xFF & headerArray[index];
+                switch (headerID & (0xC0)) {
+
+                    /*
+                     * 0x00 is a unicode null terminate string with the first
+                     * two bytes after the header identifier being the length
+                     */
+                    case 0x00:
+                        // Fall through
+                        /*
+                         * 0x40 is a byte sequence with the first
+                         * two bytes after the header identifier being the length
+                         */
+                    case 0x40:
+                        boolean trimTail = true;
+                        index++;
+                        length = 0xFF & headerArray[index];
+                        length = length << 8;
+                        index++;
+                        length += 0xFF & headerArray[index];
+                        length -= 3;
+                        index++;
+                        value = new byte[length];
+                        System.arraycopy(headerArray, index, value, 0, length);
+                        if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
+                            trimTail = false;
+                        }
+                        switch (headerID) {
+                            case HeaderSet.TYPE:
+                                try {
+                                    // Remove trailing null
+                                    if (trimTail == false) {
+                                        headerImpl.setHeader(headerID, new String(value, 0,
+                                                value.length, "ISO8859_1"));
+                                    } else {
+                                        headerImpl.setHeader(headerID, new String(value, 0,
+                                                value.length - 1, "ISO8859_1"));
+                                    }
+                                } catch (UnsupportedEncodingException e) {
+                                    throw new RuntimeException("ISO8859_1 is not supported"
+                                            + e.getMessage());
+                                }
+                                break;
+
+                            // This is the constant for the authentication challenge header
+                            // This header does not have a constant defined in the Java
+                            // OBEX API
+                            case 0x4D:
+                                headerImpl.authChall = new byte[length];
+                                System.arraycopy(headerArray, index, headerImpl.authChall, 0,
+                                        length);
+                                break;
+
+                            // This is the constant for the authentication response header
+                            // This header does not have a constant defined in the Java
+                            // OBEX API
+                            case 0x4E:
+                                headerImpl.authResp = new byte[length];
+                                System
+                                        .arraycopy(headerArray, index, headerImpl.authResp, 0,
+                                                length);
+                                break;
+
+                            /*
+                            * These two case statements are for the body (0x48)
+                             * and end of body (0x49) headers.
+                            */
+                            case 0x48:
+                                /* Fall Through */
+                            case 0x49:
+                                body = new byte[length + 1];
+                                body[0] = (byte)headerID;
+                                System.arraycopy(headerArray, index, body, 1, length);
+                                break;
+
+                            case HeaderSet.TIME_ISO_8601:
+                                try {
+                                    String dateString = new String(value, "ISO8859_1");
+                                    Calendar temp = Calendar.getInstance();
+                                    if ((dateString.length() == 16)
+                                            && (dateString.charAt(15) == 'Z')) {
+                                        temp.setTimeZone(TimeZone.getTimeZone("UTC"));
+                                    }
+                                    temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
+                                            0, 4)));
+                                    temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
+                                            4, 6)));
+                                    temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
+                                            .substring(6, 8)));
+                                    temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
+                                            .substring(9, 11)));
+                                    temp.set(Calendar.MINUTE, Integer.parseInt(dateString
+                                            .substring(11, 13)));
+                                    temp.set(Calendar.SECOND, Integer.parseInt(dateString
+                                            .substring(13, 15)));
+                                    headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
+                                } catch (UnsupportedEncodingException e) {
+                                    throw new RuntimeException("ISO8859_1 is not supported"
+                                            + e.getMessage());
+                                } catch (Exception e) {
+                                    throw new IOException(
+                                            "Time Header does not follow ISO 8601 standard");
+                                }
+                                break;
+
+                            default:
+                                try {
+                                    if ((headerID & 0xC0) == 0x00) {
+                                        headerImpl.setHeader(headerID, OBEXHelper.convertToUnicode(
+                                                value, true));
+                                    } else {
+                                        headerImpl.setHeader(headerID, value);
+                                    }
+                                } catch (Exception e) {
+                                    // Not a valid header so ignore
+                                }
+                        }
+
+                        index += length;
+                        break;
+
+                    /*
+                     * 0x80 is a byte header.  The only valid byte headers are
+                     * the 16 user defined byte headers.
+                     */
+                    case 0x80:
+                        index++;
+                        try {
+                            headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
+                        } catch (Exception e) {
+                            // Not a valid header so ignore
+                        }
+                        index++;
+                        break;
+
+                    /*
+                     * 0xC0 is a 4 byte unsigned integer header and with the
+                     * exception of TIME_4_BYTE will be converted to a Long
+                     * and added.
+                     */
+                    case 0xC0:
+                        index++;
+                        value = new byte[4];
+                        System.arraycopy(headerArray, index, value, 0, 4);
+                        try {
+                            if (headerID != HeaderSet.TIME_4_BYTE) {
+                                // Determine if it is a connection ID.  These
+                                // need to be handled differently
+                                if (headerID == 0xCB) {
+                                    headerImpl.connectionID = new byte[4];
+                                    System.arraycopy(value, 0, headerImpl.connectionID, 0, 4);
+                                } else {
+                                    headerImpl.setHeader(headerID, Long
+                                            .valueOf(convertToLong(value)));
+                                }
+                            } else {
+                                Calendar temp = Calendar.getInstance();
+                                temp.setTime(new Date(convertToLong(value) * 1000L));
+                                headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
+                            }
+                        } catch (Exception e) {
+                            // Not a valid header so ignore
+                            throw new IOException("Header was not formatted properly");
+                        }
+                        index += 4;
+                        break;
+                }
+
+            }
+        } catch (IOException e) {
+            throw new IOException("Header was not formatted properly");
+        }
+
+        return body;
+    }
+
+    /**
+     * Creates the header part of OBEX packet based on the header provided.
+     *
+     * OPTIMIZATION: Could use getHeaderList() to get the array of headers to
+     * OPTIMIZATION: include and then use the high two bits to determine the
+     * OPTIMIZATION: the type of the object and construct the byte array from
+     * OPTIMIZATION: that.  This will make the size smaller.
+     *
+     * @param head the header used to construct the byte array
+     *
+     * @param nullOut <code>true</code> if the header should be set to
+     * <code>null</code> once it is added to the array or <code>false</code>
+     * if it should not be nulled out
+     *
+     * @return the header of an OBEX packet
+     */
+    public static byte[] createHeader(HeaderSet head, boolean nullOut) {
+        Long intHeader = null;
+        String stringHeader = null;
+        Calendar dateHeader = null;
+        Byte byteHeader = null;
+        StringBuffer buffer = null;
+        byte[] value = null;
+        byte[] result = null;
+        byte[] lengthArray = new byte[2];
+        int length;
+        HeaderSet headImpl = null;
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        if (!(head instanceof HeaderSet)) {
+            throw new IllegalArgumentException("Header not created by createHeaderSet");
+        }
+        headImpl = head;
+
+        try {
+            /*
+             * Determine if there is a connection ID to send.  If there is,
+             * then it should be the first header in the packet.
+             */
+            if ((headImpl.connectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
+
+                out.write((byte)0xCB);
+                out.write(headImpl.connectionID);
+            }
+
+            // Count Header
+            intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
+            if (intHeader != null) {
+                out.write((byte)HeaderSet.COUNT);
+                value = OBEXHelper.convertToByteArray(intHeader.longValue());
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.COUNT, null);
+                }
+            }
+
+            // Name Header
+            stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
+            if (stringHeader != null) {
+                out.write((byte)HeaderSet.NAME);
+                value = OBEXHelper.convertToUnicodeByteArray(stringHeader);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.NAME, null);
+                }
+            }
+
+            // Type Header
+            stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
+            if (stringHeader != null) {
+                out.write((byte)HeaderSet.TYPE);
+                try {
+                    value = stringHeader.getBytes("ISO8859_1");
+                } catch (UnsupportedEncodingException e) {
+                    throw new RuntimeException("Unsupported Encoding Scheme: " + e.getMessage());
+                }
+
+                length = value.length + 4;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                out.write(0x00);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TYPE, null);
+                }
+            }
+
+            // Length Header
+            intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
+            if (intHeader != null) {
+                out.write((byte)HeaderSet.LENGTH);
+                value = OBEXHelper.convertToByteArray(intHeader.longValue());
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.LENGTH, null);
+                }
+            }
+
+            // Time ISO Header
+            dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
+            if (dateHeader != null) {
+
+                /*
+                 * The ISO Header should take the form YYYYMMDDTHHMMSSZ.  The
+                 * 'Z' will only be included if it is a UTC time.
+                 */
+                buffer = new StringBuffer();
+                int temp = dateHeader.get(Calendar.YEAR);
+                for (int i = temp; i < 1000; i = i * 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.MONTH);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.DAY_OF_MONTH);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                buffer.append("T");
+                temp = dateHeader.get(Calendar.HOUR_OF_DAY);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.MINUTE);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+                temp = dateHeader.get(Calendar.SECOND);
+                if (temp < 10) {
+                    buffer.append("0");
+                }
+                buffer.append(temp);
+
+                if (dateHeader.getTimeZone().getID().equals("UTC")) {
+                    buffer.append("Z");
+                }
+
+                try {
+                    value = buffer.toString().getBytes("ISO8859_1");
+                } catch (UnsupportedEncodingException e) {
+                    throw new RuntimeException("UnsupportedEncodingException: " + e.getMessage());
+                }
+
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(HeaderSet.TIME_ISO_8601);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
+                }
+            }
+
+            // Time 4 Byte Header
+            dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
+            if (dateHeader != null) {
+                out.write(HeaderSet.TIME_4_BYTE);
+
+                /*
+                 * Need to call getTime() twice.  The first call will return
+                 * a java.util.Date object.  The second call returns the number
+                 * of milliseconds since January 1, 1970.  We need to convert
+                 * it to seconds since the TIME_4_BYTE expects the number of
+                 * seconds since January 1, 1970.
+                 */
+                value = OBEXHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
+                }
+            }
+
+            // Description Header
+            stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
+            if (stringHeader != null) {
+                out.write((byte)HeaderSet.DESCRIPTION);
+                value = OBEXHelper.convertToUnicodeByteArray(stringHeader);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.DESCRIPTION, null);
+                }
+            }
+
+            // Target Header
+            value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
+            if (value != null) {
+                out.write((byte)HeaderSet.TARGET);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.TARGET, null);
+                }
+            }
+
+            // HTTP Header
+            value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
+            if (value != null) {
+                out.write((byte)HeaderSet.HTTP);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.HTTP, null);
+                }
+            }
+
+            // Who Header
+            value = (byte[])headImpl.getHeader(HeaderSet.WHO);
+            if (value != null) {
+                out.write((byte)HeaderSet.WHO);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.WHO, null);
+                }
+            }
+
+            // Connection ID Header
+            value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
+            if (value != null) {
+                out.write((byte)HeaderSet.APPLICATION_PARAMETER);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
+                }
+            }
+
+            // Object Class Header
+            value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
+            if (value != null) {
+                out.write((byte)HeaderSet.OBJECT_CLASS);
+                length = value.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(value);
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
+                }
+            }
+
+            // Check User Defined Headers
+            for (int i = 0; i < 16; i++) {
+
+                //Unicode String Header
+                stringHeader = (String)headImpl.getHeader(i + 0x30);
+                if (stringHeader != null) {
+                    out.write((byte)i + 0x30);
+                    value = OBEXHelper.convertToUnicodeByteArray(stringHeader);
+                    length = value.length + 3;
+                    lengthArray[0] = (byte)(255 & (length >> 8));
+                    lengthArray[1] = (byte)(255 & length);
+                    out.write(lengthArray);
+                    out.write(value);
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0x30, null);
+                    }
+                }
+
+                // Byte Sequence Header
+                value = (byte[])headImpl.getHeader(i + 0x70);
+                if (value != null) {
+                    out.write((byte)i + 0x70);
+                    length = value.length + 3;
+                    lengthArray[0] = (byte)(255 & (length >> 8));
+                    lengthArray[1] = (byte)(255 & length);
+                    out.write(lengthArray);
+                    out.write(value);
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0x70, null);
+                    }
+                }
+
+                // Byte Header
+                byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
+                if (byteHeader != null) {
+                    out.write((byte)i + 0xB0);
+                    out.write(byteHeader.byteValue());
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0xB0, null);
+                    }
+                }
+
+                // Integer header
+                intHeader = (Long)headImpl.getHeader(i + 0xF0);
+                if (intHeader != null) {
+                    out.write((byte)i + 0xF0);
+                    out.write(OBEXHelper.convertToByteArray(intHeader.longValue()));
+                    if (nullOut) {
+                        headImpl.setHeader(i + 0xF0, null);
+                    }
+                }
+            }
+
+            // Add the authentication challenge header
+            if (headImpl.authChall != null) {
+                out.write((byte)0x4D);
+                length = headImpl.authChall.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(headImpl.authChall);
+                if (nullOut) {
+                    headImpl.authChall = null;
+                }
+            }
+
+            // Add the authentication response header
+            if (headImpl.authResp != null) {
+                out.write((byte)0x4E);
+                length = headImpl.authResp.length + 3;
+                lengthArray[0] = (byte)(255 & (length >> 8));
+                lengthArray[1] = (byte)(255 & length);
+                out.write(lengthArray);
+                out.write(headImpl.authResp);
+                if (nullOut) {
+                    headImpl.authResp = null;
+                }
+            }
+
+        } catch (IOException e) {
+        } finally {
+            result = out.toByteArray();
+            try {
+                out.close();
+            } catch (Exception ex) {
+            }
+        }
+
+        return result;
+
+    }
+
+    /**
+     * Determines where the maximum divide is between headers.  This method is
+     * used by put and get operations to separate headers to a size that meets
+     * the max packet size allowed.
+     *
+     * @param headerArray the headers to separate
+     *
+     * @param start the starting index to search
+     *
+     * @param maxSize the maximum size of a packet
+     *
+     * @return the index of the end of the header block to send or -1 if the
+     * header could not be divided because the header is too large
+     */
+    public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
+
+        int fullLength = 0;
+        int lastLength = -1;
+        int index = start;
+        int length = 0;
+
+        while ((fullLength < maxSize) && (index < headerArray.length)) {
+            int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
+            lastLength = fullLength;
+
+            switch (headerID & (0xC0)) {
+
+                case 0x00:
+                    // Fall through
+                case 0x40:
+
+                    index++;
+                    length = (headerArray[index] < 0 ? headerArray[index] + 256
+                            : headerArray[index]);
+                    length = length << 8;
+                    index++;
+                    length += (headerArray[index] < 0 ? headerArray[index] + 256
+                            : headerArray[index]);
+                    length -= 3;
+                    index++;
+                    index += length;
+                    fullLength += length + 3;
+                    break;
+
+                case 0x80:
+
+                    index++;
+                    index++;
+                    fullLength += 2;
+                    break;
+
+                case 0xC0:
+
+                    index += 5;
+                    fullLength += 5;
+                    break;
+
+            }
+
+        }
+
+        /*
+         * Determine if this is the last header or not
+         */
+        if (lastLength == 0) {
+            /*
+             * Since this is the last header, check to see if the size of this
+             * header is less then maxSize.  If it is, return the length of the
+             * header, otherwise return -1.  The length of the header is
+             * returned since it would be the start of the next header
+             */
+            if (fullLength < maxSize) {
+                return headerArray.length;
+            } else {
+                return -1;
+            }
+        } else {
+            return lastLength + start;
+        }
+    }
+
+    /**
+     * Converts the byte array to a long.
+     *
+     * @param b the byte array to convert to a long
+     *
+     * @return the byte array as a long
+     */
+    public static long convertToLong(byte[] b) {
+        long result = 0;
+        long value = 0;
+        long power = 0;
+
+        for (int i = (b.length - 1); i >= 0; i--) {
+            value = b[i];
+            if (value < 0) {
+                value += 256;
+            }
+
+            result = result | (value << power);
+            power += 8;
+        }
+
+        return result;
+    }
+
+    /**
+     * Converts the long to a 4 byte array.  The long must be non negative.
+     *
+     * @param l the long to convert
+     *
+     * @return a byte array that is the same as the long
+     */
+    public static byte[] convertToByteArray(long l) {
+        byte[] b = new byte[4];
+
+        b[0] = (byte)(255 & (l >> 24));
+        b[1] = (byte)(255 & (l >> 16));
+        b[2] = (byte)(255 & (l >> 8));
+        b[3] = (byte)(255 & l);
+
+        return b;
+    }
+
+    /**
+     * Converts the String to a UNICODE byte array.  It will also add the ending
+     * null characters to the end of the string.
+     *
+     * @param s the string to convert
+     *
+     * @return the unicode byte array of the string
+     */
+    public static byte[] convertToUnicodeByteArray(String s) {
+        if (s == null) {
+            return null;
+        }
+
+        char c[] = s.toCharArray();
+        byte[] result = new byte[(c.length * 2) + 2];
+        for (int i = 0; i < c.length; i++) {
+            result[(i * 2)] = (byte)(c[i] >> 8);
+            result[((i * 2) + 1)] = (byte)c[i];
+        }
+
+        // Add the UNICODE null character
+        result[result.length - 2] = 0;
+        result[result.length - 1] = 0;
+
+        return result;
+    }
+
+    /**
+     * Retrieves the value from the byte array for the tag value specified.  The
+     * array should be of the form Tag - Length - Value triplet.
+     *
+     * @param tag the tag to retrieve from the byte array
+     *
+     * @param triplet the byte sequence containing the tag length value form
+     *
+     * @return the value of the specified tag
+     */
+    public static byte[] getTagValue(byte tag, byte[] triplet) {
+
+        int index = findTag(tag, triplet);
+        if (index == -1) {
+            return null;
+        }
+
+        index++;
+        int length = triplet[index] & 0xFF;
+
+        byte[] result = new byte[length];
+        index++;
+        System.arraycopy(triplet, index, result, 0, length);
+
+        return result;
+    }
+
+    /**
+     * Finds the index that starts the tag value pair in the byte array provide.
+     *
+     * @param tag the tag to look for
+     *
+     * @param value the byte array to search
+     *
+     * @return the starting index of the tag or -1 if the tag could not be found
+     */
+    public static int findTag(byte tag, byte[] value) {
+        int length = 0;
+
+        if (value == null) {
+            return -1;
+        }
+
+        int index = 0;
+
+        while ((index < value.length) && (value[index] != tag)) {
+            length = value[index + 1] & 0xFF;
+            index += length + 2;
+        }
+
+        if (index >= value.length) {
+            return -1;
+        }
+
+        return index;
+    }
+
+    /**
+     * Converts the byte array provided to a unicode string.
+     *
+     * @param b the byte array to convert to a string
+     *
+     * @param includesNull determine if the byte string provided contains the
+     * UNICODE null character at the end or not;  if it does, it will be
+     * removed
+     *
+     * @return a Unicode string
+     *
+     * @param IllegalArgumentException if the byte array has an odd length
+     */
+    public static String convertToUnicode(byte[] b, boolean includesNull) {
+        if (b == null) {
+            return null;
+        }
+        int arrayLength = b.length;
+        if (!((arrayLength % 2) == 0)) {
+            throw new IllegalArgumentException("Byte array not of a valid form");
+        }
+        arrayLength = (arrayLength >> 1);
+        if (includesNull) {
+            arrayLength -= 1;
+        }
+
+        char[] c = new char[arrayLength];
+        for (int i = 0; i < arrayLength; i++) {
+            int upper = b[2 * i];
+            int lower = b[(2 * i) + 1];
+            if (upper < 0) {
+                upper += 256;
+            }
+            if (lower < 0) {
+                lower += 256;
+            }
+
+            c[i] = (char)((upper << 8) | lower);
+        }
+
+        return new String(c);
+    }
+
+    /**
+     * Computes the MD5 hash algorithm on the byte array provided.  This
+     * implementation of MD5 is optimized for OBEX in that it provides for a
+     * single entry and exist and thus does not build up the input before
+     * applying the hash.
+     *
+     * OPTIMIZATION: Embedd MD5 algorithm in this method.  This will make the
+     * OPTIMIZATION: size smaller.
+     *
+     * @param in the byte array to hash
+     *
+     * @return the MD5 hash of the byte array
+     */
+    public static byte[] computeMD5Hash(byte[] in) {
+
+        MD5Hash hash = new MD5Hash(in);
+        return hash.computeDigest();
+    }
+
+    /**
+     * Computes an authentication challenge header.
+     *
+     *
+     * @param nonce the challenge that will be provided to the peer;  the
+     * challenge must be 16 bytes long
+     *
+     * @param realm a short description that describes what password to use
+     *
+     * @param access if <code>true</code> then full access will be granted if
+     * successful; if <code>false</code> then read only access will be granted
+     * if successful
+     *
+     * @param userID if <code>true</code>, a user ID is required in the reply;
+     * if <code>false</code>, no user ID is required
+     *
+     * @exception IllegalArgumentException if the challenge is not 16 bytes
+     * long; if the realm can not be encoded in less then 255 bytes
+     *
+     * @exception IOException if the encoding scheme ISO 8859-1 is not supported
+     */
+    public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
+            boolean userID) throws IOException {
+        byte[] authChall = null;
+
+        if (nonce.length != 16) {
+            throw new IllegalArgumentException("Nonce must be 16 bytes long");
+        }
+
+        /*
+         * The authentication challenge is a byte sequence of the following form
+         * byte 0: 0x00 - the tag for the challenge
+         * byte 1: 0x10 - the length of the challenge; must be 16
+         * byte 2-17: the authentication challenge
+         * byte 18: 0x01 - the options tag; this is optional in the spec, but
+         *                 we are going to include it in every message
+         * byte 19: 0x01 - length of the options; must be 1
+         * byte 20: the value of the options; bit 0 is set if user ID is
+         *          required; bit 1 is set if access mode is read only
+         * byte 21: 0x02 - the tag for authentication realm; only included if
+         *                 an authentication realm is specified
+         * byte 22: the length of the authentication realm; only included if
+         *          the authentication realm is specified
+         * byte 23: the encoding scheme of the authentication realm; we will use
+         *          the ISO 8859-1 encoding scheme since it is part of the KVM
+         * byte 24 & up: the realm if one is specified.
+         */
+        if (realm == null) {
+            authChall = new byte[21];
+        } else {
+            if (realm.length() >= 255) {
+                throw new IllegalArgumentException("Realm must be less then 255 bytes");
+            }
+            authChall = new byte[24 + realm.length()];
+            authChall[21] = 0x02;
+            authChall[22] = (byte)(realm.length() + 1);
+            authChall[23] = 0x01; // ISO 8859-1 Encoding
+            System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
+        }
+
+        // Include the nonce field in the header
+        authChall[0] = 0x00;
+        authChall[1] = 0x10;
+        System.arraycopy(nonce, 0, authChall, 2, 16);
+
+        // Include the options header
+        authChall[18] = 0x01;
+        authChall[19] = 0x01;
+        authChall[20] = 0x00;
+
+        if (!access) {
+            authChall[20] = (byte)(authChall[20] | 0x02);
+        }
+        if (userID) {
+            authChall[20] = (byte)(authChall[20] | 0x01);
+        }
+
+        return authChall;
+    }
+}
+
+/**
+ * This class will complete an MD5 hash of the buffer provided.
+ */
+class MD5Hash {
+
+    // Constants for MD5Transform routine.
+    private static final int A1 = 7;
+
+    private static final int A2 = 12;
+
+    private static final int A3 = 17;
+
+    private static final int A4 = 22;
+
+    private static final int B1 = 5;
+
+    private static final int B2 = 9;
+
+    private static final int B3 = 14;
+
+    private static final int B4 = 20;
+
+    private static final int C1 = 4;
+
+    private static final int C2 = 11;
+
+    private static final int C3 = 16;
+
+    private static final int C4 = 23;
+
+    private static final int D1 = 6;
+
+    private static final int D2 = 10;
+
+    private static final int D3 = 15;
+
+    private static final int D4 = 21;
+
+    private int state[];
+
+    private int count[];
+
+    /**
+     * Keeps the present digest
+     */
+    private byte buffer[];
+
+    /**
+     * Completes a hash on the data provided.
+     *
+     * @param data the byte array to hash
+     */
+    public MD5Hash(byte data[]) {
+        buffer = new byte[64];
+        state = new int[4];
+        count = new int[2];
+
+        state[0] = 0x67452301;
+        state[1] = 0xefcdab89;
+        state[2] = 0x98badcfe;
+        state[3] = 0x10325476;
+
+        count[0] = 0;
+        count[1] = 0;
+
+        MD5Update(data, 0, data.length);
+    }
+
+    /**
+    * Updates the MD5 hash buffer.
+    *
+    * @param input byte array of data
+     *
+    * @param offset offset into the array to start the digest calculation
+     *
+    * @param inputLen the length of the byte array
+    */
+    private void MD5Update(byte input[], int offset, int inputLen) {
+        int i, index, partLen;
+
+        // Compute number of bytes mod 64
+        index = (count[0] >>> 3) & 0x3F;
+
+        // Update number of bits
+        int slen = inputLen << 3;
+        if ((count[0] += slen) < slen) {
+            count[1]++;
+        }
+
+        count[1] += (inputLen >>> 29);
+
+        partLen = 64 - index;
+        // Transform as many times as possible.
+        if (inputLen >= partLen) {
+            copy(index, input, offset, partLen);
+
+            MD5Transform(buffer, 0, 0);
+
+            for (i = partLen; i + 63 < inputLen; i += 64) {
+                MD5Transform(input, offset, i);
+            }
+
+            index = 0;
+        } else {
+            i = 0;
+        }
+
+        copy(index, input, i + offset, inputLen - i);
+    }
+
+    /**
+     * Computes the final MD5 digest
+     *
+     * @return the MD5 digest
+     */
+    public byte[] computeDigest() {
+        byte bits[];
+        byte digest[];
+        int index;
+        int length;
+
+        // Save number of bits
+        bits = MD5Encode(count, 8);
+
+        // Pad out to 56 mod 64.
+        index = ((count[0] >>> 3) & 0x3f);
+        length = (index < 56) ? (56 - index) : (120 - index);
+
+        // build padding buffer.
+        if (length > 0) {
+            byte padding[] = new byte[length];
+            padding[0] = (byte)0x80;
+            for (int i = 1; i < length; i++) {
+                padding[i] = 0;
+            }
+
+            MD5Update(padding, 0, length);
+        }
+
+        // Append length
+        MD5Update(bits, 0, 8);
+
+        // Store state in digest
+        digest = MD5Encode(state, 16);
+
+        return digest;
+    }
+
+    /**
+     * Copies the input array into the local objects buffer.  It performs a
+     * check to verify that data is available to copy.
+     *
+     * @param startIndex the offset into the local buffer to copy
+     *
+     * @param input the array to copy into the local buffer
+     *
+     * @param index the index to start copying from
+     *
+     * @param length the amount of data to copy
+     */
+    private void copy(int startIndex, byte input[], int index, int length) {
+        if (index == input.length)
+            return;
+        System.arraycopy(input, index, buffer, startIndex, length);
+    }
+
+    /**
+     * Rotates the bytes in <code>x</code> <code>n</code> times to the left.
+     * The rotation wraps the bits around.
+     *
+     * @param x the integer to rotate
+     *
+     * @param n the number of bits to rotate
+     *
+     * @return <code>x</code> rotated to the left <code>n</code> times
+     */
+    private int rotate(int x, int n) {
+        return (((x) << (n)) | ((x) >>> (32 - (n))));
+    }
+
+    /**
+     * Completes a single step in the MD5 hash algorithm.
+     */
+    private int MD5Step(int a, int b, int c, int d, int x, int s, int ac, int round) {
+        switch (round) {
+            case 1:
+                a += (((b) & (c)) | ((~b) & (d))) + (x) + (ac);
+                break;
+            case 2:
+                a += (((b) & (d)) | ((c) & (~d))) + (x) + (ac);
+                break;
+            case 3:
+                a += ((b) ^ (c) ^ (d)) + (x) + (ac);
+                break;
+            case 4:
+                a += ((c) ^ ((b) | (~d))) + (x) + (ac);
+                break;
+        }
+        a = rotate(a, (s));
+        a += (b);
+        return a;
+    }
+
+    /**
+     * Performs the core MD5 algorithm.  This method will add the data provided
+     * and recompute the hash.
+     *
+     * @param data the block of data to add
+     *
+     * @param index the starting index into the data to start processing
+     *
+     * @param length the length of the byte array to process
+     */
+    private void MD5Transform(byte data[], int index, int length) {
+        int a = state[0];
+        int b = state[1];
+        int c = state[2];
+        int d = state[3];
+        int x[];
+
+        x = MD5Decode(data, index, length, 64);
+
+        // Round 1
+        a = MD5Step(a, b, c, d, x[0], A1, 0xd76aa478, 1);
+        d = MD5Step(d, a, b, c, x[1], A2, 0xe8c7b756, 1);
+        c = MD5Step(c, d, a, b, x[2], A3, 0x242070db, 1);
+        b = MD5Step(b, c, d, a, x[3], A4, 0xc1bdceee, 1);
+        a = MD5Step(a, b, c, d, x[4], A1, 0xf57c0faf, 1);
+        d = MD5Step(d, a, b, c, x[5], A2, 0x4787c62a, 1);
+        c = MD5Step(c, d, a, b, x[6], A3, 0xa8304613, 1);
+        b = MD5Step(b, c, d, a, x[7], A4, 0xfd469501, 1);
+        a = MD5Step(a, b, c, d, x[8], A1, 0x698098d8, 1);
+        d = MD5Step(d, a, b, c, x[9], A2, 0x8b44f7af, 1);
+        c = MD5Step(c, d, a, b, x[10], A3, 0xffff5bb1, 1);
+        b = MD5Step(b, c, d, a, x[11], A4, 0x895cd7be, 1);
+        a = MD5Step(a, b, c, d, x[12], A1, 0x6b901122, 1);
+        d = MD5Step(d, a, b, c, x[13], A2, 0xfd987193, 1);
+        c = MD5Step(c, d, a, b, x[14], A3, 0xa679438e, 1);
+        b = MD5Step(b, c, d, a, x[15], A4, 0x49b40821, 1);
+
+        // Round 2
+        a = MD5Step(a, b, c, d, x[1], B1, 0xf61e2562, 2);
+        d = MD5Step(d, a, b, c, x[6], B2, 0xc040b340, 2);
+        c = MD5Step(c, d, a, b, x[11], B3, 0x265e5a51, 2);
+        b = MD5Step(b, c, d, a, x[0], B4, 0xe9b6c7aa, 2);
+        a = MD5Step(a, b, c, d, x[5], B1, 0xd62f105d, 2);
+        d = MD5Step(d, a, b, c, x[10], B2, 0x2441453, 2);
+        c = MD5Step(c, d, a, b, x[15], B3, 0xd8a1e681, 2);
+        b = MD5Step(b, c, d, a, x[4], B4, 0xe7d3fbc8, 2);
+        a = MD5Step(a, b, c, d, x[9], B1, 0x21e1cde6, 2);
+        d = MD5Step(d, a, b, c, x[14], B2, 0xc33707d6, 2);
+        c = MD5Step(c, d, a, b, x[3], B3, 0xf4d50d87, 2);
+        b = MD5Step(b, c, d, a, x[8], B4, 0x455a14ed, 2);
+        a = MD5Step(a, b, c, d, x[13], B1, 0xa9e3e905, 2);
+        d = MD5Step(d, a, b, c, x[2], B2, 0xfcefa3f8, 2);
+        c = MD5Step(c, d, a, b, x[7], B3, 0x676f02d9, 2);
+        b = MD5Step(b, c, d, a, x[12], B4, 0x8d2a4c8a, 2);
+
+        // Round 3
+        a = MD5Step(a, b, c, d, x[5], C1, 0xfffa3942, 3);
+        d = MD5Step(d, a, b, c, x[8], C2, 0x8771f681, 3);
+        c = MD5Step(c, d, a, b, x[11], C3, 0x6d9d6122, 3);
+        b = MD5Step(b, c, d, a, x[14], C4, 0xfde5380c, 3);
+        a = MD5Step(a, b, c, d, x[1], C1, 0xa4beea44, 3);
+        d = MD5Step(d, a, b, c, x[4], C2, 0x4bdecfa9, 3);
+        c = MD5Step(c, d, a, b, x[7], C3, 0xf6bb4b60, 3);
+        b = MD5Step(b, c, d, a, x[10], C4, 0xbebfbc70, 3);
+        a = MD5Step(a, b, c, d, x[13], C1, 0x289b7ec6, 3);
+        d = MD5Step(d, a, b, c, x[0], C2, 0xeaa127fa, 3);
+        c = MD5Step(c, d, a, b, x[3], C3, 0xd4ef3085, 3);
+        b = MD5Step(b, c, d, a, x[6], C4, 0x4881d05, 3);
+        a = MD5Step(a, b, c, d, x[9], C1, 0xd9d4d039, 3);
+        d = MD5Step(d, a, b, c, x[12], C2, 0xe6db99e5, 3);
+        c = MD5Step(c, d, a, b, x[15], C3, 0x1fa27cf8, 3);
+        b = MD5Step(b, c, d, a, x[2], C4, 0xc4ac5665, 3);
+
+        // Round 4
+        a = MD5Step(a, b, c, d, x[0], D1, 0xf4292244, 4);
+        d = MD5Step(d, a, b, c, x[7], D2, 0x432aff97, 4);
+        c = MD5Step(c, d, a, b, x[14], D3, 0xab9423a7, 4);
+        b = MD5Step(b, c, d, a, x[5], D4, 0xfc93a039, 4);
+        a = MD5Step(a, b, c, d, x[12], D1, 0x655b59c3, 4);
+        d = MD5Step(d, a, b, c, x[3], D2, 0x8f0ccc92, 4);
+        c = MD5Step(c, d, a, b, x[10], D3, 0xffeff47d, 4);
+        b = MD5Step(b, c, d, a, x[1], D4, 0x85845dd1, 4);
+        a = MD5Step(a, b, c, d, x[8], D1, 0x6fa87e4f, 4);
+        d = MD5Step(d, a, b, c, x[15], D2, 0xfe2ce6e0, 4);
+        c = MD5Step(c, d, a, b, x[6], D3, 0xa3014314, 4);
+        b = MD5Step(b, c, d, a, x[13], D4, 0x4e0811a1, 4);
+        a = MD5Step(a, b, c, d, x[4], D1, 0xf7537e82, 4);
+        d = MD5Step(d, a, b, c, x[11], D2, 0xbd3af235, 4);
+        c = MD5Step(c, d, a, b, x[2], D3, 0x2ad7d2bb, 4);
+        b = MD5Step(b, c, d, a, x[9], D4, 0xeb86d391, 4);
+
+        state[0] += a;
+        state[1] += b;
+        state[2] += c;
+        state[3] += d;
+    }
+
+    /**
+     * Encodes the input array.  <code>input</code> must be a multiple of
+     * four.
+     *
+     * @param input the array to encode
+     *
+     * @param length the length of the array to encode
+     */
+    private byte[] MD5Encode(int input[], int length) {
+        int i, j;
+        byte output[] = new byte[length];
+
+        for (i = 0, j = 0; j < length; i++, j += 4) {
+            output[j] = (byte)(input[i] & 0xff);
+            output[j + 1] = (byte)((input[i] >>> 8) & 0xff);
+            output[j + 2] = (byte)((input[i] >>> 16) & 0xff);
+            output[j + 3] = (byte)((input[i] >>> 24) & 0xff);
+        }
+
+        return output;
+    }
+
+    /**
+     * Decodes the array.  The <code>input</code> array must be a multiple of
+     * four.  Results are undefined if this is not true.
+     *
+     * @param input the array to decode
+     *
+     * @param index the starting position into the input array to start decoding
+     *
+     * @param posn
+     *
+     * @param length
+     */
+    private int[] MD5Decode(byte input[], int offset, int posn, int length) {
+        int output[] = new int[length / 4];
+        int i, j;
+        int limit = length + posn + offset;
+
+        for (i = 0, j = offset + posn; j < limit; i++, j += 4)
+            output[i] = ((input[j]) & 0xff) | (((input[j + 1]) & 0xff) << 8)
+                    | (((input[j + 2]) & 0xff) << 16) | (((input[j + 3]) & 0xff) << 24);
+
+        return output;
+    }
+}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
new file mode 100644
index 0000000..b3da89c
--- /dev/null
+++ b/obex/javax/obex/ObexSession.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * The <code>ObexSession</code> interface characterizes the term
+ * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
+ * could be the server-side view of an OBEX connection, or the client-side view
+ * of the same connection, which is established by server's accepting of a
+ * client issued "CONNECT".
+ * <P>
+ * 
+ * This interface serves as the common super class for
+ * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
+ * 
+ * @version 0.3 November 28, 2008
+ */
+public interface ObexSession {
+
+    /*
+    public InputStream openInputStream() throws IOException;
+
+    public DataInputStream openDataInputStream() throws IOException;
+
+    public OutputStream openOutputStream() throws IOException;
+
+    public DataOutputStream openDataOutputStream() throws IOException;*/
+
+    public void close() throws IOException;
+
+}
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
new file mode 100644
index 0000000..b1734ff
--- /dev/null
+++ b/obex/javax/obex/ObexTransport.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>ObexTransport</code> interface defines the underlying transport
+ * connection which carries the OBEX protocol( such as TCP, RFCOMM device file
+ * exposed by Bluetooth or USB in kernel, RFCOMM socket emulated in Android
+ * platform, Irda). This interface provides an abstract layer to be used by the
+ * <code>ObexConnection</code>. Each kind of medium shall have its own
+ * implementation to wrap and follow the same interface.
+ * <P>
+ * See section 1.2.2 of IrDA Object Exchange Protocol specification.
+ * <P>
+ * Different kind of medium may have different construction - for example, the
+ * RFCOMM device file medium may be constructed from a file descriptor or simply
+ * a string while the TCP medium usually from a socket.
+ * 
+ * @version 0.3 November 28, 2008
+ */
+public interface ObexTransport {
+
+    void create() throws IOException;
+
+    void listen() throws IOException;
+
+    void close() throws IOException;
+
+    void connect() throws IOException;
+
+    void disconnect() throws IOException;
+
+    InputStream openInputStream() throws IOException;
+
+    OutputStream openOutputStream() throws IOException;
+
+    DataInputStream openDataInputStream() throws IOException;
+
+    DataOutputStream openDataOutputStream() throws IOException;
+    //ObexSession accept() throws IOException;
+
+}
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
new file mode 100644
index 0000000..120852a
--- /dev/null
+++ b/obex/javax/obex/Operation.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>Operation</code> interface provides ways to manipulate a single
+ * OBEX PUT or GET operation.  The implementation of this interface sends
+ * OBEX packets as they are built.  If during the operation the peer in the
+ * operation ends the operation, an <code>IOException</code> is thrown on
+ * the next read from the input stream, write to the output stream, or call to
+ * <code>sendHeaders()</code>.
+ * <P>
+ * <STRONG>Definition of methods inherited from <code>ContentConnection</code></STRONG>
+ * <P>
+ * <code>getEncoding()</code> will always return <code>null</code>.
+ * <BR><code>getLength()</code> will return the length specified by the OBEX Length
+ * header or -1 if the OBEX Length header was not included.
+ * <BR><code>getType()</code> will return the value specified in the OBEX Type
+ * header or <code>null</code> if the OBEX Type header was not included.<BR>
+ * <P>
+ * <STRONG>How Headers are Handled</STRONG>
+ * <P>
+ * As headers are received, they may be retrieved through the
+ * <code>getReceivedHeaders()</code> method.  If new headers are set during the
+ * operation, the new headers will be sent during the next packet exchange.
+ * <P>
+ * <STRONG>PUT example</STRONG>
+ * <P>
+ * <PRE>
+ * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj)
+ *     throws IOException {
+ *
+ *     // Include the length header
+ *     head.setHeader(head.LENGTH, new Long(obj.length));
+ *
+ *     // Initiate the PUT request
+ *     Operation op = conn.put(head);
+ *
+ *     // Open the output stream to put the object to it
+ *     DataOutputStream out = op.openDataOutputStream();
+ *
+ *     // Send the object to the server
+ *     out.write(obj);
+ *
+ *     // End the transaction
+ *     out.close();
+ *     op.close();
+ * }
+ * </PRE>
+ * <P>
+ * <STRONG>GET example</STRONG>
+ * <P>
+ * <PRE>
+ * byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
+ *
+ *     // Send the initial GET request to the server
+ *     Operation op = conn.get(head);
+ *
+ *     // Retrieve the length of the object being sent back
+ *     int length = op.getLength();
+ *
+ *      // Create space for the object
+ *      byte[] obj = new byte[length];
+ *
+ *     // Get the object from the input stream
+ *     DataInputStream in = trans.openDataInputStream();
+ *     in.read(obj);
+ *
+ *     // End the transaction
+ *     in.close();
+ *     op.close();
+ *
+ *     return obj;
+ * }
+ * </PRE>
+ * <H3>Client PUT Operation Flow</H3>
+ * For PUT operations, a call to <code>close()</code> the <code>OutputStream</code>
+ * returned from <code>openOutputStream()</code> or <code>openDataOutputStream()</code>
+ * will signal that the request is done.  (In OBEX terms, the End-Of-Body header should
+ * be sent and the final bit in the request will be set.)  At this point, the
+ * reply from the server may begin to be processed.  A call to
+ * <code>getResponseCode()</code> will do an implicit close on the
+ * <code>OutputStream</code> and therefore signal that the request is done.
+ * <H3>Client GET Operation Flow</H3>
+ * For GET operation, a call to <code>openInputStream()</code> or
+ * <code>openDataInputStream()</code> will signal that the request is done.  (In OBEX
+ * terms, the final bit in the request will be set.)  A call to
+ * <code>getResponseCode()</code> will cause an implicit close on the
+ * <code>InputStream</code>.  No further data may be read at this point.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public interface Operation {
+
+    /**
+     * Sends an ABORT message to the server.  By calling this method, the
+     * corresponding input and output streams will be closed along with this
+     * object.  No headers are sent in the abort request.  This will end the
+     * operation since <code>close()</code> will be called by this method.
+     *
+     * @exception IOException if the transaction has already ended or if an
+     * OBEX server calls this method
+     */
+    public void abort() throws IOException;
+
+    /**
+     * Returns the headers that have been received during the operation.
+     * Modifying the object returned has no effect on the headers that are
+     * sent or retrieved.
+     *
+     * @return the headers received during this <code>Operation</code>
+     *
+     * @exception IOException if this <code>Operation</code> has been closed
+     */
+    public HeaderSet getReceivedHeaders() throws IOException;
+
+    /**
+     * Specifies the headers that should be sent in the next OBEX message that
+     * is sent.
+     *
+     * @param headers the headers to send in the next message
+     *
+     * @exception IOException  if this <code>Operation</code> has been closed
+     * or the transaction has ended and no further messages will be exchanged
+     *
+     * @exception IllegalArgumentException if <code>headers</code> was not created
+     * by a call to <code>ServerRequestHandler.createHeaderSet()</code> or
+     * <code>ClientSession.createHeaderSet()</code>
+     *
+     * @exception NullPointerException if <code>headers</code> if <code>null</code>
+     */
+    public void sendHeaders(HeaderSet headers) throws IOException;
+
+    /**
+     * Returns the response code received from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> class.
+     *
+     * @see ResponseCodes
+     *
+     * @return the response code retrieved from the server
+     *
+     * @exception IOException if an error occurred in the transport layer during
+     * the transaction; if this object was created by an OBEX server
+     */
+    public int getResponseCode() throws IOException;
+
+    public String getEncoding();
+
+    public long getLength();
+
+    public String getType();
+
+    public InputStream openInputStream() throws IOException;
+
+    public DataInputStream openDataInputStream() throws IOException;
+
+    public OutputStream openOutputStream() throws IOException;
+
+    public DataOutputStream openDataOutputStream() throws IOException;
+
+    public void close() throws IOException;
+
+    public int getMaxPacketSize();
+}
diff --git a/obex/javax/obex/PasswordAuthentication.java b/obex/javax/obex/PasswordAuthentication.java
new file mode 100644
index 0000000..82f9623
--- /dev/null
+++ b/obex/javax/obex/PasswordAuthentication.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This class holds user name and password combinations.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class PasswordAuthentication {
+
+    private byte[] userName;
+
+    private byte[] password;
+
+    /**
+     * Creates a new <code>PasswordAuthentication</code> with the user name
+     * and password provided.
+     *
+     * @param userName the user name to include; this may be <code>null</code>
+     *
+     * @param password the password to include in the response
+     *
+     * @exception NullPointerException if <code>password</code> is
+     * <code>null</code>
+     */
+    public PasswordAuthentication(byte[] userName, byte[] password) {
+        if (userName != null) {
+            this.userName = new byte[userName.length];
+            System.arraycopy(userName, 0, this.userName, 0, userName.length);
+        }
+
+        this.password = new byte[password.length];
+        System.arraycopy(password, 0, this.password, 0, password.length);
+    }
+
+    /**
+     * Retrieves the user name that was specified in the constructor.
+     * The user name may be <code>null</code>.
+     *
+     * @return the user name
+     */
+    public byte[] getUserName() {
+        return this.userName;
+    }
+
+    /**
+     * Retrieves the password.
+     *
+     * @return the password
+     */
+    public byte[] getPassword() {
+        return this.password;
+    }
+}
diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java
new file mode 100644
index 0000000..1b83bb0
--- /dev/null
+++ b/obex/javax/obex/PrivateInputStream.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+
+/**
+ * This object provides an input stream to the Operation objects used in this
+ * package.
+ *
+ * OPTIMIZATION: Include the other read() methods defined in InputStream.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class PrivateInputStream extends InputStream {
+
+    private BaseStream parent;
+
+    private byte[] data;
+
+    private int index;
+
+    private boolean isOpen;
+
+    public PrivateInputStream() {
+
+    }
+
+    /**
+     * Creates an input stream for the <code>Operation</code> to read from
+     *
+     * @param p the connection this input stream is for
+     */
+    public PrivateInputStream(BaseStream p) {
+        parent = p;
+        data = new byte[0];
+        index = 0;
+        isOpen = true;
+    }
+
+    /**
+     * Returns the number of bytes that can be read (or skipped over) from this
+     * input stream without blocking by the next caller of a method for this
+     * input stream. The next caller might be the same thread or or another
+     * thread.
+     *
+     * @return the number of bytes that can be read from this input stream
+     * without blocking
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized int available() throws IOException {
+        ensureOpen();
+        return data.length - index;
+    }
+
+    /**
+     * Reads the next byte of data from the input stream. The value byte is
+     * returned as an int in the range 0 to 255. If no byte is available
+     * because the end of the stream has been reached, the value -1 is
+     * returned. This method blocks until input data is available, the end of
+     * the stream is detected, or an exception is thrown.
+     *
+     * @return the byte read from the input stream or -1 if it reaches the end
+     * of stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized int read() throws IOException {
+        ensureOpen();
+        while (data.length == index) {
+            if (!parent.continueOperation(true, true)) {
+                return -1;
+            }
+        }
+        return (data[index++] & 0xFF);
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public synchronized int read(byte[] b, int offset, int length) throws IOException {
+
+        if (b == null) {
+            throw new NullPointerException("buffer is null");
+        }
+        if ((offset | length) < 0 || length > b.length - offset) {
+            throw new ArrayIndexOutOfBoundsException("index outof bound");
+        }
+        ensureOpen();
+
+        int currentDataLength = data.length - index;
+        int remainReadLength = length;
+        int offset1 = offset;
+        int result = 0;
+
+        while (currentDataLength <= remainReadLength) {
+            System.arraycopy(data, index, b, offset1, currentDataLength);
+            index += currentDataLength;
+            offset1 += currentDataLength;
+            result += currentDataLength;
+            remainReadLength -= currentDataLength;
+
+            if (!parent.continueOperation(true, true)) {
+                return result == 0 ? -1 : result;
+            }
+            currentDataLength = data.length - index;
+        }
+        if (remainReadLength > 0) {
+            System.arraycopy(data, index, b, offset1, remainReadLength);
+            index += remainReadLength;
+            result += remainReadLength;
+        }
+        return result;
+    }
+
+    /**
+     * Allows the <code>OperationImpl</code> thread to add body data to the
+     * input stream.
+     *
+     * @param body the data to add to the stream
+     *
+     * @param start the start of the body to array to copy
+     */
+    public synchronized void writeBytes(byte[] body, int start) {
+
+        int length = (body.length - start) + (data.length - index);
+        byte[] temp = new byte[length];
+
+        System.arraycopy(data, index, temp, 0, data.length - index);
+        System.arraycopy(body, start, temp, data.length - index, body.length - start);
+
+        data = temp;
+        index = 0;
+        notifyAll();
+    }
+
+    /**
+     * Verifies that this stream is open
+     *
+     * @exception IOException if the stream is not open
+     */
+    private void ensureOpen() throws IOException {
+        parent.ensureOpen();
+        if (!isOpen) {
+            throw new IOException("Input stream is closed");
+        }
+    }
+
+    /**
+     * Closes the input stream.  If the input stream is already closed, do
+     * nothing.
+     *
+     * @exception IOException this will never happen
+     */
+    @Override
+    public void close() throws IOException {
+        isOpen = false;
+        parent.streamClosed(true);
+    }
+}
diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java
new file mode 100644
index 0000000..03d2363
--- /dev/null
+++ b/obex/javax/obex/PrivateOutputStream.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+
+/**
+ * This object provides an output stream to the Operation objects used in this
+ * package.
+ *
+ * @version 0.3 November 28, 2008
+ */
+class PrivateOutputStream extends OutputStream {
+
+    private BaseStream parent;
+
+    private ByteArrayOutputStream output;
+
+    private boolean isClosed;
+
+    private int maxPacketSize;
+
+    /**
+     * Creates an empty <code>PrivateOutputStream</code> to write to.
+     *
+     * @param p the connection that this stream runs over
+     */
+    public PrivateOutputStream(BaseStream p, int maxSize) {
+        parent = p;
+        output = new ByteArrayOutputStream();
+        maxPacketSize = maxSize;
+    }
+
+    /**
+     * Determines how many bytes have been written to the output stream.
+     *
+     * @return the number of bytes written to the output stream
+     */
+    protected int size() {
+        return output.size();
+    }
+
+    /**
+     * Writes the specified byte to this output stream. The general contract
+     * for write is that one byte is written to the output stream. The byte to
+     * be written is the eight low-order bits of the argument b. The 24
+     * high-order bits of b are ignored.
+     *
+     * @param b the byte to write
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized void write(int b) throws IOException {
+        ensureOpen();
+        parent.ensureNotDone();
+        output.write(b);
+        if (output.size() == maxPacketSize) {
+            parent.continueOperation(true, false);
+        }
+    }
+
+    @Override
+    public void write(byte[] buffer) throws IOException {
+        write(buffer, 0, buffer.length);
+    }
+
+    @Override
+    public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
+        int offset1 = offset;
+        int remainLength = count;
+
+        if (buffer == null) {
+            throw new NullPointerException("buffer is null");
+        }
+        if ((offset | count) < 0 || count > buffer.length - offset) {
+            throw new IndexOutOfBoundsException("index outof bound");
+        }
+
+        ensureOpen();
+        parent.ensureNotDone();
+        if (count < maxPacketSize) {
+            output.write(buffer, offset, count);
+        } else {
+            while (remainLength >= maxPacketSize) {
+                output.write(buffer, offset1, maxPacketSize);
+                offset1 += maxPacketSize;
+                remainLength = count - offset1;
+                parent.continueOperation(true, false);
+            }
+            if (remainLength > 0) {
+                output.write(buffer, offset1, remainLength);
+            }
+        }
+    }
+
+    /**
+     * Reads the bytes that have been written to this stream.
+     *
+     * @return the byte array that is written
+     */
+    protected synchronized byte[] readBytes() {
+        if (output.size() > 0) {
+            byte[] result = output.toByteArray();
+            output.reset();
+            return result;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Reads the bytes that have been written to this stream.
+     *
+     * @param size the size of the array to return
+     *
+     * @return the byte array that is written
+     */
+    protected synchronized byte[] readBytes(int size) {
+        if (output.size() > 0) {
+            byte[] temp = output.toByteArray();
+            output.reset();
+            byte[] result = new byte[size];
+            System.arraycopy(temp, 0, result, 0, size);
+            if (temp.length != size) {
+                output.write(temp, size, temp.length - size);
+            }
+            return result;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Verifies that this stream is open
+     *
+     * @exception IOException if the stream is not open
+     */
+    private void ensureOpen() throws IOException {
+        parent.ensureOpen();
+        if (isClosed) {
+            throw new IOException("Output stream is closed");
+        }
+    }
+
+    /**
+     * Closes the output stream.  If the input stream is already closed, do
+     * nothing.
+     *
+     * @exception IOException this will never happen
+     */
+    @Override
+    public void close() throws IOException {
+        isClosed = true;
+        parent.streamClosed(false);
+    }
+
+    /**
+     * Determines if the connection is closed
+     *
+     * @return <code>true</code> if the connection is closed;
+     * <code>false</code> if the connection is open
+     */
+    protected boolean isClosed() {
+        return isClosed;
+    }
+}
diff --git a/obex/javax/obex/ResponseCodes.java b/obex/javax/obex/ResponseCodes.java
new file mode 100644
index 0000000..7496738
--- /dev/null
+++ b/obex/javax/obex/ResponseCodes.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * The <code>ResponseCodes</code> class contains the list of valid
+ * response codes a server may send to a client.
+ * <P>
+ * <STRONG>IMPORTANT NOTE</STRONG>
+ * <P>
+ * It is important to note that these values are different then those defined
+ * in <code>javax.microedition.io.HttpConnection</code>.  The values in this
+ * interface represent the values defined in the IrOBEX specification.  The
+ * values in <code>javax.microedition.io.HttpConnection</code> represent values
+ * defined in the HTTP specification.
+ * <P>
+ * <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
+ * further description since they are not defined in HTTP.  The server will send
+ * an <code>OBEX_DATABASE_FULL</code> message when the client requests that
+ * something be placed into a database but the database is full (cannot take
+ * more data).   <code>OBEX_DATABASE_LOCKED</code> will be returned when the
+ * client wishes to access a database, database table, or database record that
+ * has been locked.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class ResponseCodes {
+
+    /**
+     * Defines the OBEX SUCCESS response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_OK</code> is 0xA0 (160).
+     */
+    public static final int OBEX_HTTP_OK = 0xA0;
+
+    /**
+     * Defines the OBEX CREATED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_CREATED</code> is 0xA1 (161).
+     */
+    public static final int OBEX_HTTP_CREATED = 0xA1;
+
+    /**
+     * Defines the OBEX ACCEPTED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_ACCEPTED</code> is 0xA2 (162).
+     */
+    public static final int OBEX_HTTP_ACCEPTED = 0xA2;
+
+    /**
+     * Defines the OBEX NON-AUTHORITATIVE INFORMATION response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_AUTHORITATIVE</code> is 0xA3 (163).
+     */
+    public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
+
+    /**
+     * Defines the OBEX NO CONTENT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NO_CONTENT</code> is 0xA4 (164).
+     */
+    public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
+
+    /**
+     * Defines the OBEX RESET CONTENT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_RESET</code> is 0xA5 (165).
+     */
+    public static final int OBEX_HTTP_RESET = 0xA5;
+
+    /**
+     * Defines the OBEX PARTIAL CONTENT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PARTIAL</code> is 0xA6 (166).
+     */
+    public static final int OBEX_HTTP_PARTIAL = 0xA6;
+
+    /**
+     * Defines the OBEX MULTIPLE_CHOICES response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_MULT_CHOICE</code> is 0xB0 (176).
+     */
+    public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
+
+    /**
+     * Defines the OBEX MOVED PERMANENTLY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_MOVED_PERM</code> is 0xB1 (177).
+     */
+    public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
+
+    /**
+     * Defines the OBEX MOVED TEMPORARILY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_MOVED_TEMP</code> is 0xB2 (178).
+     */
+    public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
+
+    /**
+     * Defines the OBEX SEE OTHER response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_SEE_OTHER</code> is 0xB3 (179).
+     */
+    public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
+
+    /**
+     * Defines the OBEX NOT MODIFIED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_MODIFIED</code> is 0xB4 (180).
+     */
+    public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
+
+    /**
+     * Defines the OBEX USE PROXY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_USE_PROXY</code> is 0xB5 (181).
+     */
+    public static final int OBEX_HTTP_USE_PROXY = 0xB5;
+
+    /**
+     * Defines the OBEX BAD REQUEST response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_BAD_REQUEST</code> is 0xC0 (192).
+     */
+    public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
+
+    /**
+     * Defines the OBEX UNAUTHORIZED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_UNAUTHORIZED</code> is 0xC1 (193).
+     */
+    public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
+
+    /**
+     * Defines the OBEX PAYMENT REQUIRED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PAYMENT_REQUIRED</code> is 0xC2 (194).
+     */
+    public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
+
+    /**
+     * Defines the OBEX FORBIDDEN response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_FORBIDDEN</code> is 0xC3 (195).
+     */
+    public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
+
+    /**
+     * Defines the OBEX NOT FOUND response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_FOUND</code> is 0xC4 (196).
+     */
+    public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
+
+    /**
+     * Defines the OBEX METHOD NOT ALLOWED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_BAD_METHOD</code> is 0xC5 (197).
+     */
+    public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
+
+    /**
+     * Defines the OBEX NOT ACCEPTABLE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_ACCEPTABLE</code> is 0xC6 (198).
+     */
+    public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
+
+    /**
+     * Defines the OBEX PROXY AUTHENTICATION REQUIRED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PROXY_AUTH</code> is 0xC7 (199).
+     */
+    public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
+
+    /**
+     * Defines the OBEX REQUEST TIME OUT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_TIMEOUT</code> is 0xC8 (200).
+     */
+    public static final int OBEX_HTTP_TIMEOUT = 0xC8;
+
+    /**
+     * Defines the OBEX METHOD CONFLICT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_CONFLICT</code> is 0xC9 (201).
+     */
+    public static final int OBEX_HTTP_CONFLICT = 0xC9;
+
+    /**
+     * Defines the OBEX METHOD GONE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_GONE</code> is 0xCA (202).
+     */
+    public static final int OBEX_HTTP_GONE = 0xCA;
+
+    /**
+     * Defines the OBEX METHOD LENGTH REQUIRED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_LENGTH_REQUIRED</code> is 0xCB (203).
+     */
+    public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
+
+    /**
+     * Defines the OBEX PRECONDITION FAILED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_PRECON_FAILED</code> is 0xCC (204).
+     */
+    public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
+
+    /**
+     * Defines the OBEX REQUESTED ENTITY TOO LARGE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_ENTITY_TOO_LARGE</code> is 0xCD (205).
+     */
+    public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
+
+    /**
+     * Defines the OBEX REQUESTED URL TOO LARGE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_REQ_TOO_LARGE</code> is 0xCE (206).
+     */
+    public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
+
+    /**
+     * Defines the OBEX UNSUPPORTED MEDIA TYPE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_UNSUPPORTED_TYPE</code> is 0xCF (207).
+     */
+    public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
+
+    /**
+     * Defines the OBEX INTERNAL SERVER ERROR response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_INTERNAL_ERROR</code> is 0xD0 (208).
+     */
+    public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
+
+    /**
+     * Defines the OBEX NOT IMPLEMENTED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_NOT_IMPLEMENTED</code> is 0xD1 (209).
+     */
+    public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
+
+    /**
+     * Defines the OBEX BAD GATEWAY response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_BAD_GATEWAY</code> is 0xD2 (210).
+     */
+    public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
+
+    /**
+     * Defines the OBEX SERVICE UNAVAILABLE response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_UNAVAILABLE</code> is 0xD3 (211).
+     */
+    public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
+
+    /**
+     * Defines the OBEX GATEWAY TIMEOUT response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_GATEWAY_TIMEOUT</code> is 0xD4 (212).
+     */
+    public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
+
+    /**
+     * Defines the OBEX HTTP VERSION NOT SUPPORTED response code.
+     * <P>
+     * The value of <code>OBEX_HTTP_VERSION</code> is 0xD5 (213).
+     */
+    public static final int OBEX_HTTP_VERSION = 0xD5;
+
+    /**
+     * Defines the OBEX DATABASE FULL response code.
+     * <P>
+     * The value of <code>OBEX_DATABASE_FULL</code> is 0xE0 (224).
+     */
+    public static final int OBEX_DATABASE_FULL = 0xE0;
+
+    /**
+     * Defines the OBEX DATABASE LOCKED response code.
+     * <P>
+     * The value of <code>OBEX_DATABASE_LOCKED</code> is 0xE1 (225).
+     */
+    public static final int OBEX_DATABASE_LOCKED = 0xE1;
+
+    /**
+     * Constructor does nothing.
+     */
+    private ResponseCodes() {
+        throw new RuntimeException("Not Implemented! Used to compile Code");
+    }
+}
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
new file mode 100644
index 0000000..1b9c4e9
--- /dev/null
+++ b/obex/javax/obex/ServerOperation.java
@@ -0,0 +1,772 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class implements the Operation interface for server side connections.
+ * <P>
+ * <STRONG>Request Codes</STRONG>
+ * There are four different request codes that are in this class.  0x02 is a
+ * PUT request that signals that the request is not complete and requires an
+ * additional OBEX packet.  0x82 is a PUT request that says that request is
+ * complete.  In this case, the server can begin sending the response.  The
+ * 0x03 is a GET request that signals that the request is not finished.  When
+ * the server receives a 0x83, the client is signalling the server that it is
+ * done with its request.
+ *
+ * OPTIMIZATION: Extend the ClientOperation and reuse the methods defined
+ * OPTIMIZATION: in that class.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class ServerOperation implements Operation, BaseStream {
+
+    private InputStream socketInput;
+
+    private ServerSession parent;
+
+    private int maxPacketLength;
+
+    private int responseSize;
+
+    private boolean isClosed;
+
+    boolean finalBitSet;
+
+    private boolean endOfBody; // This variable defines when the end of body
+
+    // header has been received.  When this header
+    // is received, no further body data will be
+    // received from the client
+    private boolean isGet;
+
+    boolean isAborted;
+
+    HeaderSet requestHeaders;
+
+    HeaderSet replyHeaders;
+
+    PrivateInputStream privateInput;
+
+    private PrivateOutputStream privateOutput;
+
+    private String exceptionString;
+
+    private ServerRequestHandler listener;
+
+    private boolean outputStreamOpened;
+
+    private boolean requestFinished;
+
+    private static int BASE_PACKET_LENGTH = 3;
+
+    private static final String TAG = "ServerOperation";
+
+    private boolean isHasBody;
+
+    /**
+     * Creates new PutServerOperation
+     *
+     * @param p the parent that created this object
+     *
+     * @param in the input stream to read from
+     *
+     * @param out the output stream to write to
+     *
+     * @param request the initial request that was received from the client
+     *
+     * @param maxSize the max packet size that the client will accept
+     *
+     * @param listen the listener that is responding to the request
+     *
+     * @exception IOException if an IO error occurs
+     */
+    public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
+            ServerRequestHandler listen) throws IOException {
+
+        isAborted = false;
+        parent = p;
+        socketInput = in;
+        maxPacketLength = maxSize;
+        isClosed = false;
+        requestHeaders = new HeaderSet();
+        replyHeaders = new HeaderSet();
+        privateInput = new PrivateInputStream(this);
+        endOfBody = false;
+        responseSize = 3;
+        listener = listen;
+        requestFinished = false;
+        outputStreamOpened = false;
+        isHasBody = false;
+        int bytesReceived;
+
+        /*
+         * Determine if this is a PUT request
+         */
+        if ((request == 0x02) || (request == 0x82)) {
+            /*
+             * It is a PUT request.
+             */
+            isGet = false;
+        } else {
+            /*
+             * It is a GET request.
+             */
+            isGet = true;
+        }
+
+        /*
+         * Determine if the final bit is set
+         */
+        if ((request & 0x80) == 0) {
+            finalBitSet = false;
+        } else {
+            finalBitSet = true;
+            requestFinished = true;
+        }
+
+        int length = in.read();
+        length = (length << 8) + in.read();
+
+        /*
+         * Determine if the packet length is larger than this device can receive
+         */
+        if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            parent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+            throw new IOException("Packet received was too large");
+        }
+
+        /*
+         * Determine if any headers were sent in the initial request
+         */
+        if (length > 3) {
+            byte[] data = new byte[length - 3];
+            bytesReceived = in.read(data);
+
+            while (bytesReceived != data.length) {
+                bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived);
+            }
+
+            byte[] body = OBEXHelper.updateHeaderSet(requestHeaders, data);
+
+            if (body != null) {
+                isHasBody = true;
+            }
+
+            if (requestHeaders.connectionID != null) {
+                listener.setConnectionID(OBEXHelper.convertToLong(requestHeaders.connectionID));
+            } else {
+                listener.setConnectionID(0);
+            }
+
+            if (requestHeaders.authResp != null) {
+                if (!parent.handleAuthResp(requestHeaders.authResp)) {
+                    exceptionString = "Authentication Failed";
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+                    isClosed = true;
+                    requestHeaders.authResp = null;
+                    return;
+                }
+            }
+
+            if (requestHeaders.authChall != null) {
+                parent.handleAuthChall(requestHeaders);
+                // send the  authResp to the client
+                replyHeaders.authResp = new byte[requestHeaders.authResp.length];
+                System.arraycopy(requestHeaders.authResp, 0, replyHeaders.authResp, 0,
+                        replyHeaders.authResp.length);
+                requestHeaders.authResp = null;
+                requestHeaders.authChall = null;
+
+            }
+
+            if (body != null) {
+                /*
+                 * 0x49 is the end of body header.  This signifies that no more
+                 * body data will be sent from the client
+                 */
+                if (body[0] == 0x49) {
+                    endOfBody = true;
+                }
+                //privateInput.writeBytes(body, body.length);
+                //byte [] body_tmp = new byte[body.length-1];
+                //System.arraycopy(body,1,body_tmp,0,body.length-1);
+                //privateInput.writeBytes(body_tmp, body.length-1);
+                privateInput.writeBytes(body, 1);
+            } else {
+                while ((!isGet) && (!finalBitSet)) {
+                    sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+                    if (privateInput.available() > 0) {
+                        break;
+                    }
+                }
+            }//  if (body != null) 
+
+        }// if (length > 3)
+
+        while ((!isGet) && (!finalBitSet) && (privateInput.available() == 0)) {
+            sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+            if (privateInput.available() > 0) {
+                break;
+            }
+        }
+
+        // wait for get request finished !!!!
+        while (isGet && !finalBitSet) {
+            sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+        }
+        if (finalBitSet && isGet) {
+            requestFinished = true;
+        }
+    }
+
+    public synchronized boolean isValidBody() {
+        return isHasBody;
+    }
+
+    /**
+     * Determines if the operation should continue or should wait.  If it
+     * should continue, this method will continue the operation.
+     *
+     * @param sendEmpty if <code>true</code> then this will continue the
+     * operation even if no headers will be sent; if <code>false</code> then
+     * this method will only continue the operation if there are headers to
+     * send
+     * @param isStream  if<code>true</code> the stream is input stream or
+     * is outputstream
+     * @return <code>true</code> if the operation was completed;
+     * <code>false</code> if no operation took place
+     */
+    public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+            throws IOException {
+        if (!isGet) {
+            if (!finalBitSet) {
+                if (sendEmpty) {
+                    sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+                    return true;
+                } else {
+                    if ((responseSize > 3) || (privateOutput.size() > 0)) {
+                        sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            } else {
+                return false;
+            }
+        } else {
+            sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+            return true;
+        }
+    }
+
+    /**
+     * Sends a reply to the client.  If the reply is a OBEX_HTTP_CONTINUE, it
+     * will wait for a response from the client before ending.
+     *
+     * @param type the response code to send back to the client
+     *
+     * @return <code>true</code> if the final bit was not set on the reply;
+     * <code>false</code> if no reply was received because the operation ended,
+     * an abort was received, or the final bit was set in the reply
+     *
+     * @exception IOException if an IO error occurs
+     */
+    protected synchronized boolean sendReply(int type) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int bytesReceived;
+
+        long id = listener.getConnectionID();
+        if (id == -1) {
+            replyHeaders.connectionID = null;
+        } else {
+            replyHeaders.connectionID = OBEXHelper.convertToByteArray(id);
+        }
+
+        byte[] headerArray = OBEXHelper.createHeader(replyHeaders, true);
+        int bodyLength = -1;
+        int orginalBodyLength = -1;
+
+        if (privateOutput != null) {
+            bodyLength = privateOutput.size();
+            orginalBodyLength = bodyLength;
+        }
+
+        if ((BASE_PACKET_LENGTH + headerArray.length) > maxPacketLength) {
+
+            int end = 0;
+            int start = 0;
+
+            while (end != headerArray.length) {
+                end = OBEXHelper.findHeaderEnd(headerArray, start, maxPacketLength
+                        - BASE_PACKET_LENGTH);
+                if (end == -1) {
+
+                    isClosed = true;
+
+                    if (privateInput != null) {
+                        privateInput.close();
+                    }
+
+                    if (privateOutput != null) {
+                        privateOutput.close();
+                    }
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+                    throw new IOException("OBEX Packet exceeds max packet size");
+                }
+                byte[] sendHeader = new byte[end - start];
+                System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+
+                parent.sendResponse(type, sendHeader);
+                start = end;
+            }
+
+            if (bodyLength > 0) {
+                return true;
+            } else {
+                return false;
+            }
+
+        } else {
+            out.write(headerArray);
+        }
+
+        /*
+         * Determine if there is space to add a body reply.  First, we need to
+         * verify that the client is finished sending the request.  Next, there
+         * needs to be enough space to send the headers already defined along
+         * with the reply header (3 bytes) and the body header identifier
+         * (3 bytes).
+         */
+
+        /*        if ((finalBitSet) &&
+                    ((bodyLength + 6 + headerArray.length) > maxPacketLength)) {
+
+                    exceptionString = "Header larger then can be sent in a packet";
+                    isClosed = true;
+                    privateInput.close();
+                    if (privateOutput != null) {
+                        privateOutput.close();
+                    }
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR,
+                        null);
+                    throw new IOException("OBEX Packet exceeds max packet size");
+                }
+        */
+         
+        if ((finalBitSet) || (headerArray.length < (maxPacketLength - 20))) {
+            if (bodyLength > 0) {
+                /*
+                 * Determine if I can send the whole body or just part of
+                 * the body.  Remember that there is the 3 bytes for the
+                 * response message and 3 bytes for the header ID and length
+                 */
+                if (bodyLength > (maxPacketLength - headerArray.length - 6)) {
+                    bodyLength = maxPacketLength - headerArray.length - 6;
+                }
+
+                byte[] body = privateOutput.readBytes(bodyLength);
+
+                /*
+                 * Since this is a put request if the final bit is set or
+                 * the output stream is closed we need to send the 0x49
+                 * (End of Body) otherwise, we need to send 0x48 (Body)
+                 */
+                if ((finalBitSet) || (privateOutput.isClosed())) {
+                    out.write(0x49);
+                } else {
+                    out.write(0x48);
+                }
+
+                bodyLength += 3;
+                out.write((byte)(bodyLength >> 8));
+                out.write((byte)bodyLength);
+                out.write(body);
+            }
+        }
+
+        if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
+            out.write(0x49);
+            orginalBodyLength = 3;
+            out.write((byte)(orginalBodyLength >> 8));
+            out.write((byte)orginalBodyLength);
+
+        }
+
+        responseSize = 3;
+        parent.sendResponse(type, out.toByteArray());
+
+        if (type == OBEXConstants.OBEX_HTTP_CONTINUE) {
+            int headerID = socketInput.read();
+            int length = socketInput.read();
+            length = (length << 8) + socketInput.read();
+            if ((headerID != 0x02) && (headerID != 0x82) && (headerID != 0x03)
+                    && (headerID != 0x83)) {
+
+                if (length > 3) {
+                    byte[] temp = new byte[length];
+                    bytesReceived = socketInput.read(temp);
+
+                    while (bytesReceived != length) {
+                        bytesReceived += socketInput.read(temp, bytesReceived, length
+                                - bytesReceived);
+                    }
+                }
+
+                /*
+                 * Determine if an ABORT was sent as the reply
+                 */
+                if (headerID == 0xFF) {
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
+                    isClosed = true;
+                    isAborted = true;
+                    exceptionString = "Abort Received";
+                    throw new IOException("Abort Received");
+                } else {
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
+                    isClosed = true;
+                    exceptionString = "Bad Request Received";
+                    throw new IOException("Bad Request Received");
+                }
+            } else {
+
+                if ((headerID == 0x82) || (headerID == 0x83)) {
+                    finalBitSet = true;
+                }
+
+                /*
+                 * Determine if the packet length is larger then this device can receive
+                 */
+                if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+                    parent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+                    throw new IOException("Packet received was too large");
+                }
+
+                /*
+                 * Determine if any headers were sent in the initial request
+                 */
+                if (length > 3) {
+                    byte[] data = new byte[length - 3];
+                    bytesReceived = socketInput.read(data);
+
+                    while (bytesReceived != data.length) {
+                        bytesReceived += socketInput.read(data, bytesReceived, data.length
+                                - bytesReceived);
+                    }
+                    byte[] body = OBEXHelper.updateHeaderSet(requestHeaders, data);
+                    if (body != null) {
+                        isHasBody = true;
+                    }
+                    if (requestHeaders.connectionID != null) {
+                        listener.setConnectionID(OBEXHelper
+                                .convertToLong(requestHeaders.connectionID));
+                    } else {
+                        listener.setConnectionID(1);
+                    }
+
+                    if (requestHeaders.authResp != null) {
+                        if (!parent.handleAuthResp(requestHeaders.authResp)) {
+                            exceptionString = "Authentication Failed";
+                            parent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+                            isClosed = true;
+                            requestHeaders.authResp = null;
+                            return false;
+                        }
+                        requestHeaders.authResp = null;
+                    }
+
+                    if (requestHeaders.authChall != null) {
+                        parent.handleAuthChall(requestHeaders);
+                        // send the auhtResp to the client
+                        replyHeaders.authResp = new byte[requestHeaders.authResp.length];
+                        System.arraycopy(requestHeaders.authResp, 0, replyHeaders.authResp, 0,
+                                replyHeaders.authResp.length);
+                        requestHeaders.authResp = null;
+                        requestHeaders.authChall = null;
+                    }
+
+                    if (body != null) {
+                        if (body[0] == 0x49) {
+                            endOfBody = true;
+                        }
+
+                        /*byte [] body_tmp = new byte[body.length-1];
+                        System.arraycopy(body,1,body_tmp,0,body.length-1);
+                        privateInput.writeBytes(body_tmp, body.length-1);*/
+                        privateInput.writeBytes(body, 1);
+
+                    }
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Sends an ABORT message to the server.  By calling this method, the
+     * corresponding input and output streams will be closed along with this
+     * object.
+     *
+     * @exception IOException if the transaction has already ended or if an
+     * OBEX server called this method
+     */
+    public void abort() throws IOException {
+        throw new IOException("Called from a server");
+    }
+
+    /**
+     * Returns the headers that have been received during the operation.
+     * Modifying the object returned has no effect on the headers that are
+     * sent or retrieved.
+     *
+     * @return the headers received during this <code>Operation</code>
+     *
+     * @exception IOException if this <code>Operation</code> has been closed
+     */
+    public HeaderSet getReceivedHeaders() throws IOException {
+        ensureOpen();
+        return requestHeaders;
+    }
+
+    /**
+     * Specifies the headers that should be sent in the next OBEX message that
+     * is sent.
+     *
+     * @param headers the headers to send in the next message
+     *
+     * @exception IOException  if this <code>Operation</code> has been closed
+     * or the transaction has ended and no further messages will be exchanged
+     *
+     * @exception IllegalArgumentException if <code>headers</code> was not created
+     * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+     */
+    public void sendHeaders(HeaderSet headers) throws IOException {
+        ensureOpen();
+
+        if (headers == null) {
+            throw new NullPointerException("Headers may not be null");
+        }
+
+        int[] headerList = headers.getHeaderList();
+        if (headerList != null) {
+            for (int i = 0; i < headerList.length; i++) {
+                replyHeaders.setHeader(headerList[i], headers.getHeader(headerList[i]));
+            }
+
+        }
+    }
+
+    /**
+     * Retrieves the response code retrieved from the server.  Response codes
+     * are defined in the <code>ResponseCodes</code> interface.
+     *
+     * @return the response code retrieved from the server
+     *
+     * @exception IOException if an error occurred in the transport layer during
+     * the transaction; if this method is called on a <code>HeaderSet</code>
+     * object created by calling <code>createHeaderSet</code> in a
+     * <code>ClientSession</code> object; if this is called from a server
+     */
+    public int getResponseCode() throws IOException {
+        throw new IOException("Called from a server");
+    }
+
+    /**
+     * Always returns <code>null</code>
+     *
+     * @return <code>null</code>
+     */
+    public String getEncoding() {
+        return null;
+    }
+
+    /**
+     * Returns the type of content that the resource connected to is providing.
+     * E.g. if the connection is via HTTP, then the value of the content-type
+     * header field is returned.
+     *
+     * @return the content type of the resource that the URL references, or
+     * <code>null</code> if not known
+     */
+    public String getType() {
+        try {
+            return (String)requestHeaders.getHeader(HeaderSet.TYPE);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns the length of the content which is being provided. E.g. if the
+     * connection is via HTTP, then the value of the content-length header
+     * field is returned.
+     *
+     * @return the content length of the resource that this connection's URL
+     * references, or -1 if the content length is not known
+     */
+    public long getLength() {
+        try {
+            Long temp = (Long)requestHeaders.getHeader(HeaderSet.LENGTH);
+
+            if (temp == null) {
+                return -1;
+            } else {
+                return temp.longValue();
+            }
+        } catch (IOException e) {
+            return -1;
+        }
+    }
+
+    public int getMaxPacketSize() {
+        return maxPacketLength - 6;
+    }
+
+    /**
+     * Open and return an input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public InputStream openInputStream() throws IOException {
+        ensureOpen();
+        return privateInput;
+    }
+
+    /**
+     * Open and return a data input stream for a connection.
+     *
+     * @return an input stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    /**
+     * Open and return an output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public OutputStream openOutputStream() throws IOException {
+        ensureOpen();
+
+        if (outputStreamOpened)
+            throw new IOException("no more input streams available, stream already opened");
+
+        if (!requestFinished)
+            throw new IOException("no  output streams available ,request not finished");
+
+        if (privateOutput == null) {
+            privateOutput = new PrivateOutputStream(this, maxPacketLength - 6);
+        }
+        outputStreamOpened = true;
+        return privateOutput;
+    }
+
+    /**
+     * Open and return a data output stream for a connection.
+     *
+     * @return an output stream
+     *
+     * @exception IOException if an I/O error occurs
+     */
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    /**
+     * Closes the connection and ends the transaction
+     *
+     * @exception IOException if the operation has already ended or is closed
+     */
+    public void close() throws IOException {
+        ensureOpen();
+        isClosed = true;
+    }
+
+    /**
+     * Verifies that the connection is open and no exceptions should be thrown.
+     *
+     * @exception IOException if an exception needs to be thrown
+     */
+    public void ensureOpen() throws IOException {
+        if (exceptionString != null) {
+            throw new IOException(exceptionString);
+        }
+        if (isClosed) {
+            throw new IOException("Operation has already ended");
+        }
+    }
+
+    /**
+     * Verifies that additional information may be sent.  In other words, the
+     * operation is not done.
+     * <P>
+     * Included to implement the BaseStream interface only.  It does not do
+     * anything on the server side since the operation of the Operation object
+     * is not done until after the handler returns from its method.
+     *
+     * @exception IOException if the operation is completed
+     */
+    public void ensureNotDone() throws IOException {
+    }
+
+    /**
+     * Called when the output or input stream is closed.  It does not do
+     * anything on the server side since the operation of the Operation object
+     * is not done until after the handler returns from its method.
+     *
+     * @param inStream <code>true</code> if the input stream is closed;
+     * <code>false</code> if the output stream is closed
+     *
+     * @exception IOException if an IO error occurs
+     */
+    public void streamClosed(boolean inStream) throws IOException {
+
+    }
+}
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
new file mode 100644
index 0000000..4e7f5b4
--- /dev/null
+++ b/obex/javax/obex/ServerRequestHandler.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * The <code>ServerRequestHandler</code> class defines an event
+ * listener that will respond to OBEX requests made to the server.
+ * <P>
+ * The <code>onConnect()</code>, <code>onSetPath()</code>, <code>onDelete()</code>,
+ * <code>onGet()</code>,
+ * and <code>onPut()</code> methods may return any response code defined
+ * in the <code>ResponseCodes</code> class except for
+ * <code>OBEX_HTTP_CONTINUE</code>.  If <code>OBEX_HTTP_CONTINUE</code> or
+ * a value not defined in the <code>ResponseCodes</code> class is returned,
+ * the server implementation will send an <code>OBEX_HTTP_INTERNAL_ERROR</code>
+ * response to the client.
+ * <P>
+ * <STRONG>Connection ID and Target Headers</STRONG>
+ * <P>
+ * According to the IrOBEX specification, a packet may not contain a Connection
+ * ID and Target header.  Since the Connection ID header is managed by the
+ * implementation, it will not send a Connection ID header, if a Connection ID
+ * was specified, in a packet that has a Target header.  In other words, if an
+ * application adds a Target header to a <code>HeaderSet</code> object used
+ * in an OBEX operation and a Connection ID was specified, no Connection ID
+ * will be sent in the packet containing the Target header.
+ * <P>
+ * <STRONG>CREATE-EMPTY Requests</STRONG>
+ * <P>
+ * A CREATE-EMPTY request allows clients to create empty objects on the server.
+ * When a CREATE-EMPTY request is received, the <code>onPut()</code> method
+ * will be called by the implementation.  To differentiate between a normal
+ * PUT request and a CREATE-EMPTY request, an application must open the
+ * <code>InputStream</code> from the <code>Operation</code> object passed
+ * to the <code>onPut()</code> method.  For a PUT request, the application
+ * will be able to read Body data from this <code>InputStream</code>.  For
+ * a CREATE-EMPTY request, there will be no Body data to read.  Therefore,
+ * a call to <code>InputStream.read()</code> will return -1.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public class ServerRequestHandler {
+
+    private long connectionID;
+
+    /**
+      * Creates a <code>ServerRequestHandler</code>.
+      */
+    protected ServerRequestHandler() {
+        /*
+         * A connection ID of -1 implies there is no conenction ID
+         */
+        connectionID = -1;
+    }
+
+    /**
+     * Creates a <code>HeaderSet</code> object that may be used in put and get
+     * operations.
+     *
+     * @return the <code>HeaderSet</code> object to use in put and get operations
+     */
+    public final HeaderSet createHeaderSet() {
+        return new HeaderSet();
+    }
+
+    /**
+     * Sets the connection ID header to include in the reply packets.
+     *
+     * @param id the connection ID to use; -1 if no connection ID should be
+     * sent
+     *
+     * @exception IllegalArgumentException if <code>id</code> is not in the
+     * range -1 to 2<sup>32</sup>-1
+     */
+    public void setConnectionID(long id) {
+        if ((id < -1) || (id > 0xFFFFFFFFL)) {
+            throw new IllegalArgumentException("Illegal Connection ID");
+        }
+        connectionID = id;
+    }
+
+    /**
+     * Retrieves the connection ID that is being used in the present connection.
+     * This method will return -1 if no connection ID is being used.
+     *
+     * @return the connection id being used or -1 if no connection ID is being
+     * used
+     */
+    public long getConnectionID() {
+        return connectionID;
+    }
+
+    /**
+     * Called when a CONNECT request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onConnect()</code> will always return an
+     * <code>OBEX_HTTP_OK</code> response code.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onConnect(HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    /**
+     * Called when a DISCONNECT request is received.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     */
+    public void onDisconnect(HeaderSet request, HeaderSet reply) {
+    }
+
+    /**
+     * Called when a SETPATH request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onSetPath()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     *
+     * @param backup <code>true</code> if the client requests that the server
+     * back up one directory before changing to the path described by
+     * <code>name</code>; <code>false</code> to apply the request to the present
+     * path
+     *
+     * @param create <code>true</code> if the path should be created if it does
+     * not already exist; <code>false</code> if the path should not be created
+     * if it does not exist and an error code should be returned
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
+
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when a DELETE request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onDelete()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * The headers received in the request can be retrieved from the
+     * <code>request</code> argument.  The headers that should be sent
+     * in the reply must be specified in the <code>reply</code> argument.
+     *
+     * @param request contains the headers sent by the client;
+     * <code>request</code> will never be <code>null</code>
+     *
+     * @param reply the headers that should be sent in the reply;
+     * <code>reply</code> will never be <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onDelete(HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when a PUT request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onPut()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * If an ABORT request is received during the processing of a PUT request,
+     * <code>op</code> will be closed by the implementation.
+     *
+     * @param op contains the headers sent by the client and allows new
+     * headers to be sent in the reply; <code>op</code> will never be
+     * <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onPut(Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when a GET request is received.
+     * <P>
+     * If this method is not implemented by the class that extends this
+     * class, <code>onGet()</code> will always return an
+     * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+     * <P>
+     * If an ABORT request is received during the processing of a GET request,
+     * <code>op</code> will be closed by the implementation.
+     *
+     * @param op contains the headers sent by the client and allows new
+     * headers to be sent in the reply; <code>op</code> will never be
+     * <code>null</code>
+     *
+     * @return a response code defined in <code>ResponseCodes</code> that will be
+     * returned to the client; if an invalid response code is provided, the
+     * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+     */
+    public int onGet(Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    /**
+     * Called when this object attempts to authenticate a client and the
+     * authentication request fails because the response digest in the
+     * authentication response header was wrong.
+     * <P>
+     * If this method is not implemented by the class that extends this class,
+     * this method will do nothing.
+     *
+     * @param userName the user name returned in the authentication response;
+     * <code>null</code> if no user name was provided in the response
+     */
+    public void onAuthenticationFailure(byte[] userName) {
+    }
+
+    /**Called by ServerSession to update the status of current transaction */
+    public void updateStatus(String message) {
+    }
+
+    public void onClose() {
+    }
+}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
new file mode 100644
index 0000000..31c82cc
--- /dev/null
+++ b/obex/javax/obex/ServerSession.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.*;
+
+/**
+ * This class in an implementation of the ServerSession interface.
+ * 
+ * @version 0.3 November 28, 2008
+ */
+public class ServerSession implements Runnable, ObexSession {
+
+    private ObexTransport client;
+
+    // private Socket client ;
+    private InputStream input;
+
+    private OutputStream output;
+
+    private ServerRequestHandler listener;
+
+    private Thread processThread;
+
+    private int maxPacketLength;
+
+    private Authenticator authenticator;
+
+    byte[] challengeDigest;
+
+    public boolean isClosed;
+
+    private static final String TAG = "ServerSession";
+
+    /**
+     * Creates new ServerSession.
+     *
+     * @param conn
+     *            the connection to the client
+     *
+     * @param handler
+     *            the event listener that will process requests
+     *
+     * @param auth
+     *            the authenticator to use with this connection
+     *
+     * @exception IOException
+     *                if an error occurred while opening the input and output
+     *                streams
+     */
+    public ServerSession(ObexTransport conn, ServerRequestHandler handler, Authenticator auth)
+            throws IOException {
+        authenticator = auth;
+        client = conn;
+        input = client.openInputStream();
+        output = client.openOutputStream();
+        listener = handler;
+        maxPacketLength = 256;
+
+        isClosed = false;
+        processThread = new Thread(this);
+        processThread.start();
+    }
+
+    /* removed as they're provided to the API user. Not used internally. */
+    /*
+     public boolean isCreatedServer() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).isServerCreated();
+        else
+            return false;
+    }
+
+    public boolean isClosed() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).isClosed();
+        else
+            return false;
+    }
+
+    public int getConnectionHandle() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).getConnectionHandle();
+        else
+            return -1;
+    }
+
+    public RemoteDevice getRemoteDevice() {
+        if (client instanceof BTConnection)
+            return ((BTConnection)client).getRemoteDevice();
+        else
+            return null;
+    }*/
+
+    /**
+     * Processes requests made to the server and forwards them to the
+     * appropriate event listener.
+     */
+    public void run() {
+        try {
+
+            boolean done = false;
+            while (!done && !isClosed) {
+                int requestType = input.read();
+                switch (requestType) {
+                    case 0x80:
+                        handleConnectRequest();
+                        break;
+
+                    case 0x81:
+                        handleDisconnectRequest();
+                        done = true;
+                        break;
+
+                    case 0x03:
+                    case 0x83:
+                        handleGetRequest(requestType);
+                        break;
+
+                    case 0x02:
+                    case 0x82:
+                        handlePutRequest(requestType);
+                        break;
+
+                    case 0x85:
+                        handleSetPathRequest();
+                        break;
+
+                    case -1:
+                        done = true;
+                        break;
+
+                    default:
+
+                        /*
+                         * Received a request type that is not recognized so I am
+                         * just going to read the packet and send a not implemented
+                         * to the client
+                         */
+                        int length = input.read();
+                        length = (length << 8) + input.read();
+                        for (int i = 3; i < length; i++) {
+                            input.read();
+                        }
+                        sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
+
+                        // done = true;
+                }
+            }
+
+        } catch (NullPointerException e) {
+        } catch (Exception e) {
+        }
+        close();
+    }
+
+    /**
+     * Handles a PUT request from a client. This method will provide a
+     * <code>ServerOperation</code> object to the request handler. The
+     * <code>ServerOperation</code> object will handle the rest of the request.
+     * It will also send replies and receive requests until the final reply
+     * should be sent. When the final reply should be sent, this method will get
+     * the response code to use and send the reply. The
+     * <code>ServerOperation</code> object will always reply with a
+     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+     * needed.
+     *
+     * @param type
+     *            the type of request received; either 0x02 or 0x82
+     *
+     * @exception IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handlePutRequest(int type) throws IOException {
+        ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener);
+        try {
+            int response = -1;
+
+            if ((client.finalBitSet) && !client.isValidBody()) {
+                response = validateResponseCode(listener.onDelete(client.requestHeaders,
+                        client.replyHeaders));
+            } else {
+                response = validateResponseCode(listener.onPut(client));
+            }
+            if (response != ResponseCodes.OBEX_HTTP_OK) {
+                client.sendReply(response);
+            } else if (!client.isAborted) {
+                // wait for the final bit
+                while (!client.finalBitSet) {
+                    client.sendReply(OBEXConstants.OBEX_HTTP_CONTINUE);
+                }
+                client.sendReply(response);
+            }
+        } catch (Exception e) {
+            sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+        }
+    }
+
+    /**
+     * Handles a GET request from a client. This method will provide a
+     * <code>ServerOperation</code> object to the request handler. The
+     * <code>ServerOperation</code> object will handle the rest of the request.
+     * It will also send replies and receive requests until the final reply
+     * should be sent. When the final reply should be sent, this method will get
+     * the response code to use and send the reply. The
+     * <code>ServerOperation</code> object will always reply with a
+     * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+     * needed.
+     *
+     * @param type
+     *            the type of request received; either 0x03 or 0x83
+     *
+     * @exception IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleGetRequest(int type) throws IOException {
+        ServerOperation client = new ServerOperation(this, input, type, maxPacketLength, listener);
+        try {
+            int response = validateResponseCode(listener.onGet(client));
+
+            if (!client.isAborted) {
+                client.sendReply(response);
+            }
+        } catch (Exception e) {
+            sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+        }
+    }
+
+    /**
+     * Send standard response.
+     *
+     * @param code
+     *            the response code to send
+     *
+     * @param header
+     *            the headers to include in the response
+     *
+     * @exception IOException
+     *                if an IO error occurs
+     */
+    protected void sendResponse(int code, byte[] header) throws IOException {
+        int totalLength = 3;
+        byte[] data = null;
+
+        if (header != null) {
+            totalLength += header.length;
+            data = new byte[totalLength];
+            data[0] = (byte)code;
+            data[1] = (byte)(totalLength >> 8);
+            data[2] = (byte)totalLength;
+            System.arraycopy(header, 0, data, 3, header.length);
+        } else {
+            data = new byte[totalLength];
+            data[0] = (byte)code;
+            data[1] = (byte)0x00;
+            data[2] = (byte)totalLength;
+        }
+        output.write(data);
+        output.flush();
+    }
+
+    /**
+     * Handles a SETPATH request from a client. This method will read the rest
+     * of the request from the client. Assuming the request is valid, it will
+     * create a <code>HeaderSet</code> object to pass to the
+     * <code>ServerRequestHandler</code> object. After the handler processes the
+     * request, this method will create a reply message to send to the server
+     * with the response code provided.
+     *
+     * @exception IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleSetPathRequest() throws IOException {
+        int length;
+        int flags;
+        int constants;
+        int totalLength = 3;
+        byte[] head = null;
+        int code = -1;
+        int bytesReceived;
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        length = input.read();
+        length = (length << 8) + input.read();
+        flags = input.read();
+        constants = input.read();
+
+        if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+            totalLength = 3;
+        } else {
+            if (length > 5) {
+                byte[] headers = new byte[length - 5];
+                bytesReceived = input.read(headers);
+
+                while (bytesReceived != headers.length) {
+                    bytesReceived += input.read(headers, bytesReceived, headers.length
+                            - bytesReceived);
+                }
+
+                OBEXHelper.updateHeaderSet(request, headers);
+
+                if (request.connectionID != null) {
+                    listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID));
+                } else {
+                    listener.setConnectionID(-1);
+                }
+                // the Auth chan is initiated by the server.
+                // client sent back the authResp .
+                if (request.authResp != null) {
+                    if (!handleAuthResp(request.authResp)) {
+                        code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+                        listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01,
+                                request.authResp));
+                    }
+                    request.authResp = null;
+                }
+            }
+
+            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+                // the Auth chan is initiated by the client
+                // the server will send back the authResp to the client
+                if (request.authChall != null) {
+                    handleAuthChall(request);
+                    reply.authResp = new byte[request.authResp.length];
+                    System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length);
+                    request.authChall = null;
+                    request.authResp = null;
+                }
+                boolean backup = false;
+                boolean create = true;
+                if (!((flags & 1) == 0)) {
+                    backup = true;
+                }
+                if ((flags & 2) == 0) {
+                    create = false;
+                }
+
+                try {
+                    code = listener.onSetPath(request, reply, backup, create);
+                } catch (Exception e) {
+                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+                    return;
+                }
+
+                code = validateResponseCode(code);
+
+                if (reply.nonce != null) {
+                    challengeDigest = new byte[16];
+                    System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16);
+                } else {
+                    challengeDigest = null;
+                }
+
+                long id = listener.getConnectionID();
+                if (id == -1) {
+                    reply.connectionID = null;
+                } else {
+                    reply.connectionID = OBEXHelper.convertToByteArray(id);
+                }
+
+                head = OBEXHelper.createHeader(reply, false);
+                totalLength += head.length;
+
+                if (totalLength > maxPacketLength) {
+                    totalLength = 3;
+                    head = null;
+                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+            }
+        }
+
+        // Compute Length of OBEX SETPATH packet
+        byte[] replyData = new byte[totalLength];
+        replyData[0] = (byte)code;
+        replyData[1] = (byte)(totalLength >> 8);
+        replyData[2] = (byte)totalLength;
+        if (head != null) {
+            System.arraycopy(head, 0, replyData, 3, head.length);
+        }
+        /*
+         * Write the OBEX SETPATH packet to the server. Byte 0: response code
+         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+         */
+        output.write(replyData);
+        output.flush();
+    }
+
+    /**
+     * Handles a disconnect request from a client. This method will read the
+     * rest of the request from the client. Assuming the request is valid, it
+     * will create a <code>HeaderSet</code> object to pass to the
+     * <code>ServerRequestHandler</code> object. After the handler processes the
+     * request, this method will create a reply message to send to the server.
+     *
+     * @exception IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleDisconnectRequest() throws IOException {
+        int length;
+        int code = ResponseCodes.OBEX_HTTP_OK;
+        int totalLength = 3;
+        byte[] head = null;
+        int bytesReceived;
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        length = input.read();
+        length = (length << 8) + input.read();
+
+        if (length > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+            totalLength = 3;
+        } else {
+            if (length > 3) {
+                byte[] headers = new byte[length - 3];
+                bytesReceived = input.read(headers);
+
+                while (bytesReceived != headers.length) {
+                    bytesReceived += input.read(headers, bytesReceived, headers.length
+                            - bytesReceived);
+                }
+
+                OBEXHelper.updateHeaderSet(request, headers);
+            }
+
+            if (request.connectionID != null) {
+                listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID));
+            } else {
+                listener.setConnectionID(1);
+            }
+
+            if (request.authResp != null) {
+                if (!handleAuthResp(request.authResp)) {
+                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+                    listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01,
+                            request.authResp));
+                }
+                request.authResp = null;
+            }
+
+            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+
+                if (request.authChall != null) {
+                    handleAuthChall(request);
+                    request.authChall = null;
+                }
+
+                try {
+                    listener.onDisconnect(request, reply);
+                } catch (Exception e) {
+                    sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+                    return;
+                }
+
+                /*
+                 * Since a client will never response to an authentication
+                 * challenge on a DISCONNECT, there is no reason to keep track
+                 * of the challenge.
+                 *
+                 * if (reply.nonce != null) { challengeDigest = new byte[16];
+                 * System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16); }
+                 * else { challengeDigest = null; }
+                 */
+
+                long id = listener.getConnectionID();
+                if (id == -1) {
+                    reply.connectionID = null;
+                } else {
+                    reply.connectionID = OBEXHelper.convertToByteArray(id);
+                }
+
+                head = OBEXHelper.createHeader(reply, false);
+                totalLength += head.length;
+
+                if (totalLength > maxPacketLength) {
+                    totalLength = 3;
+                    head = null;
+                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+            }
+        }
+
+        // Compute Length of OBEX CONNECT packet
+        byte[] replyData;
+        if (head != null) {
+            replyData = new byte[3 + head.length];
+        } else {
+            replyData = new byte[3];
+        }
+        replyData[0] = (byte)code;
+        replyData[1] = (byte)(totalLength >> 8);
+        replyData[2] = (byte)totalLength;
+        if (head != null) {
+            System.arraycopy(head, 0, replyData, 3, head.length);
+        }
+        /*
+         * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
+         * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+         */
+        output.write(replyData);
+        output.flush();
+    }
+
+    /**
+     * Handles a connect request from a client. This method will read the rest
+     * of the request from the client. Assuming the request is valid, it will
+     * create a <code>HeaderSet</code> object to pass to the
+     * <code>ServerRequestHandler</code> object. After the handler processes the
+     * request, this method will create a reply message to send to the server
+     * with the response code provided.
+     *
+     * @exception IOException
+     *                if an error occurred at the transport layer
+     */
+    private void handleConnectRequest() throws IOException {
+        int packetLength;
+        int version;
+        int flags;
+        int totalLength = 7;
+        byte[] head = null;
+        int code = -1;
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+        int bytesReceived;
+
+        /*
+         * Read in the length of the OBEX packet, OBEX version, flags, and max
+         * packet length
+         */
+        packetLength = input.read();
+        packetLength = (packetLength << 8) + input.read();
+        version = input.read();
+        flags = input.read();
+        maxPacketLength = input.read();
+        maxPacketLength = (maxPacketLength << 8) + input.read();
+
+        // should we check it?
+        if (maxPacketLength > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            maxPacketLength = OBEXConstants.MAX_PACKET_SIZE_INT;
+        }
+
+        if (packetLength > OBEXConstants.MAX_PACKET_SIZE_INT) {
+            code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+            totalLength = 7;
+        } else {
+            if (packetLength > 7) {
+                byte[] headers = new byte[packetLength - 7];
+                bytesReceived = input.read(headers);
+
+                while (bytesReceived != headers.length) {
+                    bytesReceived += input.read(headers, bytesReceived, headers.length
+                            - bytesReceived);
+                }
+
+                OBEXHelper.updateHeaderSet(request, headers);
+            }
+
+            if (request.connectionID != null) {
+                listener.setConnectionID(OBEXHelper.convertToLong(request.connectionID));
+            } else {
+                listener.setConnectionID(1);
+            }
+
+            if (request.authResp != null) {
+                if (!handleAuthResp(request.authResp)) {
+                    code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+                    listener.onAuthenticationFailure(OBEXHelper.getTagValue((byte)0x01,
+                            request.authResp));
+                }
+                request.authResp = null;
+            }
+
+            if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+                if (request.authChall != null) {
+                    handleAuthChall(request);
+                    reply.authResp = new byte[request.authResp.length];
+                    System.arraycopy(request.authResp, 0, reply.authResp, 0, reply.authResp.length);
+                    request.authChall = null;
+                    request.authResp = null;
+                }
+
+                try {
+                    code = listener.onConnect(request, reply);
+                    code = validateResponseCode(code);
+
+                    if (reply.nonce != null) {
+                        challengeDigest = new byte[16];
+                        System.arraycopy(reply.nonce, 0, challengeDigest, 0, 16);
+                    } else {
+                        challengeDigest = null;
+                    }
+                    long id = listener.getConnectionID();
+                    if (id == -1) {
+                        reply.connectionID = null;
+                    } else {
+                        reply.connectionID = OBEXHelper.convertToByteArray(id);
+                    }
+
+                    head = OBEXHelper.createHeader(reply, false);
+                    totalLength += head.length;
+
+                    if (totalLength > maxPacketLength) {
+                        totalLength = 7;
+                        head = null;
+                        code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    totalLength = 7;
+                    head = null;
+                    code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+
+            }
+        }
+
+        // Compute Length of OBEX CONNECT packet
+        byte[] length = OBEXHelper.convertToByteArray(totalLength);
+
+        /*
+         * Write the OBEX CONNECT packet to the server. Byte 0: response code
+         * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
+         * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
+         * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
+         */
+        byte[] sendData = new byte[totalLength];
+        sendData[0] = (byte)code;
+        sendData[1] = length[2];
+        sendData[2] = length[3];
+        sendData[3] = (byte)0x10;
+        sendData[4] = (byte)0x00;
+        sendData[5] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT >> 8);
+        sendData[6] = (byte)(OBEXConstants.MAX_PACKET_SIZE_INT & 0xFF);
+
+        if (head != null) {
+            System.arraycopy(head, 0, sendData, 7, head.length);
+        }
+
+        output.write(sendData);
+        output.flush();
+    }
+
+    /**
+     * Closes the server session - in detail close I/O streams and the
+     * underlying transport layer. Internal flag is also set so that later
+     * attempt to read/write will throw an exception.
+     */
+    public synchronized void close() {
+        if (listener != null) {
+            listener.onClose();
+        }
+        try {
+            input.close();
+            output.close();
+            client.close();
+            isClosed = true;
+        } catch (Exception e) {
+        }
+        client = null;
+        input = null;
+        output = null;
+        listener = null;
+    }
+
+    /**
+     * Verifies that the response code is valid. If it is not valid, it will
+     * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
+     *
+     * @param code
+     *            the response code to check
+     *
+     * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
+     *         if <code>code</code> is not valid
+     */
+    private int validateResponseCode(int code) {
+
+        if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
+                && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
+                && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
+                && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
+            return code;
+        }
+        if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
+                && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
+            return code;
+        }
+        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+    }
+
+    /**
+     * Called when the server received an authentication challenge header. This
+     * will cause the authenticator to handle the authentication challenge.
+     *
+     * @param header
+     *            the header with the authentication challenge
+     *
+     * @return <code>true</code> if the last request should be resent;
+     *         <code>false</code> if the last request should not be resent
+     */
+    protected boolean handleAuthChall(HeaderSet header) {
+        if (authenticator == null) {
+            return false;
+        }
+
+        /*
+         * An authentication challenge is made up of one required and two
+         * optional tag length value triplets. The tag 0x00 is required to be in
+         * the authentication challenge and it represents the challenge digest
+         * that was received. The tag 0x01 is the options tag. This tag tracks
+         * if user ID is required and if full access will be granted. The tag
+         * 0x02 is the realm, which provides a description of which user name
+         * and password to use.
+         */
+        byte[] challenge = OBEXHelper.getTagValue((byte)0x00, header.authChall);
+        byte[] option = OBEXHelper.getTagValue((byte)0x01, header.authChall);
+        byte[] description = OBEXHelper.getTagValue((byte)0x02, header.authChall);
+
+        String realm = "";
+        if (description != null) {
+            byte[] realmString = new byte[description.length - 1];
+            System.arraycopy(description, 1, realmString, 0, realmString.length);
+
+            switch (description[0] & 0xFF) {
+
+                case 0x00:
+                    // ASCII encoding
+                    // Fall through
+                case 0x01:
+                    // ISO-8859-1 encoding
+                    try {
+                        realm = new String(realmString, "ISO8859_1");
+                    } catch (Exception e) {
+                        throw new RuntimeException("Unsupported Encoding Scheme");
+                    }
+                    break;
+
+                case 0xFF:
+                    // UNICODE Encoding
+                    realm = OBEXHelper.convertToUnicode(realmString, false);
+                    break;
+
+                case 0x02:
+                    // ISO-8859-2 encoding
+                    // Fall through
+                case 0x03:
+                    // ISO-8859-3 encoding
+                    // Fall through
+                case 0x04:
+                    // ISO-8859-4 encoding
+                    // Fall through
+                case 0x05:
+                    // ISO-8859-5 encoding
+                    // Fall through
+                case 0x06:
+                    // ISO-8859-6 encoding
+                    // Fall through
+                case 0x07:
+                    // ISO-8859-7 encoding
+                    // Fall through
+                case 0x08:
+                    // ISO-8859-8 encoding
+                    // Fall through
+                case 0x09:
+                    // ISO-8859-9 encoding
+                    // Fall through
+                default:
+                    throw new RuntimeException("Unsupported Encoding Scheme");
+            }
+        }
+
+        boolean isUserIDRequired = false;
+        boolean isFullAccess = true;
+        if (option != null) {
+            if ((option[0] & 0x01) != 0) {
+                isUserIDRequired = true;
+            }
+
+            if ((option[0] & 0x02) != 0) {
+                isFullAccess = false;
+            }
+        }
+
+        PasswordAuthentication result = null;
+        header.authChall = null;
+
+        try {
+            result = authenticator.onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
+        } catch (Exception e) {
+            return false;
+        }
+
+        /*
+         * If no password is provided then we not resent the request
+         */
+        if (result == null) {
+            return false;
+        }
+
+        byte[] password = result.getPassword();
+        if (password == null) {
+            return false;
+        }
+
+        byte[] userName = result.getUserName();
+
+        /*
+         * Create the authentication response header. It includes 1 required and
+         * 2 option tag length value triples. The required triple has a tag of
+         * 0x00 and is the response digest. The first optional tag is 0x01 and
+         * represents the user ID. If no user ID is provided, then no user ID
+         * will be sent. The second optional tag is 0x02 and is the challenge
+         * that was received. This will always be sent
+         */
+        if (userName != null) {
+            header.authResp = new byte[38 + userName.length];
+            header.authResp[36] = (byte)0x01;
+            header.authResp[37] = (byte)userName.length;
+            System.arraycopy(userName, 0, header.authResp, 38, userName.length);
+        } else {
+            header.authResp = new byte[36];
+        }
+
+        // Create the secret String
+        byte[] digest = new byte[challenge.length + password.length + 1];
+        System.arraycopy(challenge, 0, digest, 0, challenge.length);
+        // Insert colon between challenge and password
+        digest[challenge.length] = (byte)0x3A;
+        System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
+
+        // Add the Response Digest
+        header.authResp[0] = (byte)0x00;
+        header.authResp[1] = (byte)0x10;
+
+        System.arraycopy(OBEXHelper.computeMD5Hash(digest), 0, header.authResp, 2, 16);
+
+        // Add the challenge
+        header.authResp[18] = (byte)0x02;
+        header.authResp[19] = (byte)0x10;
+        System.arraycopy(challenge, 0, header.authResp, 20, 16);
+
+        return true;
+    }
+
+    /**
+     * Called when the server received an authentication response header. This
+     * will cause the authenticator to handle the authentication response.
+     *
+     * @param authResp
+     *            the authentication response
+     *
+     * @return <code>true</code> if the response passed; <code>false</code> if
+     *         the response failed
+     */
+    protected boolean handleAuthResp(byte[] authResp) {
+        if (authenticator == null) {
+            return false;
+        }
+        // get the correct password from the application
+        byte[] correctPassword = authenticator.onAuthenticationResponse(OBEXHelper.getTagValue(
+                (byte)0x01, authResp));
+        if (correctPassword == null) {
+            return false;
+        }
+
+        byte[] temp = new byte[correctPassword.length + 16];
+
+        System.arraycopy(challengeDigest, 0, temp, 0, 16);
+        System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
+
+        byte[] correctResponse = OBEXHelper.computeMD5Hash(temp);
+        byte[] actualResponse = OBEXHelper.getTagValue((byte)0x00, authResp);
+
+        // compare the MD5 hash array .
+        for (int i = 0; i < 16; i++) {
+            if (correctResponse[i] != actualResponse[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/obex/javax/obex/SessionNotifier.java b/obex/javax/obex/SessionNotifier.java
new file mode 100644
index 0000000..0c9bfd5
--- /dev/null
+++ b/obex/javax/obex/SessionNotifier.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * The <code>SessionNotifier</code> interface defines a connection notifier for
+ * server-side OBEX connections.  When a <code>SessionNotifier</code> is
+ * created and calls  <code>acceptAndOpen()</code>, it will begin listening for
+ * clients to create a connection at the transport layer.  When the transport
+ * layer connection is received, the <code>acceptAndOpen()</code> method will
+ * return a  <code>javax.microedition.io.Connection</code> that is the
+ * connection to the client.  The <code>acceptAndOpen()</code> method also takes a
+ * <code>ServerRequestHandler</code> argument that will process the requests
+ * from the client that connects to the server.
+ *
+ * @version 0.3 November 28, 2008
+ */
+public interface SessionNotifier {
+
+    /**
+     * Waits for a transport layer connection to be established and specifies
+     * the handler to handle the requests from the client.  No authenticator
+     * is associated with this connection, therefore, it is implementation
+     * dependent as to how an authentication challenge and authentication
+     * response header will be received and processed.
+     * <P>
+     * <H4>Additional Note for OBEX over Bluetooth</H4>
+     * If this method is called on a <code>SessionNotifier</code> object that
+     * does not have a <code>ServiceRecord</code> in the SDDB, the
+     * <code>ServiceRecord</code> for this object will be added to the SDDB.
+     * This method requests the BCC to put the
+     * local device in connectable mode so that it will respond to
+     * connection attempts by clients.
+     * <P>
+     * The following checks are done to verify that the service record
+     * provided is valid. If any of these checks fail, then a
+     * <code>ServiceRegistrationException</code> is thrown.
+     * <UL>
+     * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory
+     * service attributes for a <code>btgoep</code> service record, must be
+     * present in the <code>ServiceRecord</code> associated with this notifier.
+     * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
+     * <LI>The <code>ServiceRecord</code> associated with this notifier must
+     *  not have changed the RFCOMM server channel number
+     * </UL>
+     * <P>
+     * This method will not ensure that <code>ServiceRecord</code> associated
+     * with this notifier is a completely
+     * valid service record. It is the responsibility of the application to
+     * ensure that the service record follows all of the applicable
+     * syntactic and semantic rules for service record correctness.
+     *
+     * @param handler the request handler that will respond to OBEX requests
+     *
+     * @return the connection to the client
+     *
+     * @exception IOException if an error occurs in the transport layer
+     *
+     * @exception NullPointerException if <code>handler</code> is
+     * <code>null</code>
+     *
+     * @exception ServiceRegistrationException if the structure of the
+     * associated service record is invalid or if the service record
+     * could not be added successfully to the local SDDB.  The
+     * structure of service record is invalid if the service
+     * record is missing any mandatory service attributes, or has
+     * changed any of the values described above which are fixed and
+     * cannot be changed. Failures to add the record to the SDDB could
+     * be due to insufficient disk space, database locks, etc.
+     *
+     * @exception BluetoothStateException if the server device could
+     * not be placed in connectable mode because the device user has
+     * configured the device to be non-connectable
+     */
+    public ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
+
+    /**
+     * Waits for a transport layer connection to be established and specifies
+     * the handler to handle the requests from the client and the
+     * <code>Authenticator</code> to use to respond to authentication challenge
+     * and authentication response headers.
+     * <P>
+     * <H4>Additional Note for OBEX over Bluetooth</H4>
+     * If this method is called on a <code>SessionNotifier</code> object that
+     * does not have a <code>ServiceRecord</code> in the SDDB, the
+     * <code>ServiceRecord</code> for this object will be added to the SDDB.
+     * This method requests the BCC to put the
+     * local device in connectable mode so that it will respond to
+     * connection attempts by clients.
+     * <P>
+     * The following checks are done to verify that the service record
+     * provided is valid. If any of these checks fail, then a
+     * <code>ServiceRegistrationException</code> is thrown.
+     * <UL>
+     * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory
+     * service attributes for a <code>btgoep</code> service record, must be
+     * present in the <code>ServiceRecord</code> associated with this notifier.
+     * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
+     * <LI>The <code>ServiceRecord</code> associated with this notifier must
+     *  not have changed the RFCOMM server channel number
+     * </UL>
+     * <P>
+     * This method will not ensure that <code>ServiceRecord</code> associated
+     * with this notifier is a completely
+     * valid service record. It is the responsibility of the application to
+     * ensure that the service record follows all of the applicable
+     * syntactic and semantic rules for service record correctness.
+     *
+     * @param handler the request handler that will respond to OBEX requests
+     *
+     * @param auth the <code>Authenticator</code> to use with this connection;
+     * if <code>null</code> then no <code>Authenticator</code> will be used
+     *
+     * @return the connection to the client
+     *
+     * @exception IOException if an error occurs in the transport layer
+     *
+     * @exception NullPointerException if <code>handler</code> is
+     * <code>null</code>
+     *
+     * @exception ServiceRegistrationException if the structure of the
+     * associated service record is invalid or if the service record
+     * could not be added successfully to the local SDDB.  The
+     * structure of service record is invalid if the service
+     * record is missing any mandatory service attributes, or has
+     * changed any of the values described above which are fixed and
+     * cannot be changed. Failures to add the record to the SDDB could
+     * be due to insufficient disk space, database locks, etc.
+     *
+     * @exception BluetoothStateException if the server device could
+     * not be placed in connectable mode because the device user has
+     * configured the device to be non-connectable
+     */
+    public ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth)
+            throws IOException;
+}