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