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
35package javax.obex;
36
37import java.io.IOException;
38import java.io.InputStream;
39import java.io.OutputStream;
40import java.io.DataInputStream;
41import java.io.DataOutputStream;
42import java.io.ByteArrayOutputStream;
43
44import 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 */
51public 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                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