Home | History | Annotate | Download | only in obex
      1 /*
      2  * Copyright (c) 2008-2009, Motorola, Inc.
      3  *
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are met:
      8  *
      9  * - Redistributions of source code must retain the above copyright notice,
     10  * this list of conditions and the following disclaimer.
     11  *
     12  * - Redistributions in binary form must reproduce the above copyright notice,
     13  * this list of conditions and the following disclaimer in the documentation
     14  * and/or other materials provided with the distribution.
     15  *
     16  * - Neither the name of the Motorola, Inc. nor the names of its contributors
     17  * may be used to endorse or promote products derived from this software
     18  * without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
     24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package javax.obex;
     34 
     35 import android.util.Log;
     36 
     37 import java.io.InputStream;
     38 import java.io.IOException;
     39 import java.io.OutputStream;
     40 
     41 /**
     42  * This class in an implementation of the OBEX ServerSession.
     43  * @hide
     44  */
     45 public final class ServerSession extends ObexSession implements Runnable {
     46 
     47     private static final String TAG = "Obex ServerSession";
     48 
     49     private ObexTransport mTransport;
     50 
     51     private InputStream mInput;
     52 
     53     private OutputStream mOutput;
     54 
     55     private ServerRequestHandler mListener;
     56 
     57     private Thread mProcessThread;
     58 
     59     private int mMaxPacketLength;
     60 
     61     private boolean mClosed;
     62 
     63     /**
     64      * Creates new ServerSession.
     65      * @param trans the connection to the client
     66      * @param handler the event listener that will process requests
     67      * @param auth the authenticator to use with this connection
     68      * @throws IOException if an error occurred while opening the input and
     69      *         output streams
     70      */
     71     public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
     72             throws IOException {
     73         mAuthenticator = auth;
     74         mTransport = trans;
     75         mInput = mTransport.openInputStream();
     76         mOutput = mTransport.openOutputStream();
     77         mListener = handler;
     78         mMaxPacketLength = 256;
     79 
     80         mClosed = false;
     81         mProcessThread = new Thread(this);
     82         mProcessThread.start();
     83     }
     84 
     85     /**
     86      * Processes requests made to the server and forwards them to the
     87      * appropriate event listener.
     88      */
     89     public void run() {
     90         try {
     91 
     92             boolean done = false;
     93             while (!done && !mClosed) {
     94                 int requestType = mInput.read();
     95                 switch (requestType) {
     96                     case ObexHelper.OBEX_OPCODE_CONNECT:
     97                         handleConnectRequest();
     98                         break;
     99 
    100                     case ObexHelper.OBEX_OPCODE_DISCONNECT:
    101                         handleDisconnectRequest();
    102                         done = true;
    103                         break;
    104 
    105                     case ObexHelper.OBEX_OPCODE_GET:
    106                     case ObexHelper.OBEX_OPCODE_GET_FINAL:
    107                         handleGetRequest(requestType);
    108                         break;
    109 
    110                     case ObexHelper.OBEX_OPCODE_PUT:
    111                     case ObexHelper.OBEX_OPCODE_PUT_FINAL:
    112                         handlePutRequest(requestType);
    113                         break;
    114 
    115                     case ObexHelper.OBEX_OPCODE_SETPATH:
    116                         handleSetPathRequest();
    117                         break;
    118                     case ObexHelper.OBEX_OPCODE_ABORT:
    119                         handleAbortRequest();
    120                         break;
    121 
    122                     case -1:
    123                         done = true;
    124                         break;
    125 
    126                     default:
    127 
    128                         /*
    129                          * Received a request type that is not recognized so I am
    130                          * just going to read the packet and send a not implemented
    131                          * to the client
    132                          */
    133                         int length = mInput.read();
    134                         length = (length << 8) + mInput.read();
    135                         for (int i = 3; i < length; i++) {
    136                             mInput.read();
    137                         }
    138                         sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
    139                 }
    140             }
    141 
    142         } catch (NullPointerException e) {
    143             Log.d(TAG, e.toString());
    144         } catch (Exception e) {
    145             Log.d(TAG, e.toString());
    146         }
    147         close();
    148     }
    149 
    150     /**
    151      * Handles a ABORT request from a client. This method will read the rest of
    152      * the request from the client. Assuming the request is valid, it will
    153      * create a <code>HeaderSet</code> object to pass to the
    154      * <code>ServerRequestHandler</code> object. After the handler processes the
    155      * request, this method will create a reply message to send to the server.
    156      *
    157      * @throws IOException if an error occurred at the transport layer
    158      */
    159     private void handleAbortRequest() throws IOException {
    160         int code = ResponseCodes.OBEX_HTTP_OK;
    161         HeaderSet request = new HeaderSet();
    162         HeaderSet reply = new HeaderSet();
    163 
    164         int length = mInput.read();
    165         length = (length << 8) + mInput.read();
    166         if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
    167             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    168         } else {
    169             for (int i = 3; i < length; i++) {
    170                 mInput.read();
    171             }
    172             code = mListener.onAbort(request, reply);
    173             Log.v(TAG, "onAbort request handler return value- " + code);
    174             code = validateResponseCode(code);
    175         }
    176         sendResponse(code, null);
    177     }
    178 
    179     /**
    180      * Handles a PUT request from a client. This method will provide a
    181      * <code>ServerOperation</code> object to the request handler. The
    182      * <code>ServerOperation</code> object will handle the rest of the request.
    183      * It will also send replies and receive requests until the final reply
    184      * should be sent. When the final reply should be sent, this method will get
    185      * the response code to use and send the reply. The
    186      * <code>ServerOperation</code> object will always reply with a
    187      * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
    188      * needed.
    189      * @param type the type of request received; either 0x02 or 0x82
    190      * @throws IOException if an error occurred at the transport layer
    191      */
    192     private void handlePutRequest(int type) throws IOException {
    193         ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
    194         try {
    195             int response = -1;
    196 
    197             if ((op.finalBitSet) && !op.isValidBody()) {
    198                 response = validateResponseCode(mListener
    199                         .onDelete(op.requestHeader, op.replyHeader));
    200             } else {
    201                 response = validateResponseCode(mListener.onPut(op));
    202             }
    203             if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
    204                 op.sendReply(response);
    205             } else if (!op.isAborted) {
    206                 // wait for the final bit
    207                 while (!op.finalBitSet) {
    208                     op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    209                 }
    210                 op.sendReply(response);
    211             }
    212         } catch (Exception e) {
    213             /*To fix bugs in aborted cases,
    214              *(client abort file transfer prior to the last packet which has the end of body header,
    215              *internal error should not be sent because server has already replied with
    216              *OK response in "sendReply")
    217              */
    218             if (!op.isAborted) {
    219                 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    220             }
    221         }
    222     }
    223 
    224     /**
    225      * Handles a GET request from a client. This method will provide a
    226      * <code>ServerOperation</code> object to the request handler. The
    227      * <code>ServerOperation</code> object will handle the rest of the request.
    228      * It will also send replies and receive requests until the final reply
    229      * should be sent. When the final reply should be sent, this method will get
    230      * the response code to use and send the reply. The
    231      * <code>ServerOperation</code> object will always reply with a
    232      * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
    233      * needed.
    234      * @param type the type of request received; either 0x03 or 0x83
    235      * @throws IOException if an error occurred at the transport layer
    236      */
    237     private void handleGetRequest(int type) throws IOException {
    238         ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
    239         try {
    240             int response = validateResponseCode(mListener.onGet(op));
    241 
    242             if (!op.isAborted) {
    243                 op.sendReply(response);
    244             }
    245         } catch (Exception e) {
    246             sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    247         }
    248     }
    249 
    250     /**
    251      * Send standard response.
    252      * @param code the response code to send
    253      * @param header the headers to include in the response
    254      * @throws IOException if an IO error occurs
    255      */
    256     public void sendResponse(int code, byte[] header) throws IOException {
    257         int totalLength = 3;
    258         byte[] data = null;
    259         OutputStream op = mOutput;
    260         if (op == null) {
    261             return;
    262         }
    263 
    264         if (header != null) {
    265             totalLength += header.length;
    266             data = new byte[totalLength];
    267             data[0] = (byte)code;
    268             data[1] = (byte)(totalLength >> 8);
    269             data[2] = (byte)totalLength;
    270             System.arraycopy(header, 0, data, 3, header.length);
    271         } else {
    272             data = new byte[totalLength];
    273             data[0] = (byte)code;
    274             data[1] = (byte)0x00;
    275             data[2] = (byte)totalLength;
    276         }
    277         op.write(data);
    278         op.flush();
    279     }
    280 
    281     /**
    282      * Handles a SETPATH request from a client. This method will read the rest
    283      * of the request from the client. Assuming the request is valid, it will
    284      * create a <code>HeaderSet</code> object to pass to the
    285      * <code>ServerRequestHandler</code> object. After the handler processes the
    286      * request, this method will create a reply message to send to the server
    287      * with the response code provided.
    288      * @throws IOException if an error occurred at the transport layer
    289      */
    290     private void handleSetPathRequest() throws IOException {
    291         int length;
    292         int flags;
    293         @SuppressWarnings("unused")
    294         int constants;
    295         int totalLength = 3;
    296         byte[] head = null;
    297         int code = -1;
    298         int bytesReceived;
    299         HeaderSet request = new HeaderSet();
    300         HeaderSet reply = new HeaderSet();
    301 
    302         length = mInput.read();
    303         length = (length << 8) + mInput.read();
    304         flags = mInput.read();
    305         constants = mInput.read();
    306 
    307         if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
    308             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    309             totalLength = 3;
    310         } else {
    311             if (length > 5) {
    312                 byte[] headers = new byte[length - 5];
    313                 bytesReceived = mInput.read(headers);
    314 
    315                 while (bytesReceived != headers.length) {
    316                     bytesReceived += mInput.read(headers, bytesReceived, headers.length
    317                             - bytesReceived);
    318                 }
    319 
    320                 ObexHelper.updateHeaderSet(request, headers);
    321 
    322                 if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
    323                     mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
    324                 } else {
    325                     mListener.setConnectionId(1);
    326                 }
    327                 // the Auth chan is initiated by the server, client sent back the authResp .
    328                 if (request.mAuthResp != null) {
    329                     if (!handleAuthResp(request.mAuthResp)) {
    330                         code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
    331                         mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
    332                                 request.mAuthResp));
    333                     }
    334                     request.mAuthResp = null;
    335                 }
    336             }
    337 
    338             if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
    339                 // the Auth challenge is initiated by the client
    340                 // the server will send back the authResp to the client
    341                 if (request.mAuthChall != null) {
    342                     handleAuthChall(request);
    343                     reply.mAuthResp = new byte[request.mAuthResp.length];
    344                     System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
    345                             reply.mAuthResp.length);
    346                     request.mAuthChall = null;
    347                     request.mAuthResp = null;
    348                 }
    349                 boolean backup = false;
    350                 boolean create = true;
    351                 if (!((flags & 1) == 0)) {
    352                     backup = true;
    353                 }
    354                 if (!((flags & 2) == 0)) {
    355                     create = false;
    356                 }
    357 
    358                 try {
    359                     code = mListener.onSetPath(request, reply, backup, create);
    360                 } catch (Exception e) {
    361                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    362                     return;
    363                 }
    364 
    365                 code = validateResponseCode(code);
    366 
    367                 if (reply.nonce != null) {
    368                     mChallengeDigest = new byte[16];
    369                     System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
    370                 } else {
    371                     mChallengeDigest = null;
    372                 }
    373 
    374                 long id = mListener.getConnectionId();
    375                 if (id == -1) {
    376                     reply.mConnectionID = null;
    377                 } else {
    378                     reply.mConnectionID = ObexHelper.convertToByteArray(id);
    379                 }
    380 
    381                 head = ObexHelper.createHeader(reply, false);
    382                 totalLength += head.length;
    383 
    384                 if (totalLength > mMaxPacketLength) {
    385                     totalLength = 3;
    386                     head = null;
    387                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    388                 }
    389             }
    390         }
    391 
    392         // Compute Length of OBEX SETPATH packet
    393         byte[] replyData = new byte[totalLength];
    394         replyData[0] = (byte)code;
    395         replyData[1] = (byte)(totalLength >> 8);
    396         replyData[2] = (byte)totalLength;
    397         if (head != null) {
    398             System.arraycopy(head, 0, replyData, 3, head.length);
    399         }
    400         /*
    401          * Write the OBEX SETPATH packet to the server. Byte 0: response code
    402          * Byte 1&2: Connect Packet Length Byte 3 to n: headers
    403          */
    404         mOutput.write(replyData);
    405         mOutput.flush();
    406     }
    407 
    408     /**
    409      * Handles a disconnect request from a client. This method will read the
    410      * rest of the request from the client. Assuming the request is valid, it
    411      * will create a <code>HeaderSet</code> object to pass to the
    412      * <code>ServerRequestHandler</code> object. After the handler processes the
    413      * request, this method will create a reply message to send to the server.
    414      * @throws IOException if an error occurred at the transport layer
    415      */
    416     private void handleDisconnectRequest() throws IOException {
    417         int length;
    418         int code = ResponseCodes.OBEX_HTTP_OK;
    419         int totalLength = 3;
    420         byte[] head = null;
    421         int bytesReceived;
    422         HeaderSet request = new HeaderSet();
    423         HeaderSet reply = new HeaderSet();
    424 
    425         length = mInput.read();
    426         length = (length << 8) + mInput.read();
    427 
    428         if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
    429             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    430             totalLength = 3;
    431         } else {
    432             if (length > 3) {
    433                 byte[] headers = new byte[length - 3];
    434                 bytesReceived = mInput.read(headers);
    435 
    436                 while (bytesReceived != headers.length) {
    437                     bytesReceived += mInput.read(headers, bytesReceived, headers.length
    438                             - bytesReceived);
    439                 }
    440 
    441                 ObexHelper.updateHeaderSet(request, headers);
    442             }
    443 
    444             if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
    445                 mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
    446             } else {
    447                 mListener.setConnectionId(1);
    448             }
    449 
    450             if (request.mAuthResp != null) {
    451                 if (!handleAuthResp(request.mAuthResp)) {
    452                     code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
    453                     mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
    454                             request.mAuthResp));
    455                 }
    456                 request.mAuthResp = null;
    457             }
    458 
    459             if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
    460 
    461                 if (request.mAuthChall != null) {
    462                     handleAuthChall(request);
    463                     request.mAuthChall = null;
    464                 }
    465 
    466                 try {
    467                     mListener.onDisconnect(request, reply);
    468                 } catch (Exception e) {
    469                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    470                     return;
    471                 }
    472 
    473                 long id = mListener.getConnectionId();
    474                 if (id == -1) {
    475                     reply.mConnectionID = null;
    476                 } else {
    477                     reply.mConnectionID = ObexHelper.convertToByteArray(id);
    478                 }
    479 
    480                 head = ObexHelper.createHeader(reply, false);
    481                 totalLength += head.length;
    482 
    483                 if (totalLength > mMaxPacketLength) {
    484                     totalLength = 3;
    485                     head = null;
    486                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    487                 }
    488             }
    489         }
    490 
    491         // Compute Length of OBEX CONNECT packet
    492         byte[] replyData;
    493         if (head != null) {
    494             replyData = new byte[3 + head.length];
    495         } else {
    496             replyData = new byte[3];
    497         }
    498         replyData[0] = (byte)code;
    499         replyData[1] = (byte)(totalLength >> 8);
    500         replyData[2] = (byte)totalLength;
    501         if (head != null) {
    502             System.arraycopy(head, 0, replyData, 3, head.length);
    503         }
    504         /*
    505          * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
    506          * Byte 1&2: Connect Packet Length Byte 3 to n: headers
    507          */
    508         mOutput.write(replyData);
    509         mOutput.flush();
    510     }
    511 
    512     /**
    513      * Handles a connect request from a client. This method will read the rest
    514      * of the request from the client. Assuming the request is valid, it will
    515      * create a <code>HeaderSet</code> object to pass to the
    516      * <code>ServerRequestHandler</code> object. After the handler processes the
    517      * request, this method will create a reply message to send to the server
    518      * with the response code provided.
    519      * @throws IOException if an error occurred at the transport layer
    520      */
    521     private void handleConnectRequest() throws IOException {
    522         int packetLength;
    523         @SuppressWarnings("unused")
    524         int version;
    525         @SuppressWarnings("unused")
    526         int flags;
    527         int totalLength = 7;
    528         byte[] head = null;
    529         int code = -1;
    530         HeaderSet request = new HeaderSet();
    531         HeaderSet reply = new HeaderSet();
    532         int bytesReceived;
    533 
    534         /*
    535          * Read in the length of the OBEX packet, OBEX version, flags, and max
    536          * packet length
    537          */
    538         packetLength = mInput.read();
    539         packetLength = (packetLength << 8) + mInput.read();
    540         version = mInput.read();
    541         flags = mInput.read();
    542         mMaxPacketLength = mInput.read();
    543         mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
    544 
    545         // should we check it?
    546         if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
    547             mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
    548         }
    549 
    550         if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
    551             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    552             totalLength = 7;
    553         } else {
    554             if (packetLength > 7) {
    555                 byte[] headers = new byte[packetLength - 7];
    556                 bytesReceived = mInput.read(headers);
    557 
    558                 while (bytesReceived != headers.length) {
    559                     bytesReceived += mInput.read(headers, bytesReceived, headers.length
    560                             - bytesReceived);
    561                 }
    562 
    563                 ObexHelper.updateHeaderSet(request, headers);
    564             }
    565 
    566             if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
    567                 mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
    568             } else {
    569                 mListener.setConnectionId(1);
    570             }
    571 
    572             if (request.mAuthResp != null) {
    573                 if (!handleAuthResp(request.mAuthResp)) {
    574                     code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
    575                     mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
    576                             request.mAuthResp));
    577                 }
    578                 request.mAuthResp = null;
    579             }
    580 
    581             if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
    582                 if (request.mAuthChall != null) {
    583                     handleAuthChall(request);
    584                     reply.mAuthResp = new byte[request.mAuthResp.length];
    585                     System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
    586                             reply.mAuthResp.length);
    587                     request.mAuthChall = null;
    588                     request.mAuthResp = null;
    589                 }
    590 
    591                 try {
    592                     code = mListener.onConnect(request, reply);
    593                     code = validateResponseCode(code);
    594 
    595                     if (reply.nonce != null) {
    596                         mChallengeDigest = new byte[16];
    597                         System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
    598                     } else {
    599                         mChallengeDigest = null;
    600                     }
    601                     long id = mListener.getConnectionId();
    602                     if (id == -1) {
    603                         reply.mConnectionID = null;
    604                     } else {
    605                         reply.mConnectionID = ObexHelper.convertToByteArray(id);
    606                     }
    607 
    608                     head = ObexHelper.createHeader(reply, false);
    609                     totalLength += head.length;
    610 
    611                     if (totalLength > mMaxPacketLength) {
    612                         totalLength = 7;
    613                         head = null;
    614                         code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    615                     }
    616                 } catch (Exception e) {
    617                     e.printStackTrace();
    618                     totalLength = 7;
    619                     head = null;
    620                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    621                 }
    622 
    623             }
    624         }
    625 
    626         // Compute Length of OBEX CONNECT packet
    627         byte[] length = ObexHelper.convertToByteArray(totalLength);
    628 
    629         /*
    630          * Write the OBEX CONNECT packet to the server. Byte 0: response code
    631          * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
    632          * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
    633          * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
    634          */
    635         byte[] sendData = new byte[totalLength];
    636         sendData[0] = (byte)code;
    637         sendData[1] = length[2];
    638         sendData[2] = length[3];
    639         sendData[3] = (byte)0x10;
    640         sendData[4] = (byte)0x00;
    641         sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
    642         sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
    643 
    644         if (head != null) {
    645             System.arraycopy(head, 0, sendData, 7, head.length);
    646         }
    647 
    648         mOutput.write(sendData);
    649         mOutput.flush();
    650     }
    651 
    652     /**
    653      * Closes the server session - in detail close I/O streams and the
    654      * underlying transport layer. Internal flag is also set so that later
    655      * attempt to read/write will throw an exception.
    656      */
    657     public synchronized void close() {
    658         if (mListener != null) {
    659             mListener.onClose();
    660         }
    661         try {
    662             mInput.close();
    663             mOutput.close();
    664             mTransport.close();
    665             mClosed = true;
    666         } catch (Exception e) {
    667         }
    668         mTransport = null;
    669         mInput = null;
    670         mOutput = null;
    671         mListener = null;
    672     }
    673 
    674     /**
    675      * Verifies that the response code is valid. If it is not valid, it will
    676      * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
    677      * @param code the response code to check
    678      * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
    679      *         if <code>code</code> is not valid
    680      */
    681     private int validateResponseCode(int code) {
    682 
    683         if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
    684             return code;
    685         }
    686         if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
    687                 && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
    688             return code;
    689         }
    690         if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
    691                 && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
    692             return code;
    693         }
    694         if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
    695                 && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
    696             return code;
    697         }
    698         if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
    699                 && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
    700             return code;
    701         }
    702         return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    703     }
    704 
    705 }
    706