/** To parse obex application parameter */
  private final boolean parseApplicationParameter(
      final byte[] appParam, AppParamValue appParamValue) {
    int i = 0;
    boolean parseOk = true;
    while ((i < appParam.length) && (parseOk == true)) {
      switch (appParam[i]) {
        case ApplicationParameter.TRIPLET_TAGID.FILTER_TAGID:
          i += 2; // length and tag field in triplet
          i += ApplicationParameter.TRIPLET_LENGTH.FILTER_LENGTH;
          break;
        case ApplicationParameter.TRIPLET_TAGID.ORDER_TAGID:
          i += 2; // length and tag field in triplet
          appParamValue.order = Byte.toString(appParam[i]);
          i += ApplicationParameter.TRIPLET_LENGTH.ORDER_LENGTH;
          break;
        case ApplicationParameter.TRIPLET_TAGID.SEARCH_VALUE_TAGID:
          i += 1; // length field in triplet
          // length of search value is variable
          int length = appParam[i];
          if (length == 0) {
            // If parse is not OK return false
            parseOk = false;
            break;
          }
          if (appParam[i + length] == 0x0) {
            appParamValue.searchValue = new String(appParam, i + 1, length - 1);
          } else {
            appParamValue.searchValue = new String(appParam, i + 1, length);
          }
          i += length;
          i += 1;
          break;
        case ApplicationParameter.TRIPLET_TAGID.SEARCH_ATTRIBUTE_TAGID:
          i += 2;
          appParamValue.searchAttr = Byte.toString(appParam[i]);
          i += ApplicationParameter.TRIPLET_LENGTH.SEARCH_ATTRIBUTE_LENGTH;
          break;
        case ApplicationParameter.TRIPLET_TAGID.MAXLISTCOUNT_TAGID:
          i += 2;
          if (appParam[i] == 0 && appParam[i + 1] == 0) {
            mNeedPhonebookSize = true;
          } else {
            int highValue = appParam[i] & 0xff;
            int lowValue = appParam[i + 1] & 0xff;
            appParamValue.maxListCount = highValue * 256 + lowValue;
          }
          i += ApplicationParameter.TRIPLET_LENGTH.MAXLISTCOUNT_LENGTH;
          break;
        case ApplicationParameter.TRIPLET_TAGID.LISTSTARTOFFSET_TAGID:
          i += 2;
          int highValue = appParam[i] & 0xff;
          int lowValue = appParam[i + 1] & 0xff;
          appParamValue.listStartOffset = highValue * 256 + lowValue;
          i += ApplicationParameter.TRIPLET_LENGTH.LISTSTARTOFFSET_LENGTH;
          break;
        case ApplicationParameter.TRIPLET_TAGID.FORMAT_TAGID:
          i += 2; // length field in triplet
          if (appParam[i] != 0) {
            appParamValue.vcard21 = false;
          }
          i += ApplicationParameter.TRIPLET_LENGTH.FORMAT_LENGTH;
          break;
        default:
          parseOk = false;
          Log.e(TAG, "Parse Application Parameter error");
          break;
      }
    }

    if (D) appParamValue.dump();

    return parseOk;
  }
  private final int pullVcardListing(
      byte[] appParam, AppParamValue appParamValue, HeaderSet reply, Operation op) {
    String searchAttr = appParamValue.searchAttr.trim();

    if (searchAttr == null || searchAttr.length() == 0) {
      // If searchAttr is not set by PCE, set default value per spec.
      appParamValue.searchAttr = "0";
      if (D) Log.d(TAG, "searchAttr is not set by PCE, assume search by name by default");
    } else if (!searchAttr.equals("0") && !searchAttr.equals("1")) {
      Log.w(TAG, "search attr not supported");
      if (searchAttr.equals("2")) {
        // search by sound is not supported currently
        Log.w(TAG, "do not support search by sound");
        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
      }
      return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
    } else {
      Log.i(TAG, "searchAttr is valid: " + searchAttr);
    }

    int size = mVcardManager.getPhonebookSize(appParamValue.needTag);
    int needSendBody = handleAppParaForResponse(appParamValue, size, reply, op);
    if (needSendBody != NEED_SEND_BODY) {
      return needSendBody;
    }

    if (size == 0) {
      if (V) Log.v(TAG, "PhonebookSize is 0, return.");
      return ResponseCodes.OBEX_HTTP_OK;
    }

    String orderPara = appParamValue.order.trim();
    if (TextUtils.isEmpty(orderPara)) {
      // If order parameter is not set by PCE, set default value per spec.
      orderPara = "0";
      if (D)
        Log.d(TAG, "Order parameter is not set by PCE. " + "Assume order by 'Indexed' by default");
    } else if (!orderPara.equals("0") && !orderPara.equals("1")) {
      if (V) Log.v(TAG, "Order parameter is not supported: " + appParamValue.order);
      if (orderPara.equals("2")) {
        // Order by sound is not supported currently
        Log.w(TAG, "Do not support order by sound");
        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
      }
      return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
    } else {
      Log.i(TAG, "Order parameter is valid: " + orderPara);
    }

    if (orderPara.equals("0")) {
      mOrderBy = ORDER_BY_INDEXED;
    } else if (orderPara.equals("1")) {
      mOrderBy = ORDER_BY_ALPHABETICAL;
    }

    int sendResult =
        sendVcardListingXml(
            appParamValue.needTag,
            op,
            appParamValue.maxListCount,
            appParamValue.listStartOffset,
            appParamValue.searchValue,
            appParamValue.searchAttr);
    return sendResult;
  }
  @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;
    }
  }