蓝牙通讯录主要包含联系人和通话记录
一、BluetoothPbapClient功能介绍
1、主要实现电话簿下载
2、电话号码簿访问协议(Phonebook Access Profile)
二、BluetoothPbapClient的使用

public void getProfileProxy() {boolean isPbapService = mAdapter.getProfileProxy(mContext, new ProxyServiceListener(), BluetoothProfile.PBAP_CLIENT);Log.i(TAG, "getProfileProxy"+isPbapService);
}
private final class ProxyServiceListener implements BluetoothProfile.ServiceListener{@Overridepublic void onServiceConnected(int profile, BluetoothProfile proxy) {Log.d(TAG,"Bluetooth service connected profile == " + profile);if (profile == BluetoothProfile.PBAP_CLIENT) {mPbapClient = (BluetoothPbapClient) proxy;isPbapProfileReady = true;}}@Overridepublic void onServiceDisconnected(int profile) {Log.d(TAG, "BluetoothPbapClient Profile Proxy Disconnected");if (profile == BluetoothProfile.PBAP_CLIENT) {isPbapProfileReady = false;mPbapClient = null;}}
}

2、BluetoothPbapClient连接

// 连接
public boolean connect(BluetoothDevice device) {if (null != mPbapClient) {return mPbapClient.connect(device);}Log.i(TAG, "mPbapClient == null");return false;
}
//断连
public boolean disconnect(BluetoothDevice device) {if (null != mPbapClient) {return mPbapClient.disconnect(device);}Log.i(TAG, "mPbapClient == null");return false;
}
//判断连接状态
public int getConnectionState() {if (null != mPbapClient) {List<BluetoothDevice> deviceList = mPbapClient.getConnectedDevices();if (deviceList.isEmpty()) {return BluetoothProfile.STATE_DISCONNECTED;} else {return mPbapClient.getConnectionState(deviceList.remove(0));}}return BluetoothProfile.STATE_DISCONNECTED;
}

三、源码分析
代码路径:

android/packages/apps/Bluetooth/src/com/android/bluetooth/pbapclient/PbapClientStateMachine.java
android/packages/apps/Bluetooth/src/com/android/bluetooth/pbapclient/PbapClientConnectionHandler.java
android/packages/apps/Bluetooth/src/com/android/bluetooth/pbapclient/PbapClientService.java

1、在PbapClientStateMachine.java文件中内部类Connecting类中,当我们连接蓝牙后就会执行Connecting类中enter()方法,并且也会注册该广播android.bluetooth.device.action.SDP_RECORD

class Connecting extends State {private SDPBroadcastReceiver mSdpReceiver;@Overridepublic void enter() {if (DBG) {Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what);}onConnectionStateChanged(mCurrentDevice, mMostRecentState,BluetoothProfile.STATE_CONNECTING);mSdpReceiver = new SDPBroadcastReceiver();mSdpReceiver.register();mCurrentDevice.sdpSearch(BluetoothUuid.PBAP_PSE);mMostRecentState = BluetoothProfile.STATE_CONNECTING;// Create a separate handler instance and thread for performing// connect/download/disconnect operations as they may be time consuming and error prone.mHandlerThread =new HandlerThread("PBAP PCE handler", Process.THREAD_PRIORITY_BACKGROUND);mHandlerThread.start();mConnectionHandler =new PbapClientConnectionHandler.Builder().setLooper(mHandlerThread.getLooper()).setContext(mService).setClientSM(PbapClientStateMachine.this).setRemoteDevice(mCurrentDevice).build();sendMessageDelayed(MSG_CONNECT_TIMEOUT, CONNECT_TIMEOUT);}@Overridepublic boolean processMessage(Message message) {if (DBG) {Log.d(TAG, "Processing MSG " + message.what + " from " + this.getName());}switch (message.what) {case MSG_DISCONNECT:if (message.obj instanceof BluetoothDevice && message.obj.equals(mCurrentDevice)) {removeMessages(MSG_CONNECT_TIMEOUT);transitionTo(mDisconnecting);}break;case MSG_CONNECTION_COMPLETE:removeMessages(MSG_CONNECT_TIMEOUT);transitionTo(mConnected);break;case MSG_CONNECTION_FAILED:case MSG_CONNECT_TIMEOUT:removeMessages(MSG_CONNECT_TIMEOUT);transitionTo(mDisconnecting);break;case MSG_SDP_COMPLETE:mConnectionHandler.obtainMessage(PbapClientConnectionHandler.MSG_CONNECT,message.obj).sendToTarget();break;default:Log.w(TAG, "Received unexpected message while Connecting");return NOT_HANDLED;}return HANDLED;}@Overridepublic void exit() {mSdpReceiver.unregister();mSdpReceiver = null;}private class SDPBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (DBG) {Log.v(TAG, "onReceive" + action);}if (action.equals(BluetoothDevice.ACTION_SDP_RECORD)) {BluetoothDevice device =intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (!device.equals(getDevice())) {Log.w(TAG, "SDP Record fetched for different device - Ignore");return;}ParcelUuid uuid = intent.getParcelableExtra(BluetoothDevice.EXTRA_UUID);if (DBG) {Log.v(TAG, "Received UUID: " + uuid.toString());}if (DBG) {Log.v(TAG, "expected UUID: " + BluetoothUuid.PBAP_PSE.toString());}if (uuid.equals(BluetoothUuid.PBAP_PSE)) {sendMessage(MSG_SDP_COMPLETE,intent.getParcelableExtra(BluetoothDevice.EXTRA_SDP_RECORD));}}}public void register() {IntentFilter filter = new IntentFilter();filter.addAction(BluetoothDevice.ACTION_SDP_RECORD);mService.registerReceiver(this, filter);}public void unregister() {mService.unregisterReceiver(this);}}
}

