Home | History | Annotate | Download | only in obex
      1 /* Copyright (c) 2015 The Android Open Source Project
      2  * Copyright (C) 2015 Samsung LSI
      3  * Copyright (c) 2008-2009, Motorola, Inc.
      4  *
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions are met:
      9  *
     10  * - Redistributions of source code must retain the above copyright notice,
     11  * this list of conditions and the following disclaimer.
     12  *
     13  * - Redistributions in binary form must reproduce the above copyright notice,
     14  * this list of conditions and the following disclaimer in the documentation
     15  * and/or other materials provided with the distribution.
     16  *
     17  * - Neither the name of the Motorola, Inc. nor the names of its contributors
     18  * may be used to endorse or promote products derived from this software
     19  * without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
     25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     31  * POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 package javax.obex;
     35 
     36 import java.io.IOException;
     37 import java.io.InputStream;
     38 import java.io.DataInputStream;
     39 import java.io.OutputStream;
     40 import java.io.DataOutputStream;
     41 import java.io.ByteArrayOutputStream;
     42 
     43 import android.util.Log;
     44 
     45 /**
     46  * This class implements the Operation interface for server side connections.
     47  * <P>
     48  * <STRONG>Request Codes</STRONG> There are four different request codes that
     49  * are in this class. 0x02 is a PUT request that signals that the request is not
     50  * complete and requires an additional OBEX packet. 0x82 is a PUT request that
     51  * says that request is complete. In this case, the server can begin sending the
     52  * response. The 0x03 is a GET request that signals that the request is not
     53  * finished. When the server receives a 0x83, the client is signaling the server
     54  * that it is done with its request. TODO: Extend the ClientOperation and reuse
     55  * the methods defined TODO: in that class.
     56  * @hide
     57  */
     58 public final class ServerOperation implements Operation, BaseStream {
     59 
     60     private static final String TAG = "ServerOperation";
     61 
     62     private static final boolean V = ObexHelper.VDBG; // Verbose debugging
     63 
     64     public boolean isAborted;
     65 
     66     public HeaderSet requestHeader;
     67 
     68     public HeaderSet replyHeader;
     69 
     70     public boolean finalBitSet;
     71 
     72     private InputStream mInput;
     73 
     74     private ServerSession mParent;
     75 
     76     private int mMaxPacketLength;
     77 
     78     private int mResponseSize;
     79 
     80     private boolean mClosed;
     81 
     82     private boolean mGetOperation;
     83 
     84     private PrivateInputStream mPrivateInput;
     85 
     86     private PrivateOutputStream mPrivateOutput;
     87 
     88     private ObexTransport mTransport;
     89 
     90     private boolean mPrivateOutputOpen;
     91 
     92     private String mExceptionString;
     93 
     94     private ServerRequestHandler mListener;
     95 
     96     private boolean mRequestFinished;
     97 
     98     private boolean mHasBody;
     99 
    100     private boolean mSendBodyHeader = true;
    101     // Assume SRM disabled - needs to be explicit
    102     // enabled by client
    103     private boolean mSrmEnabled = false;
    104     // A latch - when triggered, there is not way back ;-)
    105     private boolean mSrmActive = false;
    106     // Set to true when a SRM enable response have been send
    107     private boolean mSrmResponseSent = false;
    108     // keep waiting until final-bit is received in request
    109     // to handle the case where the SRM enable header is in
    110     // a different OBEX packet than the SRMP header.
    111     private boolean mSrmWaitingForRemote = true;
    112     // Why should we wait? - currently not exposed to apps.
    113     private boolean mSrmLocalWait = false;
    114 
    115     /**
    116      * Creates new ServerOperation
    117      * @param p the parent that created this object
    118      * @param in the input stream to read from
    119      * @param out the output stream to write to
    120      * @param request the initial request that was received from the client
    121      * @param maxSize the max packet size that the client will accept
    122      * @param listen the listener that is responding to the request
    123      * @throws IOException if an IO error occurs
    124      */
    125     public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
    126             ServerRequestHandler listen) throws IOException {
    127 
    128         isAborted = false;
    129         mParent = p;
    130         mInput = in;
    131         mMaxPacketLength = maxSize;
    132         mClosed = false;
    133         requestHeader = new HeaderSet();
    134         replyHeader = new HeaderSet();
    135         mPrivateInput = new PrivateInputStream(this);
    136         mResponseSize = 3;
    137         mListener = listen;
    138         mRequestFinished = false;
    139         mPrivateOutputOpen = false;
    140         mHasBody = false;
    141         ObexPacket packet;
    142         mTransport = p.getTransport();
    143 
    144         /*
    145          * Determine if this is a PUT request
    146          */
    147         if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
    148                 (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
    149             /*
    150              * It is a PUT request.
    151              */
    152             mGetOperation = false;
    153 
    154             /*
    155              * Determine if the final bit is set
    156              */
    157             if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
    158                 finalBitSet = false;
    159             } else {
    160                 finalBitSet = true;
    161                 mRequestFinished = true;
    162             }
    163         } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
    164                 (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
    165             /*
    166              * It is a GET request.
    167              */
    168             mGetOperation = true;
    169 
    170             // For Get request, final bit set is decided by server side logic
    171             finalBitSet = false;
    172 
    173             if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
    174                 mRequestFinished = true;
    175             }
    176         } else {
    177             throw new IOException("ServerOperation can not handle such request");
    178         }
    179 
    180         packet = ObexPacket.read(request, mInput);
    181 
    182         /*
    183          * Determine if the packet length is larger than this device can receive
    184          */
    185         if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
    186             mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
    187             throw new IOException("Packet received was too large. Length: "
    188                     + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
    189         }
    190 
    191         /*
    192          * Determine if any headers were sent in the initial request
    193          */
    194         if (packet.mLength > 3) {
    195             if(!handleObexPacket(packet)) {
    196                 return;
    197             }
    198             if (!mHasBody) {
    199                 while ((!mGetOperation) && (!finalBitSet)) {
    200                     sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    201                     if (mPrivateInput.available() > 0) {
    202                         break;
    203                     }
    204                 }
    205             }
    206         }
    207 
    208         while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
    209             sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    210             if (mPrivateInput.available() > 0) {
    211                 break;
    212             }
    213         }
    214 
    215         // wait for get request finished !!!!
    216         while (mGetOperation && !mRequestFinished) {
    217             sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    218         }
    219     }
    220 
    221     /**
    222      * Parse headers and update member variables
    223      * @param packet the received obex packet
    224      * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
    225      * response have been send. Else true.
    226      * @throws IOException
    227      */
    228     private boolean handleObexPacket(ObexPacket packet) throws IOException {
    229         byte[] body = updateRequestHeaders(packet);
    230 
    231         if (body != null) {
    232             mHasBody = true;
    233         }
    234         if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
    235             mListener.setConnectionId(ObexHelper
    236                     .convertToLong(requestHeader.mConnectionID));
    237         } else {
    238             mListener.setConnectionId(1);
    239         }
    240 
    241         if (requestHeader.mAuthResp != null) {
    242             if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
    243                 mExceptionString = "Authentication Failed";
    244                 mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
    245                 mClosed = true;
    246                 requestHeader.mAuthResp = null;
    247                 return false;
    248             }
    249             requestHeader.mAuthResp = null;
    250         }
    251 
    252         if (requestHeader.mAuthChall != null) {
    253             mParent.handleAuthChall(requestHeader);
    254             // send the auhtResp to the client
    255             replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
    256             System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
    257                     replyHeader.mAuthResp.length);
    258             requestHeader.mAuthResp = null;
    259             requestHeader.mAuthChall = null;
    260         }
    261 
    262         if (body != null) {
    263             mPrivateInput.writeBytes(body, 1);
    264         }
    265         return true;
    266     }
    267 
    268     /**
    269      * Update the request header set, and sniff on SRM headers to update local state.
    270      * @param data the OBEX packet data
    271      * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
    272      * @throws IOException
    273      */
    274     private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
    275         byte[] body = null;
    276         if (packet.mPayload != null) {
    277             body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
    278         }
    279         Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
    280         if(mTransport.isSrmSupported() && srmMode != null
    281                 && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
    282             mSrmEnabled = true;
    283             if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
    284         }
    285         checkForSrmWait(packet.mHeaderId);
    286         if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
    287             if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
    288             mSrmActive = true;
    289         }
    290         return body;
    291     }
    292 
    293     /**
    294      * Call this only when a complete request have been received.
    295      * (This is not optimal, but the current design is not really suited to
    296      * the way SRM is specified.)
    297      */
    298     private void checkForSrmWait(int headerId){
    299         if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
    300                 || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
    301                 || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
    302             try {
    303                 mSrmWaitingForRemote = false;
    304                 Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
    305                 if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
    306                     mSrmWaitingForRemote = true;
    307                     // Clear the wait header, as the absents of the header when the final bit is set
    308                     // indicates don't wait.
    309                     requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
    310                 }
    311             } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
    312         }
    313     }
    314 
    315     public boolean isValidBody() {
    316         return mHasBody;
    317     }
    318 
    319     /**
    320      * Determines if the operation should continue or should wait. If it should
    321      * continue, this method will continue the operation.
    322      * @param sendEmpty if <code>true</code> then this will continue the
    323      *        operation even if no headers will be sent; if <code>false</code>
    324      *        then this method will only continue the operation if there are
    325      *        headers to send
    326      * @param inStream if<code>true</code> the stream is input stream, otherwise
    327      *        output stream
    328      * @return <code>true</code> if the operation was completed;
    329      *         <code>false</code> if no operation took place
    330      */
    331     public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
    332             throws IOException {
    333         if (!mGetOperation) {
    334             if (!finalBitSet) {
    335                 if (sendEmpty) {
    336                     sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    337                     return true;
    338                 } else {
    339                     if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
    340                         sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    341                         return true;
    342                     } else {
    343                         return false;
    344                     }
    345                 }
    346             } else {
    347                 return false;
    348             }
    349         } else {
    350             sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
    351             return true;
    352         }
    353     }
    354 
    355     /**
    356      * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
    357      * will wait for a response from the client before ending unless SRM is active.
    358      * @param type the response code to send back to the client
    359      * @return <code>true</code> if the final bit was not set on the reply;
    360      *         <code>false</code> if no reply was received because the operation
    361      *         ended, an abort was received, the final bit was set in the
    362      *         reply or SRM is active.
    363      * @throws IOException if an IO error occurs
    364      */
    365     public synchronized boolean sendReply(int type) throws IOException {
    366         ByteArrayOutputStream out = new ByteArrayOutputStream();
    367         boolean skipSend = false;
    368         boolean skipReceive = false;
    369         boolean srmRespSendPending = false;
    370 
    371         long id = mListener.getConnectionId();
    372         if (id == -1) {
    373             replyHeader.mConnectionID = null;
    374         } else {
    375             replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
    376         }
    377 
    378         if(mSrmEnabled && !mSrmResponseSent) {
    379             // As we are not ensured that the SRM enable is in the first OBEX packet
    380             // We must check for each reply.
    381             if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
    382             replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
    383             srmRespSendPending = true;
    384         }
    385 
    386         if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
    387             replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
    388         }
    389 
    390         byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
    391         int bodyLength = -1;
    392         int orginalBodyLength = -1;
    393 
    394         if (mPrivateOutput != null) {
    395             bodyLength = mPrivateOutput.size();
    396             orginalBodyLength = bodyLength;
    397         }
    398 
    399         if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
    400 
    401             int end = 0;
    402             int start = 0;
    403 
    404             while (end != headerArray.length) {
    405                 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
    406                         - ObexHelper.BASE_PACKET_LENGTH);
    407                 if (end == -1) {
    408 
    409                     mClosed = true;
    410 
    411                     if (mPrivateInput != null) {
    412                         mPrivateInput.close();
    413                     }
    414 
    415                     if (mPrivateOutput != null) {
    416                         mPrivateOutput.close();
    417                     }
    418                     mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
    419                     throw new IOException("OBEX Packet exceeds max packet size");
    420                 }
    421                 byte[] sendHeader = new byte[end - start];
    422                 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
    423 
    424                 mParent.sendResponse(type, sendHeader);
    425                 start = end;
    426             }
    427 
    428             if (bodyLength > 0) {
    429                 return true;
    430             } else {
    431                 return false;
    432             }
    433 
    434         } else {
    435             out.write(headerArray);
    436         }
    437 
    438         // For Get operation: if response code is OBEX_HTTP_OK, then this is the
    439         // last packet; so set finalBitSet to true.
    440         if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
    441             finalBitSet = true;
    442         }
    443 
    444         if(mSrmActive) {
    445             if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
    446                     mSrmResponseSent == true) {
    447                 // we are in the middle of a SRM PUT operation, don't send a continue.
    448                 skipSend = true;
    449             } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
    450                 // We are still receiving the get request, receive, but don't send continue.
    451                 skipSend = true;
    452             } else if(mGetOperation && mRequestFinished == true) {
    453                 // All done receiving the GET request, send data to the client, without
    454                 // expecting a continue.
    455                 skipReceive = true;
    456             }
    457             if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
    458                     + " skipReceive==" + skipReceive);
    459         }
    460         if(srmRespSendPending) {
    461             if(V)Log.v(TAG,
    462                     "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
    463             mSrmResponseSent = true;
    464         }
    465 
    466         if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
    467             if (bodyLength > 0) {
    468                 /*
    469                  * Determine if I can send the whole body or just part of
    470                  * the body.  Remember that there is the 3 bytes for the
    471                  * response message and 3 bytes for the header ID and length
    472                  */
    473                 if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
    474                     bodyLength = mMaxPacketLength - headerArray.length - 6;
    475                 }
    476 
    477                 byte[] body = mPrivateOutput.readBytes(bodyLength);
    478 
    479                 /*
    480                  * Since this is a put request if the final bit is set or
    481                  * the output stream is closed we need to send the 0x49
    482                  * (End of Body) otherwise, we need to send 0x48 (Body)
    483                  */
    484                 if ((finalBitSet) || (mPrivateOutput.isClosed())) {
    485                     if(mSendBodyHeader == true) {
    486                         out.write(0x49);
    487                         bodyLength += 3;
    488                         out.write((byte)(bodyLength >> 8));
    489                         out.write((byte)bodyLength);
    490                         out.write(body);
    491                     }
    492                 } else {
    493                     if(mSendBodyHeader == true) {
    494                     out.write(0x48);
    495                     bodyLength += 3;
    496                     out.write((byte)(bodyLength >> 8));
    497                     out.write((byte)bodyLength);
    498                     out.write(body);
    499                     }
    500                 }
    501 
    502             }
    503         }
    504 
    505         if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
    506             if(mSendBodyHeader) {
    507                 out.write(0x49);
    508                 orginalBodyLength = 3;
    509                 out.write((byte)(orginalBodyLength >> 8));
    510                 out.write((byte)orginalBodyLength);
    511             }
    512         }
    513 
    514         if(skipSend == false) {
    515             mResponseSize = 3;
    516             mParent.sendResponse(type, out.toByteArray());
    517         }
    518 
    519         if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
    520 
    521             if(mGetOperation && skipReceive) {
    522                 // Here we need to check for and handle abort (throw an exception).
    523                 // Any other signal received should be discarded silently (only on server side)
    524                 checkSrmRemoteAbort();
    525             } else {
    526                 // Receive and handle data (only send reply if !skipSend)
    527                 // Read a complete OBEX Packet
    528                 ObexPacket packet = ObexPacket.read(mInput);
    529 
    530                 int headerId = packet.mHeaderId;
    531                 if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
    532                         && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
    533                         && (headerId != ObexHelper.OBEX_OPCODE_GET)
    534                         && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
    535 
    536                     /*
    537                      * Determine if an ABORT was sent as the reply
    538                      */
    539                     if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
    540                         handleRemoteAbort();
    541                     } else {
    542                         // TODO:shall we send this if it occurs during SRM? Errata on the subject
    543                         mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
    544                         mClosed = true;
    545                         mExceptionString = "Bad Request Received";
    546                         throw new IOException("Bad Request Received");
    547                     }
    548                 } else {
    549 
    550                     if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
    551                         finalBitSet = true;
    552                     } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
    553                         mRequestFinished = true;
    554                     }
    555 
    556                     /*
    557                      * Determine if the packet length is larger than the negotiated packet size
    558                      */
    559                     if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
    560                         mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
    561                         throw new IOException("Packet received was too large");
    562                     }
    563 
    564                     /*
    565                      * Determine if any headers were sent in the initial request
    566                      */
    567                     if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
    568                         if(handleObexPacket(packet) == false) {
    569                             return false;
    570                         }
    571                     }
    572                 }
    573 
    574             }
    575             return true;
    576         } else {
    577             return false;
    578         }
    579     }
    580 
    581     /**
    582      * This method will look for an abort from the peer during a SRM transfer.
    583      * The function will not block if no data has been received from the remote device.
    584      * If data have been received, the function will block while reading the incoming
    585      * OBEX package.
    586      * An Abort request will be handled, and cause an IOException("Abort Received").
    587      * Other messages will be discarded silently as per GOEP specification.
    588      * @throws IOException if an abort request have been received.
    589      * TODO: I think this is an error in the specification. If we discard other messages,
    590      *       the peer device will most likely stall, as it will not receive the expected
    591      *       response for the message...
    592      *       I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
    593      *       header values shall be ignored by the receiving device."
    594      *       If any signal is received during an active SRM transfer it is unexpected regardless
    595      *       whether or not it contains SRM/SRMP headers...
    596      */
    597     private void checkSrmRemoteAbort() throws IOException {
    598         if(mInput.available() > 0) {
    599             ObexPacket packet = ObexPacket.read(mInput);
    600             /*
    601              * Determine if an ABORT was sent as the reply
    602              */
    603             if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
    604                 handleRemoteAbort();
    605             } else {
    606                 // TODO: should we throw an exception here anyway? - don't see how to
    607                 //       ignore SRM/SRMP headers without ignoring the complete signal
    608                 //       (in this particular case).
    609                 Log.w(TAG, "Received unexpected request from client - discarding...\n"
    610                         + "   headerId: " + packet.mHeaderId + " length: " + packet.mLength);
    611             }
    612         }
    613     }
    614 
    615     private void handleRemoteAbort() throws IOException {
    616         /* TODO: To increase the speed of the abort operation in SRM, we need
    617          *       to be able to flush the L2CAP queue for the PSM in use.
    618          *       This could be implemented by introducing a control
    619          *       message to be send over the socket, that in the abort case
    620          *       could carry a flush command. */
    621         mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
    622         mClosed = true;
    623         isAborted = true;
    624         mExceptionString = "Abort Received";
    625         throw new IOException("Abort Received");
    626     }
    627 
    628     /**
    629      * Sends an ABORT message to the server. By calling this method, the
    630      * corresponding input and output streams will be closed along with this
    631      * object.
    632      * @throws IOException if the transaction has already ended or if an OBEX
    633      *         server called this method
    634      */
    635     public void abort() throws IOException {
    636         throw new IOException("Called from a server");
    637     }
    638 
    639     /**
    640      * Returns the headers that have been received during the operation.
    641      * Modifying the object returned has no effect on the headers that are sent
    642      * or retrieved.
    643      * @return the headers received during this <code>Operation</code>
    644      * @throws IOException if this <code>Operation</code> has been closed
    645      */
    646     public HeaderSet getReceivedHeader() throws IOException {
    647         ensureOpen();
    648         return requestHeader;
    649     }
    650 
    651     /**
    652      * Specifies the headers that should be sent in the next OBEX message that
    653      * is sent.
    654      * @param headers the headers to send in the next message
    655      * @throws IOException if this <code>Operation</code> has been closed or the
    656      *         transaction has ended and no further messages will be exchanged
    657      * @throws IllegalArgumentException if <code>headers</code> was not created
    658      *         by a call to <code>ServerRequestHandler.createHeaderSet()</code>
    659      */
    660     public void sendHeaders(HeaderSet headers) throws IOException {
    661         ensureOpen();
    662 
    663         if (headers == null) {
    664             throw new IOException("Headers may not be null");
    665         }
    666 
    667         int[] headerList = headers.getHeaderList();
    668         if (headerList != null) {
    669             for (int i = 0; i < headerList.length; i++) {
    670                 replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
    671             }
    672 
    673         }
    674     }
    675 
    676     /**
    677      * Retrieves the response code retrieved from the server. Response codes are
    678      * defined in the <code>ResponseCodes</code> interface.
    679      * @return the response code retrieved from the server
    680      * @throws IOException if an error occurred in the transport layer during
    681      *         the transaction; if this method is called on a
    682      *         <code>HeaderSet</code> object created by calling
    683      *         <code>createHeaderSet</code> in a <code>ClientSession</code>
    684      *         object; if this is called from a server
    685      */
    686     public int getResponseCode() throws IOException {
    687         throw new IOException("Called from a server");
    688     }
    689 
    690     /**
    691      * Always returns <code>null</code>
    692      * @return <code>null</code>
    693      */
    694     public String getEncoding() {
    695         return null;
    696     }
    697 
    698     /**
    699      * Returns the type of content that the resource connected to is providing.
    700      * E.g. if the connection is via HTTP, then the value of the content-type
    701      * header field is returned.
    702      * @return the content type of the resource that the URL references, or
    703      *         <code>null</code> if not known
    704      */
    705     public String getType() {
    706         try {
    707             return (String)requestHeader.getHeader(HeaderSet.TYPE);
    708         } catch (IOException e) {
    709             return null;
    710         }
    711     }
    712 
    713     /**
    714      * Returns the length of the content which is being provided. E.g. if the
    715      * connection is via HTTP, then the value of the content-length header field
    716      * is returned.
    717      * @return the content length of the resource that this connection's URL
    718      *         references, or -1 if the content length is not known
    719      */
    720     public long getLength() {
    721         try {
    722             Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
    723 
    724             if (temp == null) {
    725                 return -1;
    726             } else {
    727                 return temp.longValue();
    728             }
    729         } catch (IOException e) {
    730             return -1;
    731         }
    732     }
    733 
    734     public int getMaxPacketSize() {
    735         return mMaxPacketLength - 6 - getHeaderLength();
    736     }
    737 
    738     public int getHeaderLength() {
    739         long id = mListener.getConnectionId();
    740         if (id == -1) {
    741             replyHeader.mConnectionID = null;
    742         } else {
    743             replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
    744         }
    745 
    746         byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
    747 
    748         return headerArray.length;
    749     }
    750 
    751     /**
    752      * Open and return an input stream for a connection.
    753      * @return an input stream
    754      * @throws IOException if an I/O error occurs
    755      */
    756     public InputStream openInputStream() throws IOException {
    757         ensureOpen();
    758         return mPrivateInput;
    759     }
    760 
    761     /**
    762      * Open and return a data input stream for a connection.
    763      * @return an input stream
    764      * @throws IOException if an I/O error occurs
    765      */
    766     public DataInputStream openDataInputStream() throws IOException {
    767         return new DataInputStream(openInputStream());
    768     }
    769 
    770     /**
    771      * Open and return an output stream for a connection.
    772      * @return an output stream
    773      * @throws IOException if an I/O error occurs
    774      */
    775     public OutputStream openOutputStream() throws IOException {
    776         ensureOpen();
    777 
    778         if (mPrivateOutputOpen) {
    779             throw new IOException("no more input streams available, stream already opened");
    780         }
    781 
    782         if (!mRequestFinished) {
    783             throw new IOException("no  output streams available ,request not finished");
    784         }
    785 
    786         if (mPrivateOutput == null) {
    787             mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
    788         }
    789         mPrivateOutputOpen = true;
    790         return mPrivateOutput;
    791     }
    792 
    793     /**
    794      * Open and return a data output stream for a connection.
    795      * @return an output stream
    796      * @throws IOException if an I/O error occurs
    797      */
    798     public DataOutputStream openDataOutputStream() throws IOException {
    799         return new DataOutputStream(openOutputStream());
    800     }
    801 
    802     /**
    803      * Closes the connection and ends the transaction
    804      * @throws IOException if the operation has already ended or is closed
    805      */
    806     public void close() throws IOException {
    807         ensureOpen();
    808         mClosed = true;
    809     }
    810 
    811     /**
    812      * Verifies that the connection is open and no exceptions should be thrown.
    813      * @throws IOException if an exception needs to be thrown
    814      */
    815     public void ensureOpen() throws IOException {
    816         if (mExceptionString != null) {
    817             throw new IOException(mExceptionString);
    818         }
    819         if (mClosed) {
    820             throw new IOException("Operation has already ended");
    821         }
    822     }
    823 
    824     /**
    825      * Verifies that additional information may be sent. In other words, the
    826      * operation is not done.
    827      * <P>
    828      * Included to implement the BaseStream interface only. It does not do
    829      * anything on the server side since the operation of the Operation object
    830      * is not done until after the handler returns from its method.
    831      * @throws IOException if the operation is completed
    832      */
    833     public void ensureNotDone() throws IOException {
    834     }
    835 
    836     /**
    837      * Called when the output or input stream is closed. It does not do anything
    838      * on the server side since the operation of the Operation object is not
    839      * done until after the handler returns from its method.
    840      * @param inStream <code>true</code> if the input stream is closed;
    841      *        <code>false</code> if the output stream is closed
    842      * @throws IOException if an IO error occurs
    843      */
    844     public void streamClosed(boolean inStream) throws IOException {
    845 
    846     }
    847 
    848     public void noBodyHeader(){
    849         mSendBodyHeader = false;
    850     }
    851 }
    852