private final int pullVcardEntry(
      byte[] appParam,
      AppParamValue appParamValue,
      Operation op,
      final String name,
      final String current_path) {
    if (name == null || name.length() < VCARD_NAME_SUFFIX_LENGTH) {
      if (D) Log.d(TAG, "Name is Null, or the length of name < 5 !");
      return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
    }
    String strIndex = name.substring(0, name.length() - VCARD_NAME_SUFFIX_LENGTH + 1);
    int intIndex = 0;
    if (strIndex.trim().length() != 0) {
      try {
        intIndex = Integer.parseInt(strIndex);
      } catch (NumberFormatException e) {
        Log.e(TAG, "catch number format exception " + e.toString());
        return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
      }
    }

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

    boolean vcard21 = appParamValue.vcard21;
    if (appParamValue.needTag == 0) {
      Log.w(TAG, "wrong path!");
      return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
    } else if (appParamValue.needTag == ContentType.PHONEBOOK) {
      if (intIndex < 0 || intIndex >= size) {
        Log.w(TAG, "The requested vcard is not acceptable! name= " + name);
        return ResponseCodes.OBEX_HTTP_OK;
      } else if (intIndex == 0) {
        // For PB_PATH, 0.vcf is the phone number of this phone.
        String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21);
        return pushBytes(op, ownerVcard);
      } else {
        return mVcardManager.composeAndSendPhonebookOneVcard(op, intIndex, vcard21, null, mOrderBy);
      }
    } else {
      if (intIndex <= 0 || intIndex > size) {
        Log.w(TAG, "The requested vcard is not acceptable! name= " + name);
        return ResponseCodes.OBEX_HTTP_OK;
      }
      // For others (ich/och/cch/mch), 0.vcf is meaningless, and must
      // begin from 1.vcf
      if (intIndex >= 1) {
        return mVcardManager.composeAndSendCallLogVcards(
            appParamValue.needTag, op, intIndex, intIndex, vcard21);
      }
    }
    return ResponseCodes.OBEX_HTTP_OK;
  }
  public BluetoothPbapObexServer(Handler callback, Context context) {
    super();
    mConnectionId = -1;
    mCallback = callback;
    mContext = context;
    mVcardManager = new BluetoothPbapVcardManager(mContext);

    // set initial value when ObexServer created
    mMissedCallSize = mVcardManager.getPhonebookSize(ContentType.MISSED_CALL_HISTORY);
    if (D) Log.d(TAG, "Initialize mMissedCallSize=" + mMissedCallSize);
  }
  /** Form and Send an XML format String to client for Phone book listing */
  private final int sendVcardListingXml(
      final int type,
      Operation op,
      final int maxListCount,
      final int listStartOffset,
      final String searchValue,
      String searchAttr) {
    StringBuilder result = new StringBuilder();
    int itemsFound = 0;
    result.append("<?xml version=\"1.0\"?>");
    result.append("<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">");
    result.append("<vCard-listing version=\"1.0\">");

    // Phonebook listing request
    if (type == ContentType.PHONEBOOK) {
      if (searchAttr.equals("0")) { // search by name
        itemsFound = createList(maxListCount, listStartOffset, searchValue, result, "name");
      } else if (searchAttr.equals("1")) { // search by number
        itemsFound = createList(maxListCount, listStartOffset, searchValue, result, "number");
      } // end of search by number
      else {
        return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
      }
    }
    // Call history listing request
    else {
      ArrayList<String> nameList = mVcardManager.loadCallHistoryList(type);
      int requestSize = nameList.size() >= maxListCount ? maxListCount : nameList.size();
      int startPoint = listStartOffset;
      int endPoint = startPoint + requestSize;
      if (endPoint > nameList.size()) {
        endPoint = nameList.size();
      }
      if (D) Log.d(TAG, "call log list, size=" + requestSize + " offset=" + listStartOffset);

      for (int j = startPoint; j < endPoint; j++) {
        // listing object begin with 1.vcf
        result.append(
            "<card handle=\"" + (j + 1) + ".vcf\" name=\"" + nameList.get(j) + "\"" + "/>");
        itemsFound++;
      }
    }
    result.append("</vCard-listing>");

    if (V) Log.v(TAG, "itemsFound =" + itemsFound);

    return pushBytes(op, result.toString());
  }
  private final int pullPhonebook(
      byte[] appParam,
      AppParamValue appParamValue,
      HeaderSet reply,
      Operation op,
      final String name) {
    // code start for passing PTS3.2 TC_PSE_PBD_BI_01_C
    if (name != null) {
      int dotIndex = name.indexOf(".");
      String vcf = "vcf";
      if (dotIndex >= 0 && dotIndex <= name.length()) {
        if (name.regionMatches(dotIndex + 1, vcf, 0, vcf.length()) == false) {
          Log.w(TAG, "name is not .vcf");
          return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
        }
      }
    } // code end for passing PTS3.2 TC_PSE_PBD_BI_01_C

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

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

    int requestSize = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount : pbSize;
    int startPoint = appParamValue.listStartOffset;
    if (startPoint < 0 || startPoint >= pbSize) {
      Log.w(TAG, "listStartOffset is not correct! " + startPoint);
      return ResponseCodes.OBEX_HTTP_OK;
    }

    // Limit the number of call log to CALLLOG_NUM_LIMIT
    if (appParamValue.needTag != BluetoothPbapObexServer.ContentType.PHONEBOOK) {
      if (requestSize > CALLLOG_NUM_LIMIT) {
        requestSize = CALLLOG_NUM_LIMIT;
      }
    }

    int endPoint = startPoint + requestSize - 1;
    if (endPoint > pbSize - 1) {
      endPoint = pbSize - 1;
    }
    if (D)
      Log.d(
          TAG,
          "pullPhonebook(): requestSize="
              + requestSize
              + " startPoint="
              + startPoint
              + " endPoint="
              + endPoint);

    String result = null;
    boolean vcard21 = appParamValue.vcard21;
    if (appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) {
      if (startPoint == 0) {
        String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21);
        if (endPoint == 0) {
          return pushBytes(op, ownerVcard);
        } else {
          return mVcardManager.composeAndSendPhonebookVcards(op, 1, endPoint, vcard21, ownerVcard);
        }
      } else {
        return mVcardManager.composeAndSendPhonebookVcards(op, startPoint, endPoint, vcard21, null);
      }
    } else {
      return mVcardManager.composeAndSendCallLogVcards(
          appParamValue.needTag, op, startPoint + 1, endPoint + 1, vcard21);
    }
  }
  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;
  }
  private int createList(
      final int maxListCount,
      final int listStartOffset,
      final String searchValue,
      StringBuilder result,
      String type) {
    int itemsFound = 0;
    ArrayList<String> nameList = mVcardManager.getPhonebookNameList(mOrderBy);
    final int requestSize = nameList.size() >= maxListCount ? maxListCount : nameList.size();
    final int listSize = nameList.size();
    String compareValue = "", currentValue, tmpCurrentValue;

    if (D)
      Log.d(
          TAG,
          "search by "
              + type
              + ", requestSize="
              + requestSize
              + " offset="
              + listStartOffset
              + " searchValue="
              + searchValue);

    if (type.equals("number")) {
      // query the number, to get the names
      ArrayList<String> names = mVcardManager.getContactNamesByNumber(searchValue);
      for (int i = 0; i < names.size(); i++) {
        compareValue = names.get(i).trim();
        if (D) Log.d(TAG, "compareValue=" + compareValue);
        for (int pos = listStartOffset; pos < listSize && itemsFound < requestSize; pos++) {
          currentValue = nameList.get(pos);
          if (D) Log.d(TAG, "currentValue=" + currentValue);
          if (currentValue.startsWith(compareValue)) {
            itemsFound++;
            result.append("<card handle=\"" + pos + ".vcf\" name=\"" + currentValue + "\"" + "/>");
          }
        }
        if (itemsFound >= requestSize) {
          break;
        }
      }
    } else {
      if (searchValue != null) {
        compareValue = searchValue.trim();
        compareValue = compareValue.toLowerCase();
      }
      for (int pos = listStartOffset; pos < listSize && itemsFound < requestSize; pos++) {
        currentValue = nameList.get(pos);
        tmpCurrentValue = currentValue.toLowerCase();
        if (D) Log.d(TAG, "currentValue=" + currentValue);
        if (searchValue == null) {
          itemsFound++;
          result.append("<card handle=\"" + pos + ".vcf\" name=\"" + currentValue + "\"" + "/>");
        } else {
          int sIndex = -1;
          do {
            tmpCurrentValue = tmpCurrentValue.substring(sIndex + 1);
            if (tmpCurrentValue.startsWith(compareValue)) {
              itemsFound++;
              result.append(
                  "<card handle=\"" + pos + ".vcf\" name=\"" + currentValue + "\"" + "/>");
              break;
            }
            sIndex = tmpCurrentValue.indexOf(' ');
          } while (sIndex > 0);
        }
      }
    }
    return itemsFound;
  }