2、蓝牙连接后会先通过socket去连接UUID

private boolean connectSocket() {try {/* Use BluetoothSocket to connect */if (mPseRec == null) {// BackWardCompatability: Fall back to create RFCOMM through UUID.Log.v(TAG, "connectSocket: UUID: " + BluetoothUuid.PBAP_PSE.getUuid());mSocket =mDevice.createRfcommSocketToServiceRecord(BluetoothUuid.PBAP_PSE.getUuid());} else if (mPseRec.getL2capPsm() != L2CAP_INVALID_PSM) {Log.v(TAG, "connectSocket: PSM: " + mPseRec.getL2capPsm());mSocket = mDevice.createL2capSocket(mPseRec.getL2capPsm());} else {Log.v(TAG, "connectSocket: channel: " + mPseRec.getRfcommChannelNumber());mSocket = mDevice.createRfcommSocket(mPseRec.getRfcommChannelNumber());}if (mSocket != null) {mSocket.connect();return true;} else {Log.w(TAG, "Could not create socket");}} catch (IOException e) {Log.e(TAG, "Error while connecting socket", e);}return false;
}
3、蓝牙socket连接成功后就会去Obex Client Session
case MSG_CONNECT:mPseRec = (SdpPseRecord) msg.obj;/* To establish a connection, first open a socket and then create an OBEX session */if (connectSocket()) {if (DBG) {Log.d(TAG, "Socket connected");}} else {Log.w(TAG, "Socket CONNECT Failure ");mPbapClientStateMachine.obtainMessage(PbapClientStateMachine.MSG_CONNECTION_FAILED).sendToTarget();return;}if (connectObexSession()) {mPbapClientStateMachine.obtainMessage(PbapClientStateMachine.MSG_CONNECTION_COMPLETE).sendToTarget();} else {mPbapClientStateMachine.obtainMessage(PbapClientStateMachine.MSG_CONNECTION_FAILED).sendToTarget();}break;

4、Obex Client Session连接

private boolean connectObexSession() {boolean connectionSuccessful = false;try {if (DBG) {Log.v(TAG, "Start Obex Client Session");}BluetoothObexTransport transport = new BluetoothObexTransport(mSocket);mObexSession = new ClientSession(transport);mObexSession.setAuthenticator(mAuth);HeaderSet connectionRequest = new HeaderSet();connectionRequest.setHeader(HeaderSet.TARGET, PBAP_TARGET);if (mPseRec != null) {if (DBG) {Log.d(TAG, "Remote PbapSupportedFeatures " + mPseRec.getSupportedFeatures());}ObexAppParameters oap = new ObexAppParameters();if (mPseRec.getProfileVersion() >= PBAP_V1_2) {oap.add(BluetoothPbapRequest.OAP_TAGID_PBAP_SUPPORTED_FEATURES,PBAP_SUPPORTED_FEATURE);}oap.addToHeaderSet(connectionRequest);}HeaderSet connectionResponse = mObexSession.connect(connectionRequest);connectionSuccessful =(connectionResponse.getResponseCode() == ResponseCodes.OBEX_HTTP_OK);if (DBG) {Log.d(TAG, "Success = " + Boolean.toString(connectionSuccessful));}} catch (IOException e) {Log.w(TAG, "CONNECT Failure " + e.toString());closeSocket();}return connectionSuccessful;
}

5、Obex Client Session连接成功后,就可以去下载手机端的联系人和通话记录

case MSG_DOWNLOAD:try {mAccountCreated = addAccount(mAccount);if (!mAccountCreated) {Log.e(TAG, "Account creation failed.");return;}// Start at contact 1 to exclued Owner Card PBAP 1.1 sec 3.1.5.2BluetoothPbapRequestPullPhoneBook request =new BluetoothPbapRequestPullPhoneBook(PB_PATH, mAccount,PBAP_REQUESTED_FIELDS, VCARD_TYPE_30, 0, 1);request.execute(mObexSession);PhonebookPullRequest processor =new PhonebookPullRequest(mPbapClientStateMachine.getContext(),mAccount);processor.setResults(request.getList());processor.onPullComplete();HashMap<String, Integer> callCounter = new HashMap<>();downloadCallLog(MCH_PATH, callCounter);downloadCallLog(ICH_PATH, callCounter);downloadCallLog(OCH_PATH, callCounter);} catch (IOException e) {Log.w(TAG, "DOWNLOAD_CONTACTS Failure" + e.toString());}break;

总结:
1、蓝牙连接后会先通过socket去连接UUID
2、socket连接上后会连接OBEX协议
3、OBEX协议连接上之后就可以去下载联系人和通话记录
连接上蓝牙后下载通讯录的日志

08-10 22:00:57.986  2629  4617 D PbapClientStateMachine: Enter Connecting: -2
08-10 22:00:57.987  2629  4617 D PbapClientStateMachine: Connection state 54:92:09:12:BF:E9: 0->1
08-10 22:01:00.372  2629  2629 V PbapClientStateMachine: onReceiveandroid.bluetooth.device.action.SDP_RECORD
08-10 22:01:00.372  2629  2629 V PbapClientStateMachine: Received UUID: 0000112f-0000-1000-8000-00805f9b34fb
08-10 22:01:00.373  2629  2629 V PbapClientStateMachine: expected UUID: 0000112f-0000-1000-8000-00805f9b34fb
08-10 22:01:00.373  2629  4617 D PbapClientStateMachine: Processing MSG 9 from Connecting
08-10 22:01:00.373  2629  4618 D PbapClientConnectionHandler: Handling Message = 1
08-10 22:01:00.374  2629  4618 V PbapClientConnectionHandler: connectSocket: channel: 19
08-10 22:01:00.674  2629  4618 D PbapClientConnectionHandler: Socket connected
08-10 22:01:00.674  2629  4618 V PbapClientConnectionHandler: Start Obex Client Session
08-10 22:01:00.677  2629  4618 D PbapClientConnectionHandler: Remote PbapSupportedFeatures 3
08-10 22:01:00.742  2629  4618 D PbapClientConnectionHandler: Success = true
08-10 22:01:00.742  2629  4617 D PbapClientStateMachine: Processing MSG 5 from Connecting
08-10 22:01:00.744  2629  4617 D PbapClientStateMachine: Enter Connected: 5
08-10 22:01:00.745  2629  4617 D PbapClientStateMachine: Connection state 54:92:09:12:BF:E9: 1->2
08-10 22:01:00.751  2629  4618 D PbapClientConnectionHandler: Handling Message = 3
08-10 22:01:00.798  2629  4618 D PbapClientConnectionHandler: Added account Account {name=54:92:09:12:BF:E9, type=com.android.bluetooth.pbapsink}

断开蓝牙后日志

08-10 22:42:09.088  2629  3393 D PbapClientStateMachine: Disconnect Request 54:92:09:12:BF:E9
08-10 22:42:09.088  2629  4617 D PbapClientStateMachine: Processing MSG 2 from Connected
08-10 22:42:09.088  2629  4617 D PbapClientStateMachine: Enter Disconnecting: 2
08-10 22:42:09.088  2629  4617 D PbapClientStateMachine: Connection state 54:92:09:12:BF:E9: 2->3
08-10 22:42:09.104  2629  4618 D PbapClientConnectionHandler: Handling Message = 2
08-10 22:42:09.104  2629  4618 D PbapClientConnectionHandler: Starting Disconnect
08-10 22:42:09.104  2629  4618 D PbapClientConnectionHandler: obexSessionDisconnectjavax.obex.ClientSession@c0dce50
08-10 22:42:09.679  2629  4618 D PbapClientConnectionHandler: Closing Socket
08-10 22:42:09.679  2629  4618 D PbapClientConnectionHandler: Closing socketandroid.bluetooth.BluetoothSocket@89d6849
08-10 22:42:09.680  2629  4618 D PbapClientConnectionHandler: Completing Disconnect
08-10 22:42:09.768  2629  4618 D PbapClientConnectionHandler: Removed account Account {name=54:92:09:12:BF:E9, type=com.android.bluetooth.pbapsink}
08-10 22:42:10.096  2629  4617 D PbapClientStateMachine: Processing MSG 7 from Disconnecting
08-10 22:42:10.097  2629  4617 D PbapClientStateMachine: Enter Disconnected: 7
08-10 22:42:10.097  2629  4617 D PbapClientStateMachine: Connection state 54:92:09:12:BF:E9: 3->0

遇到问题点:
1、当我们使用BluetoothPbapClient来访问连接状态时候,mPbapClient.connect一直返回是false,返回false的原因是因为:getPriority(device) <= BluetoothProfile.PRIORITY_OFF,该行代码返回false,返回false就导致不能同步手机端的联系人数据。
数据存放的路径:/data/user/0/com.android.providers.contacts/databases

//客户端
public boolean connect(BluetoothDevice device) {if (null != mPbapClient) {return mPbapClient.connect(device);}Log.i(TAG, "mPbapClient == null");return false;
}
//服务端
public boolean connect(BluetoothDevice device) {if (device == null) {throw new IllegalArgumentException("Null device");}enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");Log.d(TAG, "Received request to ConnectPBAPPhonebook " + device.getAddress());Log.d(TAG, "Received request to ConnectPBAPPhonebook   getPriority(device) " + getPriority(device));Log.d(TAG, "Received request to ConnectPBAPPhonebook  BluetoothProfile.PRIORITY_OFF  " + BluetoothProfile.PRIORITY_OFF);if (getPriority(device) <= BluetoothProfile.PRIORITY_OFF) {// return false;}synchronized (mPbapClientStateMachineMap) {PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);if (pbapClientStateMachine == null&& mPbapClientStateMachineMap.size() < MAXIMUM_DEVICES) {pbapClientStateMachine = new PbapClientStateMachine(this, device);pbapClientStateMachine.start();mPbapClientStateMachineMap.put(device, pbapClientStateMachine);return true;} else {Log.w(TAG, "Received connect request while already connecting/connected.");return false;}}
}
//把该行代码注释掉就可以解决connect返回false的问题,这样就可以正常同步手机端的联系人if (getPriority(device) <= BluetoothProfile.PRIORITY_OFF) {// return false;}

Android 9.0 蓝牙通讯录 BluetoothPbapClient相关推荐

  1. aptx android8,秒杀苹果无线音频!Android 8.0蓝牙音质支持aptxHD/LDAC

    原标题:秒杀苹果无线音频!Android 8.0蓝牙音质支持aptxHD/LDAC [TechWeb报道]8月25日消息,本周将迎来Android 8.0"奥利奥"系统推送,目前已 ...

  2. Android 9.0 蓝牙功能之一:蓝牙音乐

    Android 9.0 蓝牙功能之一:蓝牙音乐 本章节记录如何构建蓝牙音乐. 文章目录 Android 9.0 蓝牙功能之一:蓝牙音乐 主要流程 相关代码 其他要点: 蓝牙AG_EVENT广播 (手机 ...

  3. android 6.0蓝牙服务开启,Android应用开发之Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法...

    本文将带你了解Android应用开发Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法,希望本文对大家学Android有所帮助. 为提供更高的数据保护   Android6.0版本 ...

  4. android 6.0 蓝牙进程,Android6.0-蓝牙权限问题

    在Android 6.0,原来的蓝牙功能,发现扫描蓝牙设备时,无法获取到蓝牙设备:因为在6.0后,蓝牙这块增加一个动态权限:需要在程序中动态申请. 1)        在6.0版本前,使用蓝牙功能,只 ...

  5. Android 8.0 蓝牙唤醒 Ble 锁屏 保活 后台 持续扫描 进程拉活 自动唤醒

    主要是api的说明,嫌啰嗦的可以直接看demo,demo中有个检测锁屏时间重复开启扫描的代码,主要是如果APP没有获得电量或者后台运行的权限,只能持续后台运行几小时. 这个demo的作用是实现8.0以 ...

  6. android Ble4.0蓝牙开发之搜索慢、startLeScan()过时,6.0以上不需要定位权限也能快速搜索到蓝牙设备

    项目中需要用到android Ble蓝牙4.0开发技术,于是开启了蓝牙填坑之旅,说实话,蓝牙开发坑真多,跳出一个又进入下一个,每次遇到 问题,就觉得不可能解决了,还好在自己的摸索中,都一一的化解了,以 ...

  7. android 6.0蓝牙,Android 6.0 蓝牙搜索不到设备原因,MIUI权限申请机制方法

    为提供更高的数据保护 Android6.0版本上增加了关于Wifi和蓝牙的权限. 蓝牙搜索到设备需要用到定位服务,所以在开发中 targetSdkVersion 大于等于23(6.0) 需要在代码中进 ...

  8. android 4.0 蓝牙分析之二

    原址 packages/apps/Settings/src/com/Android/settings/bluetooth/BluetoothSettings.Java onCreateOptionsM ...

  9. android 4.0 蓝牙分析之一

    原址 SystemServer启动开始讲起,在SystemServer启动的时,会启动一个BluetoothService与BluetoothA2DPService的实例: Code: //     ...

最新文章

  1. 数据结构-求一个字符串中的某个子串(C语言)
  2. DataScience:深入探讨与分析机器学习中的数据处理之线性变换—标准化standardization、归一化Normalization/比例化Scaling的区别与联系
  3. tf.slice解析
  4. 如何抓取html请求,怎么获取请求头
  5. 利用计算机测地震是计算机的什么,计算机在气象预报、地震探测、导弹卫星轨迹等方面的应用都属于( )...
  6. java 在已有的so基础上封装jni_webshell中的分离免杀实践java篇
  7. ubuntu17安装mysql后数据库乱码_linux安装MySQL数据库,设置编码为utf8
  8. 大学英语综合教程四 Unit 8 课文内容英译中 中英翻译
  9. Linux下tomcat修改端口(80)
  10. linux查看weblogic的安装目录,linux下weblogic安装并部署项目,图文详细.
  11. 【快递100】 物流公司对应编码分享(截止到2021-09-19 最新数据)
  12. 输入10个互不相同的数字并分成5对,问有多少种分法。
  13. springboot启动时报错 错误:找不到或无法加载主类 xxx.SpringDemoApplication
  14. 利用小米数据库查找手机归属地
  15. useing eclipse formatting
  16. Leetcode 周赛题解 215
  17. Django 实现单点登录(SSO)
  18. 我是怎么提升写作能力的
  19. u盘计算机无法找到该项目,在电脑中删除文件时提示“找不到该项目”怎么办?...
  20. 单链表LinkedList的增删改查

热门文章

  1. 堆晶结构_火成堆晶成因的超镁铁质岩
  2. bam文件读取_bam格式文件处理大全(一)
  3. 渗透测试-Python安全工具编程基础
  4. 通信专业实务(三)——接入网
  5. 若依Vue分离版本 RuoYi-Vue管理系统部署
  6. android开发者 人数,Android开发者数量超iOS(转载)
  7. mysql区分大小写嘛_Mysql区分大小写问题
  8. java计算机毕业设计基于安卓Android微信小程序的共享单车租赁系统uniApp
  9. markman的下载与使用
  10. luogu P3899 [湖南集训]谈笑风生 线段树合并