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      */
ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)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      */
setGetFinalFlag(boolean flag)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      */
abort()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      */
getResponseCode()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      */
getEncoding()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      */
getType()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      */
getLength()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      */
openInputStream()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      */
openDataInputStream()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      */
openOutputStream()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 
getMaxPacketSize()322     public int getMaxPacketSize() {
323         return mMaxPacketSize - 6 - getHeaderLength();
324     }
325 
getHeaderLength()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      */
openDataOutputStream()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      */
close()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      */
getReceivedHeader()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      */
sendHeaders(HeaderSet headers)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      */
ensureNotDone()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      */
ensureOpen()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      */
validateConnection()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      */
sendRequest(int opCode)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 
checkForSrm()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      */
startProcessing()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      */
continueOperation(boolean sendEmpty, boolean inStream)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      */
streamClosed(boolean inStream)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 
noBodyHeader()829     public void noBodyHeader(){
830         mSendBodyHeader = false;
831     }
832 }
833