PBAP
PBAP:Phone Book Access Profile的简称,电话本访问协议,是一种基于OBEX的上层协议, 该协议可以同步手机这些具有电话本功能设备上的通讯录和通话记录等联系人数据。 PBAP协议规定两种角色:
PSE:Phone Book Server Equipment,拥有电话本源数据的设备,作为服务端,比如手机。
PCE:Phone Book Client Equipment,向PSE端请求电话本信息的设备,作为客户端,比如车载
PSE是手机等带有SIM卡的设备,存储源数据的联系人信息可能是手机也可能是SIM卡。PCE在同步获取相关数据时需要指明从哪个路径下获取对应的数据,即从手机同步还是从SIM卡中同步数据。
phone book object
PBAP phone book 对象有7种类型:
pb(Phonebook): 主电话簿(所有本地联系人)
ich(Incoming Call History): 来电记录
och(Outgoing Call History): 去电记录
mch(Missed Call History): 未接来电记录
cch(Combined Call History): 所有通话记录(合并 ich+och+mch)
spd(Speed Dial): 速拨号码列表
fav(Favorites): 收藏/常用联系人
在PBAP协议中,电话薄对象有两种表现形式:
File Representations:
文件表示分为两类:列表描述文件
phone book object(pb): pb.vcf
Incoming Calls History object (ich): ich.vcf
Outgoing Calls History object (och): och.vcf
Missed Calls History object (mch): mch.vcf
Combined Calls History object (cch): cch.vcf
Speed-Dial object (spd): spd.vcf
Favorite Contacts object (fav): fav.vcf
虚拟vCard文件: 文件命名格式(Handle):
00000001.vcf (本地条目)
0000ABCD.vcf (本地条目,十六进制)
Folder Representations:
文件夹对应不同的电话簿存储位置和通话记录类型,使用树形结构 ├── telecom/ (设备本地存储) │ ├── pb (主电话簿 - Phonebook) │ ├── ich (来电记录 - Incoming Calls) │ ├── och (去电记录 - Outgoing Calls) │ ├── mch (未接来电 - Missed Calls) │ └── cch (所有通话 - Combined Calls) ├── SIM1/ (第一张 SIM 卡) │ ├── pb (SIM 卡电话簿) │ └── … (部分实现支持 ich/och 等) ├── SIM2/ (第二张 SIM 卡,双卡设备) │ └── pb └── (厂商自定义扩展)
联系人路径:
手机路径:telecom/
SIM卡路径:SIM1/telecom/
phone book entry format
Phone Book Object 中的每个独立条目(Individual Entry)均以vCard格式表示。
PSE必须同时支持 vCard 2.1 和 vCard 3.0.
PCE通过 Format 参数请求指定版本.
默认编码:UTF-8(无论vCard版本,必须使用UTF-8编码).
phone book entry 扩展属性
X-IRMC-CALL-DATETIME(ich/och/mch/cch)
vCard 2.1: X-IRMC-CALL-DATETIME;MISSED:20050320T100000
vCard 3.0:X-IRMC-CALL-DATETIME;TYPE=MISSED:20050320T100000
X-IRMC-CALL-DATETIME;MISSED:(即使没有时间信息,该属性也必须存在)
X-BT-SPEEDDIALKEY(spd/fav): Speed-dial shortcut - 用于标识联系人的快速拨号键
X-BT-UID(pb): 联系人的唯一标识符(128位), 32字符十六进制(0-9, A-F(大写)) PBAP 联系人唯一标识
X-BT-UCI:{client}:{user_id}
PBAP 功能
Call Histories
同一号码在不同文件夹(ich/och/mch)中的 handle 相互独立(och/ich XX.vcf 不同)
不同文件夹中的 handle 值可能相同,也可能不同
与主电话簿(pb)的静态 handle 不同,通话记录使用动态handle
vCard-Listing Object (x-bt/vcard-listing)
浏览电话簿时返回的 XML 格式数据结构。当 PCE(Phone Book Client,如车载系统)使用 PullvCardListing 功能从 PSE(Phone Book Server,如手机) 获取联系人列表时,返回的就是这种格式的数据
<?xml version="1.0"?> <!DOCTYPE vcard-listing SYSTEM "vcard-listing.dtd"> <vCard-listing version="1.0"> <card handle="0.vcf" name="Bolt;Usain;;;"/> <card handle="1.vcf" name="Sanvi;Nithisha;;;"/> </vCard-listing>
功能支持列表:
Download:双方都必须支持才可用
Browsing:双方都必须支持才可用
Database Identifier:PSE 生成,PCE 存储
Folder Version Counters:PSE 生成,PCE 存储
vCard Selecting:PSE 过滤,PCE 解析
Enhanced Missed Calls:PSE 通知,PCE 显示
X-BT-UCI vCard Property:vCard 属性支持
X-BT-UID vCard Property:vCard 属性支持
Referencing Contacts:跨协议引用
Contact Image Default Format:图片格式协商
Phone Book Download Feature
pullphonebook:PullPhonebook 是 PBAP 协议中用于下载完整电话簿的核心功能,基于 OBEX GET 操作实现。
Phone Book Browsing Feature
setphonebook:用于设置当前路径/浏览文件夹的核心操作
pullvcardlisting:用于获取联系人列表的核心功能,返回vCard-Listing Object(XML格式,包含文件夹中所有条目的句柄和名称信息
pullvcardentry:是 PBAP 协议中用于获取单个联系人完整 vCard 数据的核心操作,基于 OBEX GET 命令实现
PBAP 参数说明
PullPhoneBook Function
Request
Opcode:GET (0x03 或 0x83)(M)
Packet Length(M)
Connection ID(OBEX 连接ID)(M)
Single Response Mode(取决obex版本)
Single Response Mode Param(取决obex版本)
Name(M)
Type: x-bt/phonebook
Application Parameters
PropertySelector
Format(vCard 2.1/vCard 3.0)
MaxListCount
ListStartOffset(列表起始偏移量)
ResetNewMissedCalls(重置未接来电计数)
vCardSelector(vCard选择器)
vCardSelectorOperator(vCard选择器操作符)
Response
Code:Response (0x90 (Continue)/0xA0 (Success) 或错误码)
Packet Length
Single Response Mode(取决obex版本)
Single Response Mode Param(取决obex版本)
Application Parameters
PhonebookSize
NewMissedCalls
PrimaryFolderVersion
SecondaryFolderVersion
DatabaseIdentifier
Body/End of Body(vCard对象数据)
SetPhoneBook Function
Opcode: SETPATH (0x05)(M)
Packet Length(M)
Connection ID
Flags
Name
PullvCardListing Function
Request
Opcode:GET (0x03 或 0x83)(M)
Packet Length(M)
Connection ID(OBEX 连接ID)(M)
Single Response Mode(取决obex版本)
Single Response Mode Param(取决obex版本)
Name(M)
Type: x-bt/vcard-listing
Application Parameters
Order
SearchValue
SearchProperty
MaxListCount
ListStartOffset(列表起始偏移量)
ResetNewMissedCalls(重置未接来电计数)
vCardSelector(vCard选择器)
vCardSelectorOperator(vCard选择器操作符)
Response
Code:Response (0x90 (Continue)/0xA0 (Success) 或错误码)
Packet Length
Single Response Mode(取决obex版本)
Single Response Mode Param(取决obex版本)
Application Parameters
PhonebookSize
NewMissedCalls
PrimaryFolderVersion
SecondaryFolderVersion
DatabaseIdentifier
Body/End of Body(vCard对象数据)
PullvCardEntry Function
Request
Opcode:GET (0x03 或 0x83)(M)
Packet Length(M)
Connection ID(OBEX 连接ID)(M)
Single Response Mode(取决obex版本)
Single Response Mode Param(取决obex版本)
Name(M)
Type: x-bt/vcard
Application Parameters
PropertySelector
Format
Response
Code:Response (0x90 (Continue)/0xA0 (Success) 或错误码)
Packet Length
Single Response Mode(取决obex版本)
Single Response Mode Param(取决obex版本)
Application Parameters
DatabaseIdentifier
Body/End of Body(vCard对象数据)
PABP获取电话簿的完整步骤
建立蓝牙连接并授权
API 调用: bt_pbap_client_connect_request()
预期事件: BT_NOTIFY_PBAP_PROFILE_CONNECTED
详细步骤:
确保手机端蓝牙已开启并处于可发现状态
发起 PBAP 客户端连接请求
等待手机端弹出的授权对话框,必须在手机上点击”允许”或”授权”访问联系人
监听连接成功事件,确认 BT_NOTIFY_PBAP_PROFILE_CONNECTED 回调触发
注意事项: 如果手机拒绝授权,连接会失败或无法获取联系人数据;部分手机需要在蓝牙配对时勾选”共享联系人”选项
设置电话簿路径
API 调用: bt_pbap_client_set_pb()
预期事件: BT_NOTIFY_PBAP_SET_PATH_CFM(路径设置确认)
详细步骤:
指定目标路径,常用路径包括:
telecom/pb.vcf —— 主电话簿(SIM 卡 + 手机存储的联系人)(对应参数:PBAP_LOCAL, PBAP_PB)
sim1/telecom/pb.vcf —— SIM 卡 1 的电话簿(对应参数:PBAP_SIM1, PBAP_PB)
sim2/telecom/pb.vcf —— SIM 卡 2 的电话簿(双卡手机),暂时不支持
发送设置路径请求
等待 BT_NOTIFY_PBAP_SET_PATH_CFM 确认事件,验证路径是否设置成功
注意事项: 不同手机品牌支持的路径可能略有差异;如果路径错误会收到失败回调
获取电话薄
API 调用: bt_pbap_client_pull_pb()
预期事件:
BT_NOTIFY_PBAP_PULL_PB_CMPL —— 整个电话簿拉取完成
BT_NOTIFY_PBAP_VCARD_ITEM_IND —— 单个 vCard 条目到达指示(可能多次触发)
详细步骤:
发起拉取请求,可附带过滤参数(如只获取姓名和电话号码,减少传输量)
处理实时数据流: 对于每个联系人条目,会收到 BT_NOTIFY_PBAP_VCARD_ITEM_IND 事件,事件中包含单个 vCard 数据
解析每个 vCard 条目(通常为 vCard 2.1 或 3.0 格式),提取:
FN 或 N 字段:联系人姓名
等待 BT_NOTIFY_PBAP_PULL_PB_CMPL 事件,表示所有联系人已传输完毕
注意事项: 联系人较多时,vCard 条目指示事件会分批触发
本文档主要是基于Sifli SDK,介绍对PBAP的PCE基本功能支持。涉及文件如下:
bts2_app_interface
bts2_app_pbap_c
PABP初始化
PBAP初始化的函数:bt_pbap_clt_init,PBAP相关的状态、标志赋初始值
void bt_pbap_clt_init(bts2_app_stru *bts2_app_data)
{
local_inst = (bts2s_pbap_clt_inst_data *)bmalloc(sizeof(bts2s_pbap_clt_inst_data));
// Must allocate successful
BT_ASSERT(local_inst);
local_inst->pbap_clt_st = BT_PBAPC_IDLE_ST;
local_inst->is_valid_vcard = FALSE;
local_inst->elem_index = BT_PBAP_ELEM_VCARD_IDLE;
local_inst->pbab_vcard = NULL;
local_inst->mfs = pbap_clt_get_max_mtu();
local_inst->rmt_supp_repos = 0;
local_inst->curr_cmd = BT_PBAP_CLT_IDLE;
local_inst->curr_repos = PBAP_LOCAL;
local_inst->curr_phonebook = PBAP_PB;
local_inst->target_repos = PBAP_UNKNOWN_REPO;
local_inst->target_phonebook = PBAP_UNKNOWN_PHONEBOOK;
local_inst->cur_file_hdl = NULL;
}
PABP获取联系人名称的功能
由于手机的电话本太多同步到手表端数据太多,因此采用在通话过程中通过号码去手机端获取联系人的方式。 注:此功能需要在配对时在手机端给予相应的权限。
PBAP连接设备接口:
bts2_app_interface连接接口:bt_interface_conn_ext
bts2_app_pbap_c连接接口:bt_pbap_clt_conn_to_srv
PBAP连接断开设备接口:
bts2_app_interface断开连接接口:bt_interface_disc_ext
bts2_app_pbap_c断开接口:bt_pbap_clt_disc_to_srv
PBAP选择设置获取联系人存储库(连接时已默认设置为手机存储联系人):
bts2_app_pbap_c设置获取联系人存储库接口:bt_pbap_client_set_pb
PBAP通过号码获取联系人名称(有通话时已调用):
bts2_app_pbap_c设置获取联系人存储库接口:bt_pbap_client_get_name_by_number
联系人名称event:BT_NOTIFY_PBAP_VCARD_LIST_ITEM_IND
联系人名称获取结束event:BT_NOTIFY_PBAP_VCARD_LIST_CMPL
//register notify event handle function start
// step1: 通过接口将PBAP 建立连接成功
// step2:设置获取联系人路径 bt_pbap_client_set_pb(手机路径:telecom/xxx.vcf/ SIM卡路径:SIM1/telecom/xxx.vcf)
// step3: bt_pbap_client_get_name_by_number 传入号码获取联系人名字
int bt_sifli_notify_pbap_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
// PBAP连接成功
case BT_NOTIFY_PBAP_PROFILE_CONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
break;
}
// PBAP断开连接成功
case BT_NOTIFY_PBAP_PROFILE_DISCONNECTED:
{
bt_notify_profile_state_info_t *profile_info = (bt_notify_profile_state_info_t *)data;
break;
}
// 联系人handle值(1.vcf) + 联系人名字信息
case BT_NOTIFY_PBAP_VCARD_LIST_ITEM_IND:
{
pbap_vcard_listing_item_t *list_item = (pbap_vcard_listing_item_t *)data;
break;
}
// 联系人名字获取完毕
case BT_NOTIFY_PBAP_VCARD_LIST_CMPL:
{
break;
}
default:
return -1;
}
return 0;
}
static int bt_sifli_notify_common_event_hdl(uint16_t event_id, uint8_t *data, uint16_t data_len)
{
switch (event_id)
{
//bt功能开启成功,可以正常使用
case BT_NOTIFY_COMMON_BT_STACK_READY:
{
break;
}
//bt关闭成功
case BT_NOTIFY_COMMON_CLOSE_COMPLETE:
{
break;
}
// ACL 连接成功
case BT_NOTIFY_COMMON_ACL_CONNECTED:
{
bt_notify_device_acl_conn_info_t *acl_info = (bt_notify_device_acl_conn_info_t *) data;
//device acl connected
break;
}
// ACL 断开连接成功
case BT_NOTIFY_COMMON_ACL_DISCONNECTED:
{
bt_notify_device_base_info_t *device_info = (bt_notify_device_base_info_t *)data;
//device acl disconnected
break;
}
default:
return -1;
}
return 0;
}
static int bt_notify_handle(uint16_t type, uint16_t event_id, uint8_t *data, uint16_t data_len)
{
int ret = -1;
switch (type)
{
case BT_NOTIFY_COMMON:
{
ret = bt_sifli_notify_common_event_hdl(event_id, data, data_len);
}
break;
case BT_NOTIFY_PBAP:
{
bt_sifli_notify_pbap_event_hdl(event_id, data, data_len);
}
break;
default:
break;
}
return 0;
}
int app_bt_notify_init(void)
{
bt_interface_register_bt_event_notify_callback(bt_notify_handle);
return 0;
}
INIT_ENV_EXPORT(app_bt_notify_init);
//register notify event handle function end