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 
    260         if (header != null) {
    261             totalLength += header.length;
    262             data = new byte[totalLength];
    263             data[0] = (byte)code;
    264             data[1] = (byte)(totalLength >> 8);
    265             data[2] = (byte)totalLength;
    266             System.arraycopy(header, 0, data, 3, header.length);
    267         } else {
    268             data = new byte[totalLength];
    269             data[0] = (byte)code;
    270             data[1] = (byte)0x00;
    271             data[2] = (byte)totalLength;
    272         }
    273         mOutput.write(data);
    274         mOutput.flush();
    275     }
    276 
    277     /**
    278      * Handles a SETPATH request from a client. This method will read the rest
    279      * of the request from the client. Assuming the request is valid, it will
    280      * create a <code>HeaderSet</code> object to pass to the
    281      * <code>ServerRequestHandler</code> object. After the handler processes the
    282      * request, this method will create a reply message to send to the server
    283      * with the response code provided.
    284      * @throws IOException if an error occurred at the transport layer
    285      */
    286     private void handleSetPathRequest() throws IOException {
    287         int length;
    288         int flags;
    289         @SuppressWarnings("unused")
    290         int constants;
    291         int totalLength = 3;
    292         byte[] head = null;
    293         int code = -1;
    294         int bytesReceived;
    295         HeaderSet request = new HeaderSet();
    296         HeaderSet reply = new HeaderSet();
    297 
    298         length = mInput.read();
    299         length = (length << 8) + mInput.read();
    300         flags = mInput.read();
    301         constants = mInput.read();
    302 
    303         if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
    304             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    305             totalLength = 3;
    306         } else {
    307             if (length > 5) {
    308                 byte[] headers = new byte[length - 5];
    309                 bytesReceived = mInput.read(headers);
    310 
    311                 while (bytesReceived != headers.length) {
    312                     bytesReceived += mInput.read(headers, bytesReceived, headers.length
    313                             - bytesReceived);
    314                 }
    315 
    316                 ObexHelper.updateHeaderSet(request, headers);
    317 
    318                 if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
    319                     mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
    320                 } else {
    321                     mListener.setConnectionId(1);
    322                 }
    323                 // the Auth chan is initiated by the server, client sent back the authResp .
    324                 if (request.mAuthResp != null) {
    325                     if (!handleAuthResp(request.mAuthResp)) {
    326                         code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
    327                         mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
    328                                 request.mAuthResp));
    329                     }
    330                     request.mAuthResp = null;
    331                 }
    332             }
    333 
    334             if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
    335                 // the Auth challenge is initiated by the client
    336                 // the server will send back the authResp to the client
    337                 if (request.mAuthChall != null) {
    338                     handleAuthChall(request);
    339                     reply.mAuthResp = new byte[request.mAuthResp.length];
    340                     System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
    341                             reply.mAuthResp.length);
    342                     request.mAuthChall = null;
    343                     request.mAuthResp = null;
    344                 }
    345                 boolean backup = false;
    346                 boolean create = true;
    347                 if (!((flags & 1) == 0)) {
    348                     backup = true;
    349                 }
    350                 if (!((flags & 2) == 0)) {
    351                     create = false;
    352                 }
    353 
    354                 try {
    355                     code = mListener.onSetPath(request, reply, backup, create);
    356                 } catch (Exception e) {
    357                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    358                     return;
    359                 }
    360 
    361                 code = validateResponseCode(code);
    362 
    363                 if (reply.nonce != null) {
    364                     mChallengeDigest = new byte[16];
    365                     System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
    366                 } else {
    367                     mChallengeDigest = null;
    368                 }
    369 
    370                 long id = mListener.getConnectionId();
    371                 if (id == -1) {
    372                     reply.mConnectionID = null;
    373                 } else {
    374                     reply.mConnectionID = ObexHelper.convertToByteArray(id);
    375                 }
    376 
    377                 head = ObexHelper.createHeader(reply, false);
    378                 totalLength += head.length;
    379 
    380                 if (totalLength > mMaxPacketLength) {
    381                     totalLength = 3;
    382                     head = null;
    383                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    384                 }
    385             }
    386         }
    387 
    388         // Compute Length of OBEX SETPATH packet
    389         byte[] replyData = new byte[totalLength];
    390         replyData[0] = (byte)code;
    391         replyData[1] = (byte)(totalLength >> 8);
    392         replyData[2] = (byte)totalLength;
    393         if (head != null) {
    394             System.arraycopy(head, 0, replyData, 3, head.length);
    395         }
    396         /*
    397          * Write the OBEX SETPATH packet to the server. Byte 0: response code
    398          * Byte 1&2: Connect Packet Length Byte 3 to n: headers
    399          */
    400         mOutput.write(replyData);
    401         mOutput.flush();
    402     }
    403 
    404     /**
    405      * Handles a disconnect request from a client. This method will read the
    406      * rest of the request from the client. Assuming the request is valid, it
    407      * will create a <code>HeaderSet</code> object to pass to the
    408      * <code>ServerRequestHandler</code> object. After the handler processes the
    409      * request, this method will create a reply message to send to the server.
    410      * @throws IOException if an error occurred at the transport layer
    411      */
    412     private void handleDisconnectRequest() throws IOException {
    413         int length;
    414         int code = ResponseCodes.OBEX_HTTP_OK;
    415         int totalLength = 3;
    416         byte[] head = null;
    417         int bytesReceived;
    418         HeaderSet request = new HeaderSet();
    419         HeaderSet reply = new HeaderSet();
    420 
    421         length = mInput.read();
    422         length = (length << 8) + mInput.read();
    423 
    424         if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
    425             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    426             totalLength = 3;
    427         } else {
    428             if (length > 3) {
    429                 byte[] headers = new byte[length - 3];
    430                 bytesReceived = mInput.read(headers);
    431 
    432                 while (bytesReceived != headers.length) {
    433                     bytesReceived += mInput.read(headers, bytesReceived, headers.length
    434                             - bytesReceived);
    435                 }
    436 
    437                 ObexHelper.updateHeaderSet(request, headers);
    438             }
    439 
    440             if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
    441                 mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
    442             } else {
    443                 mListener.setConnectionId(1);
    444             }
    445 
    446             if (request.mAuthResp != null) {
    447                 if (!handleAuthResp(request.mAuthResp)) {
    448                     code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
    449                     mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
    450                             request.mAuthResp));
    451                 }
    452                 request.mAuthResp = null;
    453             }
    454 
    455             if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
    456 
    457                 if (request.mAuthChall != null) {
    458                     handleAuthChall(request);
    459                     request.mAuthChall = null;
    460                 }
    461 
    462                 try {
    463                     mListener.onDisconnect(request, reply);
    464                 } catch (Exception e) {
    465                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    466                     return;
    467                 }
    468 
    469                 long id = mListener.getConnectionId();
    470                 if (id == -1) {
    471                     reply.mConnectionID = null;
    472                 } else {
    473                     reply.mConnectionID = ObexHelper.convertToByteArray(id);
    474                 }
    475 
    476                 head = ObexHelper.createHeader(reply, false);
    477                 totalLength += head.length;
    478 
    479                 if (totalLength > mMaxPacketLength) {
    480                     totalLength = 3;
    481                     head = null;
    482                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    483                 }
    484             }
    485         }
    486 
    487         // Compute Length of OBEX CONNECT packet
    488         byte[] replyData;
    489         if (head != null) {
    490             replyData = new byte[3 + head.length];
    491         } else {
    492             replyData = new byte[3];
    493         }
    494         replyData[0] = (byte)code;
    495         replyData[1] = (byte)(totalLength >> 8);
    496         replyData[2] = (byte)totalLength;
    497         if (head != null) {
    498             System.arraycopy(head, 0, replyData, 3, head.length);
    499         }
    500         /*
    501          * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
    502          * Byte 1&2: Connect Packet Length Byte 3 to n: headers
    503          */
    504         mOutput.write(replyData);
    505         mOutput.flush();
    506     }
    507 
    508     /**
    509      * Handles a connect request from a client. This method will read the rest
    510      * of the request from the client. Assuming the request is valid, it will
    511      * create a <code>HeaderSet</code> object to pass to the
    512      * <code>ServerRequestHandler</code> object. After the handler processes the
    513      * request, this method will create a reply message to send to the server
    514      * with the response code provided.
    515      * @throws IOException if an error occurred at the transport layer
    516      */
    517     private void handleConnectRequest() throws IOException {
    518         int packetLength;
    519         @SuppressWarnings("unused")
    520         int version;
    521         @SuppressWarnings("unused")
    522         int flags;
    523         int totalLength = 7;
    524         byte[] head = null;
    525         int code = -1;
    526         HeaderSet request = new HeaderSet();
    527         HeaderSet reply = new HeaderSet();
    528         int bytesReceived;
    529 
    530         /*
    531          * Read in the length of the OBEX packet, OBEX version, flags, and max
    532          * packet length
    533          */
    534         packetLength = mInput.read();
    535         packetLength = (packetLength << 8) + mInput.read();
    536         version = mInput.read();
    537         flags = mInput.read();
    538         mMaxPacketLength = mInput.read();
    539         mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
    540 
    541         // should we check it?
    542         if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
    543             mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
    544         }
    545 
    546         if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
    547             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
    548             totalLength = 7;
    549         } else {
    550             if (packetLength > 7) {
    551                 byte[] headers = new byte[packetLength - 7];
    552                 bytesReceived = mInput.read(headers);
    553 
    554                 while (bytesReceived != headers.length) {
    555                     bytesReceived += mInput.read(headers, bytesReceived, headers.length
    556                             - bytesReceived);
    557                 }
    558 
    559                 ObexHelper.updateHeaderSet(request, headers);
    560             }
    561 
    562             if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
    563                 mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
    564             } else {
    565                 mListener.setConnectionId(1);
    566             }
    567 
    568             if (request.mAuthResp != null) {
    569                 if (!handleAuthResp(request.mAuthResp)) {
    570                     code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
    571                     mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
    572                             request.mAuthResp));
    573                 }
    574                 request.mAuthResp = null;
    575             }
    576 
    577             if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
    578                 if (request.mAuthChall != null) {
    579                     handleAuthChall(request);
    580                     reply.mAuthResp = new byte[request.mAuthResp.length];
    581                     System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
    582                             reply.mAuthResp.length);
    583                     request.mAuthChall = null;
    584                     request.mAuthResp = null;
    585                 }
    586 
    587                 try {
    588                     code = mListener.onConnect(request, reply);
    589                     code = validateResponseCode(code);
    590 
    591                     if (reply.nonce != null) {
    592                         mChallengeDigest = new byte[16];
    593                         System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
    594                     } else {
    595                         mChallengeDigest = null;
    596                     }
    597                     long id = mListener.getConnectionId();
    598                     if (id == -1) {
    599                         reply.mConnectionID = null;
    600                     } else {
    601                         reply.mConnectionID = ObexHelper.convertToByteArray(id);
    602                     }
    603 
    604                     head = ObexHelper.createHeader(reply, false);
    605                     totalLength += head.length;
    606 
    607                     if (totalLength > mMaxPacketLength) {
    608                         totalLength = 7;
    609                         head = null;
    610                         code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    611                     }
    612                 } catch (Exception e) {
    613                     e.printStackTrace();
    614                     totalLength = 7;
    615                     head = null;
    616                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    617                 }
    618 
    619             }
    620         }
    621 
    622         // Compute Length of OBEX CONNECT packet
    623         byte[] length = ObexHelper.convertToByteArray(totalLength);
    624 
    625         /*
    626          * Write the OBEX CONNECT packet to the server. Byte 0: response code
    627          * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
    628          * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
    629          * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
    630          */
    631         byte[] sendData = new byte[totalLength];
    632         sendData[0] = (byte)code;
    633         sendData[1] = length[2];
    634         sendData[2] = length[3];
    635         sendData[3] = (byte)0x10;
    636         sendData[4] = (byte)0x00;
    637         sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
    638         sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
    639 
    640         if (head != null) {
    641             System.arraycopy(head, 0, sendData, 7, head.length);
    642         }
    643 
    644         mOutput.write(sendData);
    645         mOutput.flush();
    646     }
    647 
    648     /**
    649      * Closes the server session - in detail close I/O streams and the
    650      * underlying transport layer. Internal flag is also set so that later
    651      * attempt to read/write will throw an exception.
    652      */
    653     public synchronized void close() {
    654         if (mListener != null) {
    655             mListener.onClose();
    656         }
    657         try {
    658             mInput.close();
    659             mOutput.close();
    660             mTransport.close();
    661             mClosed = true;
    662         } catch (Exception e) {
    663         }
    664         mTransport = null;
    665         mInput = null;
    666         mOutput = null;
    667         mListener = null;
    668     }
    669 
    670     /**
    671      * Verifies that the response code is valid. If it is not valid, it will
    672      * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
    673      * @param code the response code to check
    674      * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
    675      *         if <code>code</code> is not valid
    676      */
    677     private int validateResponseCode(int code) {
    678 
    679         if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
    680             return code;
    681         }
    682         if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
    683                 && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
    684             return code;
    685         }
    686         if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
    687                 && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
    688             return code;
    689         }
    690         if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
    691                 && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
    692             return code;
    693         }
    694         if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
    695                 && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
    696             return code;
    697         }
    698         return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    699     }
    700 
    701 }
    702