summaryrefslogtreecommitdiff
path: root/src/com/android/obex/ClientOperation.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/obex/ClientOperation.java')
-rw-r--r--src/com/android/obex/ClientOperation.java819
1 files changed, 819 insertions, 0 deletions
diff --git a/src/com/android/obex/ClientOperation.java b/src/com/android/obex/ClientOperation.java
new file mode 100644
index 0000000..8aa15c3
--- /dev/null
+++ b/src/com/android/obex/ClientOperation.java
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.obex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class implements the {@link Operation} interface. It will read and write data via puts and
+ * gets.
+ */
+public final class ClientOperation implements Operation, BaseStream {
+
+ private static final String TAG = "ClientOperation";
+
+ private static final boolean V = ObexHelper.VDBG;
+
+ private ClientSession mParent;
+
+ private boolean mInputOpen;
+
+ private PrivateInputStream mPrivateInput;
+
+ private boolean mPrivateInputOpen;
+
+ private PrivateOutputStream mPrivateOutput;
+
+ private boolean mPrivateOutputOpen;
+
+ private String mExceptionMessage;
+
+ private int mMaxPacketSize;
+
+ private boolean mOperationDone;
+
+ private boolean mGetOperation;
+
+ private boolean mGetFinalFlag;
+
+ private HeaderSet mRequestHeader;
+
+ private HeaderSet mReplyHeader;
+
+ private boolean mEndOfBodySent;
+
+ private boolean mSendBodyHeader = true;
+ // A latch - when triggered, there is not way back ;-)
+ private boolean mSrmActive = false;
+
+ // Assume SRM disabled - until support is confirmed
+ // by the server
+ private boolean mSrmEnabled = false;
+ // keep waiting until final-bit is received in request
+ // to handle the case where the SRM enable header is in
+ // a different OBEX packet than the SRMP header.
+ private boolean mSrmWaitingForRemote = true;
+
+
+ /**
+ * Creates new OperationImpl to read and write data to a server.
+ *
+ * @param maxSize the maximum packet size
+ * @param p the parent to this object
+ * @param type <code>true</code> if this is a get request;
+ * <code>false</code>. if this is a put request
+ * @param header the header to set in the initial request
+ * @throws IOException if an IO error occurred
+ */
+ ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
+ throws IOException {
+
+ mParent = p;
+ mEndOfBodySent = false;
+ mInputOpen = true;
+ mOperationDone = false;
+ mMaxPacketSize = maxSize;
+ mGetOperation = type;
+ mGetFinalFlag = false;
+
+ mPrivateInputOpen = false;
+ mPrivateOutputOpen = false;
+ mPrivateInput = null;
+ mPrivateOutput = null;
+
+ mReplyHeader = new HeaderSet();
+
+ mRequestHeader = new HeaderSet();
+
+ int[] headerList = header.getHeaderList();
+
+ if (headerList != null) {
+
+ for (int i = 0; i < headerList.length; i++) {
+ mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
+ }
+ }
+
+ if ((header).mAuthChall != null) {
+ mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
+ System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
+ (header).mAuthChall.length);
+ }
+
+ if ((header).mAuthResp != null) {
+ mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
+ System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
+ (header).mAuthResp.length);
+
+ }
+
+ if ((header).mConnectionID != null) {
+ mRequestHeader.mConnectionID = new byte[4];
+ System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0,
+ 4);
+
+ }
+ }
+
+ /**
+ * Allows setting the flag which will force GET to always be sent as single packet request with
+ * the final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
+ * require requests to be sent this way.
+ *
+ * @param flag true if the final flag should be set in the packet, false if it should not
+ */
+ public void setGetFinalFlag(boolean flag) {
+ mGetFinalFlag = flag;
+ }
+
+ /**
+ * Sends an ABORT message to the server. By calling this method, the corresponding input and
+ * output streams will be closed along with this object.
+ *
+ * @throws IOException if the transaction has already ended or if an OBEX server called this
+ * method
+ */
+ public synchronized void abort() throws IOException {
+ ensureOpen();
+ //no compatible with sun-ri
+ if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ throw new IOException("Operation has already ended");
+ }
+
+ mExceptionMessage = "Operation aborted";
+ if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ mOperationDone = true;
+ /*
+ * Since we are not sending any headers or returning any headers then
+ * we just need to write and read the same bytes
+ */
+ mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
+ throw new IOException("Invalid response code from server");
+ }
+
+ mExceptionMessage = null;
+ }
+
+ close();
+ }
+
+ /**
+ * Retrieves the response code retrieved from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> interface.
+ * @return the response code retrieved from the server
+ * @throws IOException if an error occurred in the transport layer during
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet</code> in a <code>ClientSession</code>
+ * object
+ */
+ public synchronized int getResponseCode() throws IOException {
+ if ((mReplyHeader.responseCode == -1)
+ || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ validateConnection();
+ }
+
+ return mReplyHeader.responseCode;
+ }
+
+ /**
+ * Open and return an input stream for a connection.
+ * @return an input stream
+ * @throws IOException if an I/O error occurs
+ */
+ public InputStream openInputStream() throws IOException {
+
+ ensureOpen();
+
+ if (mPrivateInputOpen)
+ throw new IOException("no more input streams available");
+ if (mGetOperation) {
+ // send the GET request here
+ validateConnection();
+ } else {
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ }
+
+ mPrivateInputOpen = true;
+
+ return mPrivateInput;
+ }
+
+ /**
+ * Open and return a data input stream for a connection.
+ * @return an input stream
+ * @throws IOException if an I/O error occurs
+ *
+ * @hide
+ */
+ public DataInputStream openDataInputStream() throws IOException {
+ return new DataInputStream(openInputStream());
+ }
+
+ /**
+ * Open and return an output stream for a connection.
+ * @return an output stream
+ * @throws IOException if an I/O error occurs
+ */
+ public OutputStream openOutputStream() throws IOException {
+
+ ensureOpen();
+ ensureNotDone();
+
+ if (mPrivateOutputOpen)
+ throw new IOException("no more output streams available");
+
+ if (mPrivateOutput == null) {
+ // there are 3 bytes operation headers and 3 bytes body headers //
+ mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
+ }
+
+ mPrivateOutputOpen = true;
+
+ return mPrivateOutput;
+ }
+
+ public int getMaxPacketSize() {
+ return mMaxPacketSize - 6 - getHeaderLength();
+ }
+
+ /** @hide */
+ public int getHeaderLength() {
+ // OPP may need it
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
+ return headerArray.length;
+ }
+
+ /**
+ * Open and return a data output stream for a connection.
+ * @return an output stream
+ * @throws IOException if an I/O error occurs
+ */
+ public DataOutputStream openDataOutputStream() throws IOException {
+ return new DataOutputStream(openOutputStream());
+ }
+
+ /**
+ * Closes the connection and ends the transaction
+ * @throws IOException if the operation has already ended or is closed
+ */
+ public void close() throws IOException {
+ mInputOpen = false;
+ mPrivateInputOpen = false;
+ mPrivateOutputOpen = false;
+ mParent.setRequestInactive();
+ }
+
+ /**
+ * Returns the headers that have been received during the operation.
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
+ * @return the headers received during this <code>Operation</code>
+ * @throws IOException if this <code>Operation</code> has been closed
+ */
+ public HeaderSet getReceivedHeader() throws IOException {
+ ensureOpen();
+
+ return mReplyHeader;
+ }
+
+ /**
+ * Specifies the headers that should be sent in the next OBEX message that
+ * is sent.
+ * @param headers the headers to send in the next message
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
+ * @throws IllegalArgumentException if <code>headers</code> was not created
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+ * @throws NullPointerException if <code>headers</code> is <code>null</code>
+ *
+ * @hide
+ */
+ public void sendHeaders(HeaderSet headers) throws IOException {
+ ensureOpen();
+ if (mOperationDone) {
+ throw new IOException("Operation has already exchanged all data");
+ }
+
+ if (headers == null) {
+ throw new IOException("Headers may not be null");
+ }
+
+ int[] headerList = headers.getHeaderList();
+ if (headerList != null) {
+ for (int i = 0; i < headerList.length; i++) {
+ mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
+ }
+ }
+ }
+
+ /**
+ * Verifies that additional information may be sent. In other words, the
+ * operation is not done.
+ * @throws IOException if the operation is completed
+ *
+ * @hide
+ */
+ public void ensureNotDone() throws IOException {
+ if (mOperationDone) {
+ throw new IOException("Operation has completed");
+ }
+ }
+
+ /**
+ * Verifies that the connection is open and no exceptions should be thrown.
+ * @throws IOException if an exception needs to be thrown
+ *
+ * @hide
+ */
+ public void ensureOpen() throws IOException {
+ mParent.ensureOpen();
+
+ if (mExceptionMessage != null) {
+ throw new IOException(mExceptionMessage);
+ }
+ if (!mInputOpen) {
+ throw new IOException("Operation has already ended");
+ }
+ }
+
+ /**
+ * Verifies that the connection is open and the proper data has been read.
+ * @throws IOException if an IO error occurs
+ */
+ private void validateConnection() throws IOException {
+ ensureOpen();
+
+ // Make sure that a response has been received from remote
+ // before continuing
+ if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
+ startProcessing();
+ }
+ }
+
+ /**
+ * Sends a request to the client of the specified type.
+ * This function will enable SRM and set SRM active if the server
+ * response allows this.
+ * @param opCode the request code to send to the client
+ * @return <code>true</code> if there is more data to send;
+ * <code>false</code> if there is no more data to send
+ * @throws IOException if an IO error occurs
+ */
+ private boolean sendRequest(int opCode) throws IOException {
+ boolean returnValue = false;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int bodyLength = -1;
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
+ if (mPrivateOutput != null) {
+ bodyLength = mPrivateOutput.size();
+ }
+
+ /*
+ * Determine if there is space to add a body request. At present
+ * this method checks to see if there is room for at least a 17
+ * byte body header. This number needs to be at least 6 so that
+ * there is room for the header ID and length and the reply ID and
+ * length, but it is a waste of resources if we can't send much of
+ * the body.
+ */
+ final int MINIMUM_BODY_LENGTH = 3;
+ if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
+ > mMaxPacketSize) {
+ int end = 0;
+ int start = 0;
+ // split & send the headerArray in multiple packets.
+
+ while (end != headerArray.length) {
+ //split the headerArray
+
+ end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
+ - ObexHelper.BASE_PACKET_LENGTH);
+ // can not split
+ if (end == -1) {
+ mOperationDone = true;
+ abort();
+ mExceptionMessage = "Header larger then can be sent in a packet";
+ mInputOpen = false;
+
+ if (mPrivateInput != null) {
+ mPrivateInput.close();
+ }
+
+ if (mPrivateOutput != null) {
+ mPrivateOutput.close();
+ }
+ throw new IOException("OBEX Packet exceeds max packet size");
+ }
+
+ byte[] sendHeader = new byte[end - start];
+ System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+ if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
+ return false;
+ }
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ return false;
+ }
+
+ start = end;
+ }
+
+ // Enable SRM if it should be enabled
+ checkForSrm();
+
+ if (bodyLength > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ /* All headers will fit into a single package */
+ if(mSendBodyHeader == false) {
+ /* As we are not to send any body data, set the FINAL_BIT */
+ opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
+ }
+ out.write(headerArray);
+ }
+
+ if (bodyLength > 0) {
+ /*
+ * Determine if we can send the whole body or just part of
+ * the body. Remember that there is the 3 bytes for the
+ * response message and 3 bytes for the header ID and length
+ */
+ if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
+ returnValue = true;
+
+ bodyLength = mMaxPacketSize - headerArray.length - 6;
+ }
+
+ byte[] body = mPrivateOutput.readBytes(bodyLength);
+
+ /*
+ * Since this is a put request if the final bit is set or
+ * the output stream is closed we need to send the 0x49
+ * (End of Body) otherwise, we need to send 0x48 (Body)
+ */
+ if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
+ && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
+ out.write(HeaderSet.END_OF_BODY);
+ mEndOfBodySent = true;
+ } else {
+ out.write(HeaderSet.BODY);
+ }
+
+ bodyLength += 3;
+ out.write((byte)(bodyLength >> 8));
+ out.write((byte)bodyLength);
+
+ if (body != null) {
+ out.write(body);
+ }
+ }
+
+ if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
+ // only 0x82 or 0x83 can send 0x49
+ if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
+ out.write(HeaderSet.BODY);
+ } else {
+ out.write(HeaderSet.END_OF_BODY);
+ mEndOfBodySent = true;
+ }
+
+ bodyLength = 3;
+ out.write((byte)(bodyLength >> 8));
+ out.write((byte)bodyLength);
+ }
+
+ if (out.size() == 0) {
+ if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
+ return false;
+ }
+ // Enable SRM if it should be enabled
+ checkForSrm();
+ return returnValue;
+ }
+ if ((out.size() > 0)
+ && (!mParent.sendRequest(opCode, out.toByteArray(),
+ mReplyHeader, mPrivateInput, mSrmActive))) {
+ return false;
+ }
+ // Enable SRM if it should be enabled
+ checkForSrm();
+
+ // send all of the output data in 0x48,
+ // send 0x49 with empty body
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
+ returnValue = true;
+
+ return returnValue;
+ }
+
+ private void checkForSrm() throws IOException {
+ Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+ if(mParent.isSrmSupported() == true && srmMode != null
+ && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
+ mSrmEnabled = true;
+ }
+ /**
+ * Call this only when a complete obex packet have been received.
+ * (This is not optimal, but the current design is not really suited to
+ * the way SRM is specified.)
+ * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
+ * into every OBEX packet, hence if another header occupies the entire packet,
+ * the scheme will not work - unlikely though.
+ */
+ if(mSrmEnabled) {
+ mSrmWaitingForRemote = false;
+ Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+ if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
+ mSrmWaitingForRemote = true;
+ // Clear the wait header, as the absence of the header in the next packet
+ // indicates don't wait anymore.
+ mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+ }
+ }
+ if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
+ mSrmActive = true;
+ }
+ }
+
+ /**
+ * This method starts the processing thread results. It will send the
+ * initial request. If the response takes more than one packet, a thread
+ * will be started to handle additional requests
+ * @throws IOException if an IO error occurs
+ */
+ private synchronized void startProcessing() throws IOException {
+
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ boolean more = true;
+
+ if (mGetOperation) {
+ if (!mOperationDone) {
+ if (!mGetFinalFlag) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode ==
+ ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
+ }
+ // For GET we need to loop until all headers have been sent,
+ // And then we wait for the first continue package with the
+ // reply.
+ if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
+ null, mReplyHeader, mPrivateInput, mSrmActive);
+ }
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ } else {
+ checkForSrm();
+ }
+ } else {
+ more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
+
+ if (more) {
+ throw new IOException("FINAL_GET forced, data didn't fit into one packet");
+ }
+
+ mOperationDone = true;
+ }
+ }
+ } else {
+ // PUT operation
+ if (!mOperationDone) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
+ }
+ }
+
+ if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
+ null, mReplyHeader, mPrivateInput, mSrmActive);
+ }
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ }
+ }
+
+ /**
+ * Continues the operation since there is no data to read.
+ * @param sendEmpty <code>true</code> if the operation should send an empty
+ * packet or not send anything if there is no data to send
+ * @param inStream <code>true</code> if the stream is input stream or is
+ * output stream
+ * @throws IOException if an IO error occurs
+ */
+ public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+ throws IOException {
+
+ // One path to the first put operation - the other one does not need to
+ // handle SRM, as all will fit into one packet.
+
+ if (mGetOperation) {
+ if ((inStream) && (!mOperationDone)) {
+ // to deal with inputstream in get operation
+ mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
+ null, mReplyHeader, mPrivateInput, mSrmActive);
+ /*
+ * Determine if that was not the last packet in the operation
+ */
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ } else {
+ checkForSrm();
+ }
+
+ return true;
+
+ } else if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in get operation
+
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+
+ if (!mGetFinalFlag) {
+ sendRequest(ObexHelper.OBEX_OPCODE_GET);
+ } else {
+ sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
+ }
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ return true;
+
+ } else if (mOperationDone) {
+ return false;
+ }
+
+ } else {
+ // PUT operation
+ if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in put operation
+ if (mReplyHeader.responseCode == -1) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+ sendRequest(ObexHelper.OBEX_OPCODE_PUT);
+ return true;
+ } else if ((inStream) && (!mOperationDone)) {
+ // How to deal with inputstream in put operation ?
+ return false;
+
+ } else if (mOperationDone) {
+ return false;
+ }
+
+ }
+ return false;
+ }
+
+ /**
+ * Called when the output or input stream is closed.
+ * @param inStream <code>true</code> if the input stream is closed;
+ * <code>false</code> if the output stream is closed
+ * @throws IOException if an IO error occurs
+ *
+ * @hide
+ */
+ public void streamClosed(boolean inStream) throws IOException {
+ if (!mGetOperation) {
+ if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in put operation
+
+ boolean more = true;
+
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
+ if (headerArray.length <= 0)
+ more = false;
+ }
+ // If have not sent any data so send all now
+ if (mReplyHeader.responseCode == -1) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
+ }
+
+ /*
+ * According to the IrOBEX specification, after the final put, you
+ * only have a single reply to send. so we don't need the while
+ * loop.
+ */
+ while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+
+ sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
+ }
+ mOperationDone = true;
+ } else if ((inStream) && (mOperationDone)) {
+ // how to deal with input stream in put stream ?
+ mOperationDone = true;
+ }
+ } else {
+ if ((inStream) && (!mOperationDone)) {
+
+ // to deal with inputstream in get operation
+ // Have not sent any data so send it all now
+
+ if (mReplyHeader.responseCode == -1) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+
+ while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE
+ && !mOperationDone) {
+ if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
+ break;
+ }
+ }
+ while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE
+ && !mOperationDone) {
+ mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
+ mReplyHeader, mPrivateInput, false);
+ // Regardless of the SRM state, wait for the response.
+ }
+ mOperationDone = true;
+ } else if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in get operation
+ // part of the data may have been sent in continueOperation.
+
+ boolean more = true;
+
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
+ if (headerArray.length <= 0)
+ more = false;
+ }
+
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
+ more = false;
+
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
+ }
+ sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
+ // parent.sendRequest(0x83, null, replyHeaders, privateInput);
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ }
+ }
+ }
+
+ /** @hide */
+ public void noBodyHeader(){
+ mSendBodyHeader = false;
+ }
+}