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         if ((mReplyHeader.responseCode == -1)
    211                 || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    212             validateConnection();
    213         }
    214 
    215         return mReplyHeader.responseCode;
    216     }
    217 
    218     /**
    219      * This method will always return <code>null</code>
    220      * @return <code>null</code>
    221      */
    222     public String getEncoding() {
    223         return null;
    224     }
    225 
    226     /**
    227      * Returns the type of content that the resource connected to is providing.
    228      * E.g. if the connection is via HTTP, then the value of the content-type
    229      * header field is returned.
    230      * @return the content type of the resource that the URL references, or
    231      *         <code>null</code> if not known
    232      */
    233     public String getType() {
    234         try {
    235             return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
    236         } catch (IOException e) {
    237             if(V) Log.d(TAG, "Exception occured - returning null",e);
    238             return null;
    239         }
    240     }
    241 
    242     /**
    243      * Returns the length of the content which is being provided. E.g. if the
    244      * connection is via HTTP, then the value of the content-length header field
    245      * is returned.
    246      * @return the content length of the resource that this connection's URL
    247      *         references, or -1 if the content length is not known
    248      */
    249     public long getLength() {
    250         try {
    251             Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
    252 
    253             if (temp == null) {
    254                 return -1;
    255             } else {
    256                 return temp.longValue();
    257             }
    258         } catch (IOException e) {
    259             if(V) Log.d(TAG,"Exception occured - returning -1",e);
    260             return -1;
    261         }
    262     }
    263 
    264     /**
    265      * Open and return an input stream for a connection.
    266      * @return an input stream
    267      * @throws IOException if an I/O error occurs
    268      */
    269     public InputStream openInputStream() throws IOException {
    270 
    271         ensureOpen();
    272 
    273         if (mPrivateInputOpen)
    274             throw new IOException("no more input streams available");
    275         if (mGetOperation) {
    276             // send the GET request here
    277             validateConnection();
    278         } else {
    279             if (mPrivateInput == null) {
    280                 mPrivateInput = new PrivateInputStream(this);
    281             }
    282         }
    283 
    284         mPrivateInputOpen = true;
    285 
    286         return mPrivateInput;
    287     }
    288 
    289     /**
    290      * Open and return a data input stream for a connection.
    291      * @return an input stream
    292      * @throws IOException if an I/O error occurs
    293      */
    294     public DataInputStream openDataInputStream() throws IOException {
    295         return new DataInputStream(openInputStream());
    296     }
    297 
    298     /**
    299      * Open and return an output stream for a connection.
    300      * @return an output stream
    301      * @throws IOException if an I/O error occurs
    302      */
    303     public OutputStream openOutputStream() throws IOException {
    304 
    305         ensureOpen();
    306         ensureNotDone();
    307 
    308         if (mPrivateOutputOpen)
    309             throw new IOException("no more output streams available");
    310 
    311         if (mPrivateOutput == null) {
    312             // there are 3 bytes operation headers and 3 bytes body headers //
    313             mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
    314         }
    315 
    316         mPrivateOutputOpen = true;
    317 
    318         return mPrivateOutput;
    319     }
    320 
    321     public int getMaxPacketSize() {
    322         return mMaxPacketSize - 6 - getHeaderLength();
    323     }
    324 
    325     public int getHeaderLength() {
    326         // OPP may need it
    327         byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
    328         return headerArray.length;
    329     }
    330 
    331     /**
    332      * Open and return a data output stream for a connection.
    333      * @return an output stream
    334      * @throws IOException if an I/O error occurs
    335      */
    336     public DataOutputStream openDataOutputStream() throws IOException {
    337         return new DataOutputStream(openOutputStream());
    338     }
    339 
    340     /**
    341      * Closes the connection and ends the transaction
    342      * @throws IOException if the operation has already ended or is closed
    343      */
    344     public void close() throws IOException {
    345         mInputOpen = false;
    346         mPrivateInputOpen = false;
    347         mPrivateOutputOpen = false;
    348         mParent.setRequestInactive();
    349     }
    350 
    351     /**
    352      * Returns the headers that have been received during the operation.
    353      * Modifying the object returned has no effect on the headers that are sent
    354      * or retrieved.
    355      * @return the headers received during this <code>Operation</code>
    356      * @throws IOException if this <code>Operation</code> has been closed
    357      */
    358     public HeaderSet getReceivedHeader() throws IOException {
    359         ensureOpen();
    360 
    361         return mReplyHeader;
    362     }
    363 
    364     /**
    365      * Specifies the headers that should be sent in the next OBEX message that
    366      * is sent.
    367      * @param headers the headers to send in the next message
    368      * @throws IOException if this <code>Operation</code> has been closed or the
    369      *         transaction has ended and no further messages will be exchanged
    370      * @throws IllegalArgumentException if <code>headers</code> was not created
    371      *         by a call to <code>ServerRequestHandler.createHeaderSet()</code>
    372      * @throws NullPointerException if <code>headers</code> is <code>null</code>
    373      */
    374     public void sendHeaders(HeaderSet headers) throws IOException {
    375         ensureOpen();
    376         if (mOperationDone) {
    377             throw new IOException("Operation has already exchanged all data");
    378         }
    379 
    380         if (headers == null) {
    381             throw new IOException("Headers may not be null");
    382         }
    383 
    384         int[] headerList = headers.getHeaderList();
    385         if (headerList != null) {
    386             for (int i = 0; i < headerList.length; i++) {
    387                 mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
    388             }
    389         }
    390     }
    391 
    392     /**
    393      * Verifies that additional information may be sent. In other words, the
    394      * operation is not done.
    395      * @throws IOException if the operation is completed
    396      */
    397     public void ensureNotDone() throws IOException {
    398         if (mOperationDone) {
    399             throw new IOException("Operation has completed");
    400         }
    401     }
    402 
    403     /**
    404      * Verifies that the connection is open and no exceptions should be thrown.
    405      * @throws IOException if an exception needs to be thrown
    406      */
    407     public void ensureOpen() throws IOException {
    408         mParent.ensureOpen();
    409 
    410         if (mExceptionMessage != null) {
    411             throw new IOException(mExceptionMessage);
    412         }
    413         if (!mInputOpen) {
    414             throw new IOException("Operation has already ended");
    415         }
    416     }
    417 
    418     /**
    419      * Verifies that the connection is open and the proper data has been read.
    420      * @throws IOException if an IO error occurs
    421      */
    422     private void validateConnection() throws IOException {
    423         ensureOpen();
    424 
    425         // Make sure that a response has been recieved from remote
    426         // before continuing
    427         if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
    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                 if (!mGetFinalFlag) {
    636                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    637                     while ((more) && (mReplyHeader.responseCode ==
    638                             ResponseCodes.OBEX_HTTP_CONTINUE)) {
    639                         more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
    640                     }
    641                     // For GET we need to loop until all headers have been sent,
    642                     // And then we wait for the first continue package with the
    643                     // reply.
    644                     if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
    645                         mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
    646                                 null, mReplyHeader, mPrivateInput, mSrmActive);
    647                     }
    648                     if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    649                         mOperationDone = true;
    650                     } else {
    651                         checkForSrm();
    652                     }
    653                 } else {
    654                     more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
    655 
    656                     if (more) {
    657                         throw new IOException("FINAL_GET forced, data didn't fit into one packet");
    658                     }
    659 
    660                     mOperationDone = true;
    661                 }
    662             }
    663         } else {
    664             // PUT operation
    665             if (!mOperationDone) {
    666                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    667                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    668                     more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
    669                 }
    670             }
    671 
    672             if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
    673                 mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
    674                         null, mReplyHeader, mPrivateInput, mSrmActive);
    675             }
    676 
    677             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    678                 mOperationDone = true;
    679             }
    680         }
    681     }
    682 
    683     /**
    684      * Continues the operation since there is no data to read.
    685      * @param sendEmpty <code>true</code> if the operation should send an empty
    686      *        packet or not send anything if there is no data to send
    687      * @param inStream <code>true</code> if the stream is input stream or is
    688      *        output stream
    689      * @throws IOException if an IO error occurs
    690      */
    691     public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
    692             throws IOException {
    693 
    694         // One path to the first put operation - the other one does not need to
    695         // handle SRM, as all will fit into one packet.
    696 
    697         if (mGetOperation) {
    698             if ((inStream) && (!mOperationDone)) {
    699                 // to deal with inputstream in get operation
    700                 mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
    701                         null, mReplyHeader, mPrivateInput, mSrmActive);
    702                 /*
    703                   * Determine if that was not the last packet in the operation
    704                   */
    705                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    706                     mOperationDone = true;
    707                 } else {
    708                     checkForSrm();
    709                 }
    710 
    711                 return true;
    712 
    713             } else if ((!inStream) && (!mOperationDone)) {
    714                 // to deal with outputstream in get operation
    715 
    716                 if (mPrivateInput == null) {
    717                     mPrivateInput = new PrivateInputStream(this);
    718                 }
    719 
    720                 if (!mGetFinalFlag) {
    721                     sendRequest(ObexHelper.OBEX_OPCODE_GET);
    722                 } else {
    723                     sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
    724                 }
    725                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    726                     mOperationDone = true;
    727                 }
    728                 return true;
    729 
    730             } else if (mOperationDone) {
    731                 return false;
    732             }
    733 
    734         } else {
    735             // PUT operation
    736             if ((!inStream) && (!mOperationDone)) {
    737                 // to deal with outputstream in put operation
    738                 if (mReplyHeader.responseCode == -1) {
    739                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    740                 }
    741                 sendRequest(ObexHelper.OBEX_OPCODE_PUT);
    742                 return true;
    743             } else if ((inStream) && (!mOperationDone)) {
    744                 // How to deal with inputstream  in put operation ?
    745                 return false;
    746 
    747             } else if (mOperationDone) {
    748                 return false;
    749             }
    750 
    751         }
    752         return false;
    753     }
    754 
    755     /**
    756      * Called when the output or input stream is closed.
    757      * @param inStream <code>true</code> if the input stream is closed;
    758      *        <code>false</code> if the output stream is closed
    759      * @throws IOException if an IO error occurs
    760      */
    761     public void streamClosed(boolean inStream) throws IOException {
    762         if (!mGetOperation) {
    763             if ((!inStream) && (!mOperationDone)) {
    764                 // to deal with outputstream in put operation
    765 
    766                 boolean more = true;
    767 
    768                 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
    769                     byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
    770                     if (headerArray.length <= 0)
    771                         more = false;
    772                 }
    773                 // If have not sent any data so send  all now
    774                 if (mReplyHeader.responseCode == -1) {
    775                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    776                 }
    777 
    778                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    779                     more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
    780                 }
    781 
    782                 /*
    783                  * According to the IrOBEX specification, after the final put, you
    784                  * only have a single reply to send.  so we don't need the while
    785                  * loop.
    786                  */
    787                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
    788 
    789                     sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
    790                 }
    791                 mOperationDone = true;
    792             } else if ((inStream) && (mOperationDone)) {
    793                 // how to deal with input stream in put stream ?
    794                 mOperationDone = true;
    795             }
    796         } else {
    797             if ((inStream) && (!mOperationDone)) {
    798 
    799                 // to deal with inputstream in get operation
    800                 // Have not sent any data so send it all now
    801 
    802                 if (mReplyHeader.responseCode == -1) {
    803                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    804                 }
    805 
    806                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
    807                     if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
    808                         break;
    809                     }
    810                 }
    811                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
    812                     mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
    813                             mReplyHeader, mPrivateInput, false);
    814                     // Regardless of the SRM state, wait for the response.
    815                 }
    816                 mOperationDone = true;
    817             } else if ((!inStream) && (!mOperationDone)) {
    818                 // to deal with outputstream in get operation
    819                 // part of the data may have been sent in continueOperation.
    820 
    821                 boolean more = true;
    822 
    823                 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
    824                     byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
    825                     if (headerArray.length <= 0)
    826                         more = false;
    827                 }
    828 
    829                 if (mPrivateInput == null) {
    830                     mPrivateInput = new PrivateInputStream(this);
    831                 }
    832                 if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
    833                     more = false;
    834 
    835                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
    836                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
    837                     more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
    838                 }
    839                 sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
    840                 //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
    841                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
    842                     mOperationDone = true;
    843                 }
    844             }
    845         }
    846     }
    847 
    848     public void noBodyHeader(){
    849         mSendBodyHeader = false;
    850     }
    851 }
    852