一、问题描述
在苹果手机中, 会将 只有名字,没有电话号码的 vcard 传到 车机侧。 但是应用侧确显示不出来。针对这个问题,我们深入探讨一下,究竟是谁把他给过滤掉了。
下面的log 是从 btsnoop 中导出的。
c
BEGIN:VCARD
VERSION:3.0
FN:空号测试
N:空号测试
TEL:
END:VCARD
BEGIN:VCARD
VERSION:3.0
FN:Konghao
N:Konghao
TEL:
END:VCARD
备注:
- 但是用其他手机, 例如 IQOO Nevo 3E 上述 vcard 并不会 通过蓝牙 传递到 车机侧, 只有 苹果手机会传。
二、源码回溯
当车机解析手机传递过来的 vcard 时, 将触发 VCardEntry.java::addProperty. 调用流程可以参考 【android bluetooth 协议分析 18】【PBAP详解 1】【为何我们的通话记录中会出现1-521-8xx-1x9x】 这里不再重复。
- android/frameworks/opt/vcard/java/com/android/vcard/VCardEntry.java
c
public void addProperty(final VCardProperty property) {
final String propertyName = property.getName();
final Map<String, Collection<String>> paramMap = property.getParameterMap();
final List<String> propertyValueList = property.getValueList();
byte[] propertyBytes = property.getByteValue();
if ((propertyValueList == null || propertyValueList.size() == 0)
&& propertyBytes == null) {
return;
}
final String propValue = (propertyValueList != null
? listToString(propertyValueList).trim()
: null);
if (propertyName.equals(VCardConstants.PROPERTY_VERSION)) {
// vCard version. Ignore this.
} else if (propertyName.equals(VCardConstants.PROPERTY_FN)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_NAME)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_N)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_NICKNAME)
|| propertyName.equals(VCardConstants.ImportOnly.PROPERTY_X_NICKNAME)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_SOUND)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_ADR)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_EMAIL)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_ORG)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_TITLE)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_ROLE)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_PHOTO)
|| propertyName.equals(VCardConstants.PROPERTY_LOGO)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_TEL)) {
String phoneNumber = null;
boolean isSip = false;
if (VCardConfig.isVersion40(mVCardType)) {
...
} else {
phoneNumber = propValue;
}
if (isSip) {
...
} else {
if (propValue.length() == 0) {
return; // 这里当 TEL 标签 空时,直接返回了。 这里直接将 TEL 为空的联系人给过滤掉了。
}
final Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
final Object typeObject = VCardUtils.getPhoneTypeFromStrings(typeCollection,
phoneNumber);
final int type;
final String label;
if (typeObject instanceof Integer) {
type = (Integer) typeObject;
label = null;
} else {
type = Phone.TYPE_CUSTOM;
label = typeObject.toString();
}
final boolean isPrimary;
if (typeCollection != null &&
typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) {
isPrimary = true;
} else {
isPrimary = false;
}
addPhone(type, phoneNumber, label, isPrimary);
}
} else if (propertyName.equals(VCardConstants.PROPERTY_X_SKYPE_PSTNNUMBER)) {
...
} else if (sImMap.containsKey(propertyName)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_NOTE)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_URL)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_BDAY)) {
mBirthday = new BirthdayData(propValue);
} else if (propertyName.equals(VCardConstants.PROPERTY_ANNIVERSARY)) {
mAnniversary = new AnniversaryData(propValue);
} else if (propertyName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
mNameData.mPhoneticGiven = propValue;
} else if (propertyName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
mNameData.mPhoneticMiddle = propValue;
} else if (propertyName.equals(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME)) {
mNameData.mPhoneticFamily = propValue;
} else if (propertyName.equals(VCardConstants.PROPERTY_IMPP)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_X_SIP)) {
...
} else if (propertyName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) {
...
} else if (propertyName.toUpperCase().startsWith("X-")) {
...
} else {
}
// Be careful when adding some logic here, as some blocks above may use "return".
}
将 return 注释掉后, 电话号码为空的联系人,也可以正常在车机进行显示了。