/** Function to send obex header back to client such as get phonebook size request */
  private final int pushHeader(final Operation op, final HeaderSet reply) {
    OutputStream outputStream = null;

    if (D) Log.d(TAG, "Push Header");
    if (D) Log.d(TAG, reply.toString());

    int pushResult = ResponseCodes.OBEX_HTTP_OK;
    try {
      op.sendHeaders(reply);
      outputStream = op.openOutputStream();
      outputStream.flush();
    } catch (IOException e) {
      Log.e(TAG, e.toString());
      pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    } finally {
      if (!closeStream(outputStream, op)) {
        pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
      }
    }
    return pushResult;
  }
 public static boolean closeStream(final OutputStream out, final Operation op) {
   boolean returnvalue = true;
   try {
     if (out != null) {
       out.close();
     }
   } catch (IOException e) {
     Log.e(TAG, "outputStream close failed" + e.toString());
     returnvalue = false;
   }
   try {
     if (op != null) {
       op.close();
     }
   } catch (IOException e) {
     Log.e(TAG, "operation close failed" + e.toString());
     returnvalue = false;
   }
   return returnvalue;
 }
  private final int handleAppParaForResponse(
      AppParamValue appParamValue, int size, HeaderSet reply, Operation op) {
    byte[] misnum = new byte[1];
    ApplicationParameter ap = new ApplicationParameter();

    // In such case, PCE only want the number of index.
    // So response not contain any Body header.
    if (mNeedPhonebookSize) {
      if (V) Log.v(TAG, "Need Phonebook size in response header.");
      mNeedPhonebookSize = false;

      byte[] pbsize = new byte[2];

      pbsize[0] = (byte) ((size / 256) & 0xff); // HIGH VALUE
      pbsize[1] = (byte) ((size % 256) & 0xff); // LOW VALUE
      ap.addAPPHeader(
          ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID,
          ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH,
          pbsize);

      if (mNeedNewMissedCallsNum) {
        mNeedNewMissedCallsNum = false;
        int nmnum = size - mMissedCallSize;
        mMissedCallSize = size;

        nmnum = nmnum > 0 ? nmnum : 0;
        misnum[0] = (byte) nmnum;
        ap.addAPPHeader(
            ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID,
            ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH,
            misnum);
        if (D)
          Log.d(TAG, "handleAppParaForResponse(): mNeedNewMissedCallsNum=true,  num= " + nmnum);
      }
      reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());

      if (D) Log.d(TAG, "Send back Phonebook size only, without body info! Size= " + size);

      return pushHeader(op, reply);
    }

    // Only apply to "mch" download/listing.
    // NewMissedCalls is used only in the response, together with Body
    // header.
    if (mNeedNewMissedCallsNum) {
      if (V) Log.v(TAG, "Need new missed call num in response header.");
      mNeedNewMissedCallsNum = false;

      int nmnum = size - mMissedCallSize;
      mMissedCallSize = size;

      nmnum = nmnum > 0 ? nmnum : 0;
      misnum[0] = (byte) nmnum;
      ap.addAPPHeader(
          ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID,
          ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH,
          misnum);
      reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
      if (D) Log.d(TAG, "handleAppParaForResponse(): mNeedNewMissedCallsNum=true,  num= " + nmnum);

      // Only Specifies the headers, not write for now, will write to PCE
      // together with Body
      try {
        op.sendHeaders(reply);
      } catch (IOException e) {
        Log.e(TAG, e.toString());
        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
      }
    }
    return NEED_SEND_BODY;
  }
  /** Function to send vcard data to client */
  private final int pushBytes(Operation op, final String vcardString) {
    if (vcardString == null) {
      Log.w(TAG, "vcardString is null!");
      return ResponseCodes.OBEX_HTTP_OK;
    }

    int vcardStringLen = vcardString.length();
    if (D) Log.d(TAG, "Send Data: len=" + vcardStringLen);

    OutputStream outputStream = null;
    int pushResult = ResponseCodes.OBEX_HTTP_OK;
    try {
      outputStream = op.openOutputStream();
    } catch (IOException e) {
      Log.e(TAG, "open outputstrem failed" + e.toString());
      return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    }

    int position = 0;
    long timestamp = 0;
    int outputBufferSize = op.getMaxPacketSize();
    if (V) Log.v(TAG, "outputBufferSize = " + outputBufferSize);
    while (position != vcardStringLen) {
      if (sIsAborted) {
        ((ServerOperation) op).isAborted = true;
        sIsAborted = false;
        break;
      }
      if (V) timestamp = System.currentTimeMillis();
      int readLength = outputBufferSize;
      if (vcardStringLen - position < outputBufferSize) {
        readLength = vcardStringLen - position;
      }
      String subStr = vcardString.substring(position, position + readLength);
      try {
        outputStream.write(subStr.getBytes(), 0, readLength);
      } catch (IOException e) {
        Log.e(TAG, "write outputstrem failed" + e.toString());
        pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
        break;
      }
      if (V) {
        Log.v(
            TAG,
            "Sending vcard String position = "
                + position
                + " readLength "
                + readLength
                + " bytes took "
                + (System.currentTimeMillis() - timestamp)
                + " ms");
      }
      position += readLength;
    }

    if (V) Log.v(TAG, "Send Data complete!");

    if (!closeStream(outputStream, op)) {
      pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    }

    return pushResult;
  }
  @Override
  public int onGet(Operation op) {
    sIsAborted = false;
    HeaderSet request = null;
    HeaderSet reply = new HeaderSet();
    String type = "";
    String name = "";
    byte[] appParam = null;
    AppParamValue appParamValue = new AppParamValue();
    try {
      request = op.getReceivedHeader();
      type = (String) request.getHeader(HeaderSet.TYPE);
      name = (String) request.getHeader(HeaderSet.NAME);
      appParam = (byte[]) request.getHeader(HeaderSet.APPLICATION_PARAMETER);
    } catch (IOException e) {
      Log.e(TAG, "request headers error");
      return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
    }

    if (V) logHeader(request);
    if (D) Log.d(TAG, "OnGet type is " + type + "; name is " + name);

    if (type == null) {
      return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
    }
    // Accroding to specification,the name header could be omitted such as
    // sony erriccsonHBH-DS980

    // For "x-bt/phonebook" and "x-bt/vcard-listing":
    // if name == null, guess what carkit actually want from current path
    // For "x-bt/vcard":
    // We decide which kind of content client would like per current path

    boolean validName = true;
    if (TextUtils.isEmpty(name)) {
      validName = false;
    }

    if (!validName || (validName && type.equals(TYPE_VCARD))) {
      if (D) Log.d(TAG, "Guess what carkit actually want from current path (" + mCurrentPath + ")");

      if (mCurrentPath.equals(PB_PATH)) {
        appParamValue.needTag = ContentType.PHONEBOOK;
      } else if (mCurrentPath.equals(ICH_PATH)) {
        appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY;
      } else if (mCurrentPath.equals(OCH_PATH)) {
        appParamValue.needTag = ContentType.OUTGOING_CALL_HISTORY;
      } else if (mCurrentPath.equals(MCH_PATH)) {
        appParamValue.needTag = ContentType.MISSED_CALL_HISTORY;
        mNeedNewMissedCallsNum = true;
      } else if (mCurrentPath.equals(CCH_PATH)) {
        appParamValue.needTag = ContentType.COMBINED_CALL_HISTORY;
      } else {
        Log.w(TAG, "mCurrentpath is not valid path!!!");
        return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
      }
      if (D) Log.v(TAG, "onGet(): appParamValue.needTag=" + appParamValue.needTag);
    } else {
      // Not support SIM card currently
      if (name.contains(SIM1.subSequence(0, SIM1.length()))) {
        Log.w(TAG, "Not support access SIM card info!");
        return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
      }

      // we have weak name checking here to provide better
      // compatibility with other devices,although unique name such as
      // "pb.vcf" is required by SIG spec.
      if (name.contains(PB.subSequence(0, PB.length()))) {
        appParamValue.needTag = ContentType.PHONEBOOK;
        if (D) Log.v(TAG, "download phonebook request");
      } else if (name.contains(ICH.subSequence(0, ICH.length()))) {
        appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY;
        if (D) Log.v(TAG, "download incoming calls request");
      } else if (name.contains(OCH.subSequence(0, OCH.length()))) {
        appParamValue.needTag = ContentType.OUTGOING_CALL_HISTORY;
        if (D) Log.v(TAG, "download outgoing calls request");
      } else if (name.contains(MCH.subSequence(0, MCH.length()))) {
        appParamValue.needTag = ContentType.MISSED_CALL_HISTORY;
        mNeedNewMissedCallsNum = true;
        if (D) Log.v(TAG, "download missed calls request");
      } else if (name.contains(CCH.subSequence(0, CCH.length()))) {
        appParamValue.needTag = ContentType.COMBINED_CALL_HISTORY;
        if (D) Log.v(TAG, "download combined calls request");
      } else {
        Log.w(TAG, "Input name doesn't contain valid info!!!");
        return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
      }
    }

    if ((appParam != null) && !parseApplicationParameter(appParam, appParamValue)) {
      return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
    }

    // listing request
    if (type.equals(TYPE_LISTING)) {
      return pullVcardListing(appParam, appParamValue, reply, op);
    }
    // pull vcard entry request
    else if (type.equals(TYPE_VCARD)) {
      return pullVcardEntry(appParam, appParamValue, op, name, mCurrentPath);
    }
    // down load phone book request
    else if (type.equals(TYPE_PB)) {
      return pullPhonebook(appParam, appParamValue, reply, op, name);
    } else {
      Log.w(TAG, "unknown type request!!!");
      return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
    }
  }