content: /* 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 javax.obex;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.DataInputStream;
  import java.io.OutputStream;
  import java.io.DataOutputStream;
  import java.io.ByteArrayOutputStream;
  
  import android.util.Log;
  
  /**
   * This class implements the Operation interface for server side connections.
   * <P>
   * <STRONG>Request Codes</STRONG> There are four different request codes that
   * are in this class. 0x02 is a PUT request that signals that the request is not
   * complete and requires an additional OBEX packet. 0x82 is a PUT request that
   * says that request is complete. In this case, the server can begin sending the
   * response. The 0x03 is a GET request that signals that the request is not
   * finished. When the server receives a 0x83, the client is signaling the server
   * that it is done with its request. TODO: Extend the ClientOperation and reuse
   * the methods defined TODO: in that class.
   * @hide
   */
  public final class ServerOperation implements Operation, BaseStream {
  
      private static final String TAG = "ServerOperation";
  
      private static final boolean V = ObexHelper.VDBG; // Verbose debugging
  
      public boolean isAborted;
  
      public HeaderSet requestHeader;
  
      public HeaderSet replyHeader;
  
      public boolean finalBitSet;
  
      private InputStream mInput;
  
      private ServerSession mParent;
  
      private int mMaxPacketLength;
  
      private int mResponseSize;
  
      private boolean mClosed;
  
      private boolean mGetOperation;
  
      private PrivateInputStream mPrivateInput;
  
      private PrivateOutputStream mPrivateOutput;
  
      private ObexTransport mTransport;
  
      private boolean mPrivateOutputOpen;
  
      private String mExceptionString;
  
      private ServerRequestHandler mListener;
  
      private boolean mRequestFinished;
  
      private boolean mHasBody;
  
      private boolean mSendBodyHeader = true;
      // Assume SRM disabled - needs to be explicit
      // enabled by client
      private boolean mSrmEnabled = false;
      // A latch - when triggered, there is not way back ;-)
      private boolean mSrmActive = false;
      // Set to true when a SRM enable response have been send
      private boolean mSrmResponseSent = 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;
      // Why should we wait? - currently not exposed to apps.
      private boolean mSrmLocalWait = false;
  
      /**
       * Creates new ServerOperation
       * @param p the parent that created this object
       * @param in the input stream to read from
       * @param out the output stream to write to
       * @param request the initial request that was received from the client
       * @param maxSize the max packet size that the client will accept
       * @param listen the listener that is responding to the request
       * @throws IOException if an IO error occurs
       */
      public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
              ServerRequestHandler listen) throws IOException {
  
          isAborted = false;
          mParent = p;
          mInput = in;
          mMaxPacketLength = maxSize;
          mClosed = false;
          requestHeader = new HeaderSet();
          replyHeader = new HeaderSet();
          mPrivateInput = new PrivateInputStream(this);
          mResponseSize = 3;
          mListener = listen;
          mRequestFinished = false;
          mPrivateOutputOpen = false;
          mHasBody = false;
          ObexPacket packet;
          mTransport = p.getTransport();
  
          /*
           * Determine if this is a PUT request
           */
          if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
                  (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
              /*
               * It is a PUT request.
               */
              mGetOperation = false;
  
              /*
               * Determine if the final bit is set
               */
              if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
                  finalBitSet = false;
              } else {
                  finalBitSet = true;
                  mRequestFinished = true;
              }
          } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
                  (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
              /*
               * It is a GET request.
               */
              mGetOperation = true;
  
              // For Get request, final bit set is decided by server side logic
              finalBitSet = false;
  
              if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
                  mRequestFinished = true;
              }
          } else {
              throw new IOException("ServerOperation can not handle such request");
          }
  
          packet = ObexPacket.read(request, mInput);
  
          /*
           * Determine if the packet length is larger than this device can receive
           */
          if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
              mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
              throw new IOException("Packet received was too large. Length: "
                      + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
          }
  
          /*
           * Determine if any headers were sent in the initial request
           */
          if (packet.mLength > 3) {
              if(!handleObexPacket(packet)) {
                  return;
              }
              /* Don't Pre-Send continue when Remote requested for SRM
               * Let the Application confirm.
               */
              if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
                      + " not hasBody case: " + mHasBody);
              if (!mHasBody && !mSrmEnabled) {
                  while ((!mGetOperation) && (!finalBitSet)) {
                      sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                      if (mPrivateInput.available() > 0) {
                          break;
                      }
                  }
              }
          }
          /* Don't Pre-Send continue when Remote requested for SRM
            * Let the Application confirm.
            */
          if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
              + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
          while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
                  && (mPrivateInput.available() == 0)) {
              sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
              if (mPrivateInput.available() > 0) {
                  break;
              }
          }
  
          // wait for get request finished !!!!
          while (mGetOperation && !mRequestFinished) {
              sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
          }
      }
  
      /**
       * Parse headers and update member variables
       * @param packet the received obex packet
       * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
       * response have been send. Else true.
       * @throws IOException
       */
      private boolean handleObexPacket(ObexPacket packet) throws IOException {
          byte[] body = updateRequestHeaders(packet);
  
          if (body != null) {
              mHasBody = true;
          }
          if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
              mListener.setConnectionId(ObexHelper
                      .convertToLong(requestHeader.mConnectionID));
          } else {
              mListener.setConnectionId(1);
          }
  
          if (requestHeader.mAuthResp != null) {
              if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
                  mExceptionString = "Authentication Failed";
                  mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
                  mClosed = true;
                  requestHeader.mAuthResp = null;
                  return false;
              }
              requestHeader.mAuthResp = null;
          }
  
          if (requestHeader.mAuthChall != null) {
              mParent.handleAuthChall(requestHeader);
              // send the auhtResp to the client
              replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
              System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
                      replyHeader.mAuthResp.length);
              requestHeader.mAuthResp = null;
              requestHeader.mAuthChall = null;
          }
  
          if (body != null) {
              mPrivateInput.writeBytes(body, 1);
          }
          return true;
      }
  
      /**
       * Update the request header set, and sniff on SRM headers to update local state.
       * @param data the OBEX packet data
       * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
       * @throws IOException
       */
      private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
          byte[] body = null;
          if (packet.mPayload != null) {
              body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
          }
          Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
          if(mTransport.isSrmSupported() && srmMode != null
                  && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
              mSrmEnabled = true;
              if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
          }
          checkForSrmWait(packet.mHeaderId);
          if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
              if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
              mSrmActive = true;
          }
          return body;
      }
  
      /**
       * Call this only when a complete request have been received.
       * (This is not optimal, but the current design is not really suited to
       * the way SRM is specified.)
       */
      private void checkForSrmWait(int headerId){
          if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
                  || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
                  || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
              try {
                  mSrmWaitingForRemote = false;
                  Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
                  if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
                      mSrmWaitingForRemote = true;
                      // Clear the wait header, as the absents of the header when the final bit is set
                      // indicates don't wait.
                      requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
                  }
              } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
          }
      }
  
      public boolean isValidBody() {
          return mHasBody;
      }
  
      /**
       * Determines if the operation should continue or should wait. If it should
       * continue, this method will continue the operation.
       * @param sendEmpty if <code>true</code> then this will continue the
       *        operation even if no headers will be sent; if <code>false</code>
       *        then this method will only continue the operation if there are
       *        headers to send
       * @param inStream if<code>true</code> the stream is input stream, otherwise
       *        output stream
       * @return <code>true</code> if the operation was completed;
       *         <code>false</code> if no operation took place
       */
      public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
              throws IOException {
          if (!mGetOperation) {
              if (!finalBitSet) {
                  if (sendEmpty) {
                      sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                      return true;
                  } else {
                      if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
                          sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                          return true;
                      } else {
                          return false;
                      }
                  }
              } else {
                  return false;
              }
          } else {
              sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
              return true;
          }
      }
  
      /**
       * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
       * will wait for a response from the client before ending unless SRM is active.
       * @param type the response code to send back to the client
       * @return <code>true</code> if the final bit was not set on the reply;
       *         <code>false</code> if no reply was received because the operation
       *         ended, an abort was received, the final bit was set in the
       *         reply or SRM is active.
       * @throws IOException if an IO error occurs
       */
      public synchronized boolean sendReply(int type) throws IOException {
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          boolean skipSend = false;
          boolean skipReceive = false;
          boolean srmRespSendPending = false;
  
          long id = mListener.getConnectionId();
          if (id == -1) {
              replyHeader.mConnectionID = null;
          } else {
              replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
          }
  
          if(mSrmEnabled && !mSrmResponseSent) {
              // As we are not ensured that the SRM enable is in the first OBEX packet
              // We must check for each reply.
              if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
              replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
              srmRespSendPending = true;
          }
  
          if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
              replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
          }
  
          byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
          int bodyLength = -1;
          int orginalBodyLength = -1;
  
          if (mPrivateOutput != null) {
              bodyLength = mPrivateOutput.size();
              orginalBodyLength = bodyLength;
          }
  
          if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
  
              int end = 0;
              int start = 0;
  
              while (end != headerArray.length) {
                  end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
                          - ObexHelper.BASE_PACKET_LENGTH);
                  if (end == -1) {
  
                      mClosed = true;
  
                      if (mPrivateInput != null) {
                          mPrivateInput.close();
                      }
  
                      if (mPrivateOutput != null) {
                          mPrivateOutput.close();
                      }
                      mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
                      throw new IOException("OBEX Packet exceeds max packet size");
                  }
                  byte[] sendHeader = new byte[end - start];
                  System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
  
                  mParent.sendResponse(type, sendHeader);
                  start = end;
              }
  
              if (bodyLength > 0) {
                  return true;
              } else {
                  return false;
              }
  
          } else {
              out.write(headerArray);
          }
  
          // For Get operation: if response code is OBEX_HTTP_OK, then this is the
          // last packet; so set finalBitSet to true.
          if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
              finalBitSet = true;
          }
  
          if(mSrmActive) {
              if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
                      mSrmResponseSent == true) {
                  // we are in the middle of a SRM PUT operation, don't send a continue.
                  skipSend = true;
              } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
                  // We are still receiving the get request, receive, but don't send continue.
                  skipSend = true;
              } else if(mGetOperation && mRequestFinished == true) {
                  // All done receiving the GET request, send data to the client, without
                  // expecting a continue.
                  skipReceive = true;
              }
              if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
                      + " skipReceive==" + skipReceive);
          }
          if(srmRespSendPending) {
              if(V)Log.v(TAG,
                      "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
              mSrmResponseSent = true;
          }
  
          if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
              if (bodyLength > 0) {
                  /*
                   * Determine if I 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 > (mMaxPacketLength - headerArray.length - 6)) {
                      bodyLength = mMaxPacketLength - 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 ((finalBitSet) || (mPrivateOutput.isClosed())) {
                      if(mSendBodyHeader == true) {
                          out.write(0x49);
                          bodyLength += 3;
                          out.write((byte)(bodyLength >> 8));
                          out.write((byte)bodyLength);
                          out.write(body);
                      }
                  } else {
                      if(mSendBodyHeader == true) {
                      out.write(0x48);
                      bodyLength += 3;
                      out.write((byte)(bodyLength >> 8));
                      out.write((byte)bodyLength);
                      out.write(body);
                      }
                  }
  
              }
          }
  
          if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
              if(mSendBodyHeader) {
                  out.write(0x49);
                  orginalBodyLength = 3;
                  out.write((byte)(orginalBodyLength >> 8));
                  out.write((byte)orginalBodyLength);
              }
          }
  
          if(skipSend == false) {
              mResponseSize = 3;
              mParent.sendResponse(type, out.toByteArray());
          }
  
          if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
  
              if(mGetOperation && skipReceive) {
                  // Here we need to check for and handle abort (throw an exception).
                  // Any other signal received should be discarded silently (only on server side)
                  checkSrmRemoteAbort();
              } else {
                  // Receive and handle data (only send reply if !skipSend)
                  // Read a complete OBEX Packet
                  ObexPacket packet = ObexPacket.read(mInput);
  
                  int headerId = packet.mHeaderId;
                  if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
                          && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
                          && (headerId != ObexHelper.OBEX_OPCODE_GET)
                          && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
  
                      /*
                       * Determine if an ABORT was sent as the reply
                       */
                      if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
                          handleRemoteAbort();
                      } else {
                          // TODO:shall we send this if it occurs during SRM? Errata on the subject
                          mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
                          mClosed = true;
                          mExceptionString = "Bad Request Received";
                          throw new IOException("Bad Request Received");
                      }
                  } else {
  
                      if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
                          finalBitSet = true;
                      } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
                          mRequestFinished = true;
                      }
  
                      /*
                       * Determine if the packet length is larger than the negotiated packet size
                       */
                      if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
                          mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
                          throw new IOException("Packet received was too large");
                      }
  
                      /*
                       * Determine if any headers were sent in the initial request
                       */
                      if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
                          if(handleObexPacket(packet) == false) {
                              return false;
                          }
                      }
                  }
  
              }
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * This method will look for an abort from the peer during a SRM transfer.
       * The function will not block if no data has been received from the remote device.
       * If data have been received, the function will block while reading the incoming
       * OBEX package.
       * An Abort request will be handled, and cause an IOException("Abort Received").
       * Other messages will be discarded silently as per GOEP specification.
       * @throws IOException if an abort request have been received.
       * TODO: I think this is an error in the specification. If we discard other messages,
       *       the peer device will most likely stall, as it will not receive the expected
       *       response for the message...
       *       I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
       *       header values shall be ignored by the receiving device."
       *       If any signal is received during an active SRM transfer it is unexpected regardless
       *       whether or not it contains SRM/SRMP headers...
       */
      private void checkSrmRemoteAbort() throws IOException {
          if(mInput.available() > 0) {
              ObexPacket packet = ObexPacket.read(mInput);
              /*
               * Determine if an ABORT was sent as the reply
               */
              if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
                  handleRemoteAbort();
              } else {
                  // TODO: should we throw an exception here anyway? - don't see how to
                  //       ignore SRM/SRMP headers without ignoring the complete signal
                  //       (in this particular case).
                  Log.w(TAG, "Received unexpected request from client - discarding...\n"
                          + "   headerId: " + packet.mHeaderId + " length: " + packet.mLength);
              }
          }
      }
  
      private void handleRemoteAbort() throws IOException {
          /* TODO: To increase the speed of the abort operation in SRM, we need
           *       to be able to flush the L2CAP queue for the PSM in use.
           *       This could be implemented by introducing a control
           *       message to be send over the socket, that in the abort case
           *       could carry a flush command. */
          mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
          mClosed = true;
          isAborted = true;
          mExceptionString = "Abort Received";
          throw new IOException("Abort Received");
      }
  
      /**
       * 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 void abort() throws IOException {
          throw new IOException("Called from a server");
      }
  
      /**
       * 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 requestHeader;
      }
  
      /**
       * 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>
       */
      public void sendHeaders(HeaderSet headers) throws IOException {
          ensureOpen();
  
          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++) {
                  replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
              }
  
          }
      }
  
      /**
       * 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; if this is called from a server
       */
      public int getResponseCode() throws IOException {
          throw new IOException("Called from a server");
      }
  
      /**
       * Always returns <code>null</code>
       * @return <code>null</code>
       */
      public String getEncoding() {
          return null;
      }
  
      /**
       * Returns the type of content that the resource connected to is providing.
       * E.g. if the connection is via HTTP, then the value of the content-type
       * header field is returned.
       * @return the content type of the resource that the URL references, or
       *         <code>null</code> if not known
       */
      public String getType() {
          try {
              return (String)requestHeader.getHeader(HeaderSet.TYPE);
          } catch (IOException e) {
              return null;
          }
      }
  
      /**
       * Returns the length of the content which is being provided. E.g. if the
       * connection is via HTTP, then the value of the content-length header field
       * is returned.
       * @return the content length of the resource that this connection's URL
       *         references, or -1 if the content length is not known
       */
      public long getLength() {
          try {
              Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
  
              if (temp == null) {
                  return -1;
              } else {
                  return temp.longValue();
              }
          } catch (IOException e) {
              return -1;
          }
      }
  
      public int getMaxPacketSize() {
          return mMaxPacketLength - 6 - getHeaderLength();
      }
  
      public int getHeaderLength() {
          long id = mListener.getConnectionId();
          if (id == -1) {
              replyHeader.mConnectionID = null;
          } else {
              replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
          }
  
          byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
  
          return headerArray.length;
      }
  
      /**
       * 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();
          return mPrivateInput;
      }
  
      /**
       * Open and return a data input stream for a connection.
       * @return an input stream
       * @throws IOException if an I/O error occurs
       */
      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();
  
          if (mPrivateOutputOpen) {
              throw new IOException("no more input streams available, stream already opened");
          }
  
          if (!mRequestFinished) {
              throw new IOException("no  output streams available ,request not finished");
          }
  
          if (mPrivateOutput == null) {
              mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
          }
          mPrivateOutputOpen = true;
          return mPrivateOutput;
      }
  
      /**
       * 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 {
          ensureOpen();
          mClosed = true;
      }
  
      /**
       * Verifies that the connection is open and no exceptions should be thrown.
       * @throws IOException if an exception needs to be thrown
       */
      public void ensureOpen() throws IOException {
          if (mExceptionString != null) {
              throw new IOException(mExceptionString);
          }
          if (mClosed) {
              throw new IOException("Operation has already ended");
          }
      }
  
      /**
       * Verifies that additional information may be sent. In other words, the
       * operation is not done.
       * <P>
       * Included to implement the BaseStream interface only. It does not do
       * anything on the server side since the operation of the Operation object
       * is not done until after the handler returns from its method.
       * @throws IOException if the operation is completed
       */
      public void ensureNotDone() throws IOException {
      }
  
      /**
       * Called when the output or input stream is closed. It does not do anything
       * on the server side since the operation of the Operation object is not
       * done until after the handler returns from its method.
       * @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
       */
      public void streamClosed(boolean inStream) throws IOException {
  
      }
  
      public void noBodyHeader(){
          mSendBodyHeader = false;
      }
  }
  