2017-06-15 | learn

Android PBAP Contacts

PBAP

Phone Book Access Profile 定义了两部设备间传输通讯录的协议。主要是为车载终端的 Hands-free 使用场景设计,可以将移动设备上的通讯录同步到车载设备上。也可用于客户端请求访问储存在服务端的通讯录。

OBEX

OBject EXchange 的缩写。OBEX 是一个在设备间交换二进制对象的协议。

它在设计上和 HTTP 协议相似,为 client 和 server 提供可靠连接来传输数据。
主要区别有:

  • HTTP 通常在 TCP/IP 连接下使用。OBEX 也可以,不过更常见是用于红外和蓝牙这些设备的连接上,或者 USB 设备
  • HTTP 的传输内容为可读文本,OBEX 是二进制,更利于在设备资源有限的情况下解析
  • HTTP 的每个请求之间是无关联的,而 OBEX 的每个请求可能影响后续请求

For Android

在 Android 源码中已经有 PBAP 协议实现,不过处于 hide 状态,简单点可以把源码拷贝到自己的包名下使用。

相关代码:

使用方式:

  1. connect

    1
    2
    3
    4
    5
    // 首先和普通蓝牙一样通过适配器获取设备,然后构造 BluetoothPbapClient 对象
    private fun getPBAPClient(address: String) = BluetoothPbapClient(btAdapter?.getRemoteDevice(address), handler)
    // 尝试链接
    pbapClient = getPBAPClient(address)
    pbapClient?.connect()

    handler 用来处理整个过程中的消息(BluetoothPbapClient#EVENT_*), 和已有逻辑使用同一个 handler 时需要注意不要和自己的消息冲突, 最好单独创建一个 handler .

    最终会调用以下函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // android.bluetooth.client.pbap.BluetoothPbapSession$RfcommConnectThread::run
    // 建立一个连接
    mSocket = mDevice.createRfcommSocketToServiceRecord(
    UUID.fromString(PBAP_UUID));
    mSocket.connect();
    // 初始化 OBEX
    BluetoothPbapObexTransport transport;
    transport = new BluetoothPbapObexTransport(mSocket);
    // 发送连接成功 MSG
    mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport).sendToTarget();
  2. Pull

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 拉取数据
    pbapClient?.pullPhoneBook(BluetoothPbapClient.PB_PATH)

    /**
    * Pulls complete phone book.
    *
    * @param pbName absolute path to the phone book
    * @param filter bit mask which indicates which fields of the vCard hall be
    * included in each entry of the resulting list
    * @param format vCard format of entries in the resulting list
    * @param maxListCount limits number of entries in the response
    * @param listStartOffset offset to the first entry of the list that would
    * be returned
    * @return <code>true</code> if request has been sent successfully;
    * <code>false</code> otherwise; upon completion PCE sends
    * {@link #EVENT_PULL_PHONE_BOOK_DONE} or
    * {@link #EVENT_PULL_PHONE_BOOK_ERROR} in case of failure
    */
    @Override
    public boolean pullPhoneBook(String pbName, long filter,
    byte format, int maxListCount,int listStartOffset)
    • pbName: 表示想要拉取的数据, 包括并不限于通讯录, 通话记录, 未接来电, etc(详见 BluetoothPbapClient#*_PATH)
    • Filter: 可以过滤掉 VCard 中不需要的字段(0x07 -> NameAndNumberOnly)

最终会通过 BluetoothPbapClient.EVENT_PULL_PHONE_BOOK_DONE 消息的 msg.obj 字段把一个 ArrayList<VCardEntry> 对象传递到 handler.

测试结果

4.4.4 设备连接 7.1 手机: 成功 (手机需点同意访问通讯录)
7.1 手机连接 7.1 手机: 成功 (两部手机都要点同意访问通讯录)

Ref