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 java.io.IOException;
     38 import java.io.InputStream;
     39 import java.io.OutputStream;
     40 import java.io.DataInputStream;
     41 import java.io.DataOutputStream;
     42 import java.io.ByteArrayOutputStream;
     43 
     44 import android.util.Log;
     45 
     46 /**
     47  * This class implements the <code>Operation</code> interface. It will read and
     48  * write data via puts and gets.
     49  * @hide
     50  */
     51 public final class ClientOperation implements Operation, BaseStream {
     52 
     53     private static final String TAG = "ClientOperation";
     54 
     55     private static final boolean V = ObexHelper.VDBG;
     56 
     57     private ClientSession mParent;
     58 
     59     private boolean mInputOpen;
     60 
     61     private PrivateInputStream mPrivateInput;
     62 
     63     private boolean mPrivateInputOpen;
     64 
     65     private PrivateOutputStream mPrivateOutput;
     66 
     67     private boolean mPrivateOutputOpen;
     68 
     69     private String mExceptionMessage;
     70 
     71     private int mMaxPacketSize;
     72 
     73     private boolean mOperationDone;
     74 
     75     private boolean mGetOperation;
     76 
     77     private boolean mGetFinalFlag;
     78 
     79     private HeaderSet mRequestHeader;
     80 
     81     private HeaderSet mReplyHeader;
     82 
     83     private boolean mEndOfBodySent;
     84 
     85     private boolean mSendBodyHeader = true;
     86     // A latch - when triggered, there is not way back ;-)
     87     private boolean mSrmActive = false;
     88 
     89     // Assume SRM disabled - until support is confirmed
     90     // by the server
     91     private boolean mSrmEnabled = false;
     92     // keep waiting until final-bit is received in request
     93     // to handle the case where the SRM enable header is in
     94     // a different OBEX packet than the SRMP header.
     95     private boolean mSrmWaitingForRemote = true;
     96 
     97 
     98     /**
     99      * Creates new OperationImpl to read and write data to a server
    100      * @param maxSize the maximum packet size
    101      * @param p the parent to this object
    102      * @param type <code>true</code> if this is a get request;
    103      *        <code>false</code. if this is a put request
    104      * @param header the header to set in the initial request
    105      * @throws IOException if the an IO error occurred
    106      */
    107     public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
    108             throws IOException {
    109 
    110         mParent = p;
    111         mEndOfBodySent = false;
    112         mInputOpen = true;
    113         mOperationDone = false;
    114         mMaxPacketSize = maxSize;
    115         mGetOperation = type;
    116         mGetFinalFlag = false;
    117 
    118         mPrivateInputOpen = false;
    119         mPrivateOutputOpen = false;
    120         mPrivateInput = null;
    121         mPrivateOutput = null;
    122 
    123         mReplyHeader = new HeaderSet();
    124 
    125         mRequestHeader = new HeaderSet();
    126 
    127         int[] headerList = header.getHeaderList();
    128 
    129         if (headerList != null) {
    130 
    131             for (int i = 0; i < headerList.length; i++) {
    132                 mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
    133             }
    134         }
    135 
    136         if ((header).mAuthChall != null) {
    137             mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
    138             System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
    139                     (header).mAuthChall.length);
    140         }
    141 
    142         if ((header).mAuthResp != null) {
    143             mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
    144             System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
    145                     (header).mAuthResp.length);
    146 
    147         }
    148 
    149         if ((header).mConnectionID != null) {
    150             mRequestHeader.mConnectionID = new byte[4];
    151             System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0,
    152                     4);
    153 
    154         }
    155     }
    156 
    157     /**
    158      * Allows to set flag which will force GET to be always sent as single packet request with
    159      * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
    160      * require requests to be sent this way.
    161      */
    162     public void setGetFinalFlag(boolean flag) {
    163         mGetFinalFlag = flag;
    164     }
    165 
    166     /**
    167      * Sends an ABORT message to the server. By calling this method, the
    168      * corresponding input and output streams will be closed along with this
    169      * object.
    170      * @throws IOException if the transaction has already ended or if an OBEX
    171      *         server called this method
    172      */
    173     public synchronized void abort() throws IOException {
    174         ensureOpen();
    175         //no compatible with sun-ri
    176         if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
    177             throw new IOException("Operation has already ended");
    178         }
    179 
    180         mExceptionMessage = "Operation aborted";
    181         if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    182             mOperationDone = true;
    183             /*
    184              * Since we are not sending any headers or returning any headers then
    185              * we just need to write and read the same bytes
    186              */
    187             mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
    188 
    189             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
    190                 throw new IOException("Invalid response code from server");
    191             }
    192 
    193             mExceptionMessage = null;
    194         }
    195 
    196         close();
    197     }
    198 
    199     /**
    200      * Retrieves the response code retrieved from the server. Response codes are
    201      * defined in the <code>ResponseCodes</code> interface.
    202      * @return the response code retrieved from the server
    203      * @throws IOException if an error occurred in the transport layer during
    204      *         the transaction; if this method is called on a
    205      *         <code>HeaderSet</code> object created by calling
    206      *         <code>createHeaderSet</code> in a <code>ClientSession</code>
    207      *         object
    208      */
    209     public synchronized int getResponseCode() throws IOException {
    210         //avoid dup validateConnection
    211         if ((mReplyHeader.responseCode == -1)
    212                 || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    213             validateConnection();
    214         }
    215 
    216         return mReplyHeader.responseCode;
    217     }
    218 
    219     /**
    220      * This method will always return <code>null</code>
    221      * @return <code>null</code>
    222      */
    223     public String getEncoding() {
    224         return null;
    225     }
    226 
    227     /**
    228      * Returns the type of content that the resource connected to is providing.
    229      * E.g. if the connection is via HTTP, then the value of the content-type
    230      * header field is returned.
    231      * @return the content type of the resource that the URL references, or
    232      *         <code>null</code> if not known
    233      */
    234     public String getType() {
    235         try {
    236             return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
    237         } catch (IOException e) {
    238             if(V) Log.d(TAG, "Exception occured - returning null",e);
    239             return null;
    240         }
    241     }
    242 
    243     /**
    244      * Returns the length of the content which is being provided. E.g. if the
    245      * connection is via HTTP, then the value of the content-length header field
    246      * is returned.
    247      * @return the content length of the resource that this connection's URL
    248      *         references, or -1 if the content length is not known
    249      */
    250     public long getLength() {
    251         try {
    252             Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
    253 
    254             if (temp == null) {
    255                 return -1;
    256             } else {
    257                 return temp.longValue();
    258             }
    259         } catch (IOException e) {
    260             if(V) Log.d(TAG,"Exception occured - returning -1",e);
    261             return -1;
    262         }
    263     }
    264 
    265     /**
    266      * Open and return an input stream for a connection.
    267      * @return an input stream
    268      * @throws IOException if an I/O error occurs
    269      */
    270     public InputStream openInputStream() throws IOException {
    271 
    272         ensureOpen();
    273 
    274         if (mPrivateInputOpen)
    275             throw new IOException("no more input streams available");
    276         if (mGetOperation) {
    277             // send the GET request here
    278             validateConnection();
    279         } else {
    280             if (mPrivateInput == null) {
    281                 mPrivateInput = new PrivateInputStream(this);
    282             }
    283         }
    284 
    285         mPrivateInputOpen = true;
    286 
    287         return mPrivateInput;
    288     }
    289 
    290     /**
    291      * Open and return a data input stream for a connection.
    292      * @return an input stream
    293      * @throws IOException if an I/O error occurs
    294      */
    295     public DataInputStream openDataInputStream() throws IOException {
    296         return new DataInputStream(openInputStream());
    297     }
    298 
    299     /**
    300      * Open and return an output stream for a connection.
    301      * @return an output stream
    302      * @throws IOException if an I/O error occurs
    303      */
    304     public OutputStream openOutputStream() throws IOException {
    305 
    306         ensureOpen();
    307         ensureNotDone();
    308 
    309         if (mPrivateOutputOpen)
    310             throw new IOException("no more output streams available");
    311 
    312         if (mPrivateOutput == null) {
    313             // there are 3 bytes operation headers and 3 bytes body headers //
    314             mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
    315         }
    316 
    317         mPrivateOutputOpen = true;
    318 
    319         return mPrivateOutput;
    320     }
    321 
    322     public int getMaxPacketSize() {
    323         return mMaxPacketSize - 6 - getHeaderLength();
    324     }
    325 
    326     public int getHeaderLength() {
    327         // OPP may need it
    328         byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
    329         return headerArray.length;
    330     }
    331 
    332     /**
    333      * Open and return a data output stream for a connection.
    334      * @return an output stream
    335      * @throws IOException if an I/O error occurs
    336      */
    337     public DataOutputStream openDataOutputStream() throws IOException {
    338         return new DataOutputStream(openOutputStream());
    339     }
    340 
    341     /**
    342      * Closes the connection and ends the transaction
    343      * @throws IOException if the operation has already ended or is closed
    344      */
    345     public void close() throws IOException {
    346         mInputOpen = false;
    347         mPrivateInputOpen = false;
    348         mPrivateOutputOpen = false;
    349         mParent.setRequestInactive();
    350     }
    351 
    352     /**
    353      * Returns the headers that have been received during the operation.
    354      * Modifying the object returned has no effect on the headers that are sent
    355      * or retrieved.
    356      * @return the headers received during this <code>Operation</code>
    357      * @throws IOException if this <code>Operation</code> has been closed
    358      */
    359     public HeaderSet getReceivedHeader() throws IOException {
    360         ensureOpen();
    361 
    362         return mReplyHeader;
    363     }
    364 
    365     /**
    366      * Specifies the headers that should be sent in the next OBEX message that
    367      * is sent.
    368      * @param headers the headers to send in the next message
    369      * @throws IOException if this <code>Operation</code> has been closed or the
    370      *         transaction has ended and no further messages will be exchanged
    371      * @throws IllegalArgumentException if <code>headers</code> was not created
    372      *         by a call to <code>ServerRequestHandler.createHeaderSet()</code>
    373      * @throws NullPointerException if <code>headers</code> is <code>null</code>
    374      */
    375     public void sendHeaders(HeaderSet headers) throws IOException {
    376         ensureOpen();
    377         if (mOperationDone) {
    378             throw new IOException("Operation has already exchanged all data");
    379         }
    380 
    381         if (headers == null) {
    382             throw new IOException("Headers may not be null");
    383         }
    384 
    385         int[] headerList = headers.getHeaderList();
    386         if (headerList != null) {
    387             for (int i = 0; i < headerList.length; i++) {
    388                 mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
    389             }
    390         }
    391     }
    392 
    393     /**
    394      * Verifies that additional information may be sent. In other words, the
    395      * operation is not done.
    396      * @throws IOException if the operation is completed
    397      */
    398     public void ensureNotDone() throws IOException {
    399         if (mOperationDone) {
    400             throw new IOException("Operation has completed");
    401         }
    402     }
    403 
    404     /**
    405      * Verifies that the connection is open and no exceptions should be thrown.
    406      * @throws IOException if an exception needs to be thrown
    407      */
    408     public void ensureOpen() throws IOException {
    409         mParent.ensureOpen();
    410 
    411         if (mExceptionMessage != null) {
    412             throw new IOException(mExceptionMessage);
    413         }
    414         if (!mInputOpen) {
    415             throw new IOException("Operation has already ended");
    416         }
    417     }
    418 
    419     /**
    420      * Verifies that the connection is open and the proper data has been read.
    421      * @throws IOException if an IO error occurs
    422      */
    423     private void validateConnection() throws IOException {
    424         ensureOpen();
    425 
    426         // to sure only one privateInput object exist.
    427         if (mPrivateInput == null) {
    428             startProcessing();
    429         }
    430     }
    431 
    432     /**
    433      * Sends a request to the client of the specified type.
    434      * This function will enable SRM and set SRM active if the server
    435      * response allows this.
    436      * @param opCode the request code to send to the client
    437      * @return <code>true</code> if there is more data to send;
    438      *         <code>false</code> if there is no more data to send
    439      * @throws IOException if an IO error occurs
    440      */
    441     private boolean sendRequest(int opCode) throws IOException {
    442         boolean returnValue = false;
    443         ByteArrayOutputStream out = new ByteArrayOutputStream();
    444         int bodyLength = -1;
    445         byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
    446         if (mPrivateOutput != null) {
    447             bodyLength = mPrivateOutput.size();
    448         }
    449 
    450         /*
    451          * Determine if there is space to add a body request.  At present
    452          * this method checks to see if there is room for at least a 17
    453          * byte body header.  This number needs to be at least 6 so that
    454          * there is room for the header ID and length and the reply ID and
    455          * length, but it is a waste of resources if we can't send much of
    456          * the body.
    457          */
    458         final int MINIMUM_BODY_LENGTH = 3;
    459         if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
    460                 > mMaxPacketSize) {
    461             int end = 0;
    462             int start = 0;
    463             // split & send the headerArray in multiple packets.
    464 
    465             while (end != headerArray.length) {
    466                 //split the headerArray
    467 
    468                 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
    469                         - ObexHelper.BASE_PACKET_LENGTH);
    470                 // can not split
    471                 if (end == -1) {
    472                     mOperationDone = true;
    473                     abort();
    474                     mExceptionMessage = "Header larger then can be sent in a packet";
    475                     mInputOpen = false;
    476 
    477                     if (mPrivateInput != null) {
    478                         mPrivateInput.close();
    479                     }
    480 
    481                     if (mPrivateOutput != null) {
    482                         mPrivateOutput.close();
    483                     }
    484                     throw new IOException("OBEX Packet exceeds max packet size");
    485                 }
    486 
    487                 byte[] sendHeader = new byte[end - start];
    488                 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
    489                 if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
    490                     return false;
    491                 }
    492 
    493                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    494                     return false;
    495                 }
    496 
    497                 start = end;
    498             }
    499 
    500             // Enable SRM if it should be enabled
    501             checkForSrm();
    502 
    503             if (bodyLength > 0) {
    504                 return true;
    505             } else {
    506                 return false;
    507             }
    508         } else {
    509             /* All headers will fit into a single package */
    510             if(mSendBodyHeader == false) {
    511                 /* As we are not to send any body data, set the FINAL_BIT */
    512                 opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
    513             }
    514             out.write(headerArray);
    515         }
    516 
    517         if (bodyLength > 0) {
    518             /*
    519              * Determine if we can send the whole body or just part of
    520              * the body.  Remember that there is the 3 bytes for the
    521              * response message and 3 bytes for the header ID and length
    522              */
    523             if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
    524                 returnValue = true;
    525 
    526                 bodyLength = mMaxPacketSize - headerArray.length - 6;
    527             }
    528 
    529             byte[] body = mPrivateOutput.readBytes(bodyLength);
    530 
    531             /*
    532              * Since this is a put request if the final bit is set or
    533              * the output stream is closed we need to send the 0x49
    534              * (End of Body) otherwise, we need to send 0x48 (Body)
    535              */
    536             if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
    537                     && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
    538                 out.write(HeaderSet.END_OF_BODY);
    539                 mEndOfBodySent = true;
    540             } else {
    541                 out.write(HeaderSet.BODY);
    542             }
    543 
    544             bodyLength += 3;
    545             out.write((byte)(bodyLength >> 8));
    546             out.write((byte)bodyLength);
    547 
    548             if (body != null) {
    549                 out.write(body);
    550             }
    551         }
    552 
    553         if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
    554             // only 0x82 or 0x83 can send 0x49
    555             if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
    556                 out.write(HeaderSet.BODY);
    557             } else {
    558                 out.write(HeaderSet.END_OF_BODY);
    559                 mEndOfBodySent = true;
    560             }
    561 
    562             bodyLength = 3;
    563             out.write((byte)(bodyLength >> 8));
    564             out.write((byte)bodyLength);
    565         }
    566 
    567         if (out.size() == 0) {
    568             if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
    569                 return false;
    570             }
    571             // Enable SRM if it should be enabled
    572             checkForSrm();
    573             return returnValue;
    574         }
    575         if ((out.size() > 0)
    576                 && (!mParent.sendRequest(opCode, out.toByteArray(),
    577                         mReplyHeader, mPrivateInput, mSrmActive))) {
    578             return false;
    579         }
    580         // Enable SRM if it should be enabled
    581         checkForSrm();
    582 
    583         // send all of the output data in 0x48,
    584         // send 0x49 with empty body
    585         if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
    586             returnValue = true;
    587 
    588         return returnValue;
    589     }
    590 
    591     private void checkForSrm() throws IOException {
    592         Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
    593         if(mParent.isSrmSupported() == true && srmMode != null
    594                 && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
    595             mSrmEnabled = true;
    596         }
    597         /**
    598          * Call this only when a complete obex packet have been received.
    599          * (This is not optimal, but the current design is not really suited to
    600          * the way SRM is specified.)
    601          * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
    602          * into every OBEX packet, hence if another header occupies the entire packet,
    603          * the scheme will not work - unlikely though.
    604          */
    605         if(mSrmEnabled) {
    606             mSrmWaitingForRemote = false;
    607             Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
    608             if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
    609                 mSrmWaitingForRemote = true;
    610                 // Clear the wait header, as the absence of the header in the next packet
    611                 // indicates don't wait anymore.
    612                 mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
    613             }
    614         }
    615         if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
    616             mSrmActive = true;
    617         }
    618     }
    619 
    620     /**
    621      * This method starts the processing thread results. It will send the
    622      * initial request. If the response takes more then one packet, a thread
    623      * will be started to handle additional requests
    624      * @throws IOException if an IO error occurs
    625      */
    626     private synchronized void startProcessing() throws IOException {
    627 
    628         if (mPrivateInput == null) {
    629             mPrivateInput = new PrivateInputStream(this);
    630         }
    631         boolean more = true;
    632 
    633         if (mGetOperation) {
    634             if (!mOperationDone) {
    635                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    636                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    637                     more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
    638                 }
    639                 // For GET we need to loop until all headers have been sent,
    640                 // And then we wait for the first continue package with the
    641                 // reply.
    642                 if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
    643                     mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
    644                             null, mReplyHeader, mPrivateInput, mSrmActive);
    645                 }
    646                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    647                     mOperationDone = true;
    648                 } else {
    649                     checkForSrm();
    650                 }
    651             }
    652         } else {
    653             // PUT operation
    654             if (!mOperationDone) {
    655                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    656                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    657                     more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
    658                 }
    659             }
    660 
    661             if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
    662                 mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
    663                         null, mReplyHeader, mPrivateInput, mSrmActive);
    664             }
    665 
    666             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    667                 mOperationDone = true;
    668             }
    669         }
    670     }
    671 
    672     /**
    673      * Continues the operation since there is no data to read.
    674      * @param sendEmpty <code>true</code> if the operation should send an empty
    675      *        packet or not send anything if there is no data to send
    676      * @param inStream <code>true</code> if the stream is input stream or is
    677      *        output stream
    678      * @throws IOException if an IO error occurs
    679      */
    680     public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
    681             throws IOException {
    682 
    683         // One path to the first put operation - the other one does not need to
    684         // handle SRM, as all will fit into one packet.
    685 
    686         if (mGetOperation) {
    687             if ((inStream) && (!mOperationDone)) {
    688                 // to deal with inputstream in get operation
    689                 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
    690                         null, mReplyHeader, mPrivateInput, mSrmActive);
    691                 /*
    692                   * Determine if that was not the last packet in the operation
    693                   */
    694                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    695                     mOperationDone = true;
    696                 } else {
    697                     checkForSrm();
    698                 }
    699 
    700                 return true;
    701 
    702             } else if ((!inStream) && (!mOperationDone)) {
    703                 // to deal with outputstream in get operation
    704 
    705                 if (mPrivateInput == null) {
    706                     mPrivateInput = new PrivateInputStream(this);
    707                 }
    708                 sendRequest(ObexHelper.OBEX_OPCODE_GET);
    709                 return true;
    710 
    711             } else if (mOperationDone) {
    712                 return false;
    713             }
    714 
    715         } else {
    716             // PUT operation
    717             if ((!inStream) && (!mOperationDone)) {
    718                 // to deal with outputstream in put operation
    719                 if (mReplyHeader.responseCode == -1) {
    720                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    721                 }
    722                 sendRequest(ObexHelper.OBEX_OPCODE_PUT);
    723                 return true;
    724             } else if ((inStream) && (!mOperationDone)) {
    725                 // How to deal with inputstream  in put operation ?
    726                 return false;
    727 
    728             } else if (mOperationDone) {
    729                 return false;
    730             }
    731 
    732         }
    733         return false;
    734     }
    735 
    736     /**
    737      * Called when the output or input stream is closed.
    738      * @param inStream <code>true</code> if the input stream is closed;
    739      *        <code>false</code> if the output stream is closed
    740      * @throws IOException if an IO error occurs
    741      */
    742     public void streamClosed(boolean inStream) throws IOException {
    743         if (!mGetOperation) {
    744             if ((!inStream) && (!mOperationDone)) {
    745                 // to deal with outputstream in put operation
    746 
    747                 boolean more = true;
    748 
    749                 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
    750                     byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
    751                     if (headerArray.length <= 0)
    752                         more = false;
    753                 }
    754                 // If have not sent any data so send  all now
    755                 if (mReplyHeader.responseCode == -1) {
    756                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    757                 }
    758 
    759                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    760                     more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
    761                 }
    762 
    763                 /*
    764                  * According to the IrOBEX specification, after the final put, you
    765                  * only have a single reply to send.  so we don't need the while
    766                  * loop.
    767                  */
    768                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
    769 
    770                     sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
    771                 }
    772                 mOperationDone = true;
    773             } else if ((inStream) && (mOperationDone)) {
    774                 // how to deal with input stream in put stream ?
    775                 mOperationDone = true;
    776             }
    777         } else {
    778             if ((inStream) && (!mOperationDone)) {
    779 
    780                 // to deal with inputstream in get operation
    781                 // Have not sent any data so send it all now
    782 
    783                 if (mReplyHeader.responseCode == -1) {
    784                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    785                 }
    786 
    787                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
    788                     if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
    789                         break;
    790                     }
    791                 }
    792                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
    793                     mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
    794                             mReplyHeader, mPrivateInput, false);
    795                     // Regardless of the SRM state, wait for the response.
    796                 }
    797                 mOperationDone = true;
    798             } else if ((!inStream) && (!mOperationDone)) {
    799                 // to deal with outputstream in get operation
    800                 // part of the data may have been sent in continueOperation.
    801 
    802                 boolean more = true;
    803 
    804                 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
    805                     byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
    806                     if (headerArray.length <= 0)
    807                         more = false;
    808                 }
    809 
    810                 if (mPrivateInput == null) {
    811                     mPrivateInput = new PrivateInputStream(this);
    812                 }
    813                 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
    814                     more = false;
    815 
    816                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    817                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    818                     more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
    819                 }
    820                 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
    821                 //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
    822                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    823                     mOperationDone = true;
    824                 }
    825             }
    826         }
    827     }
    828 
    829     public void noBodyHeader(){
    830         mSendBodyHeader = false;
    831     }
    832 }
    833