Android 9.0 蓝牙通讯录 BluetoothPbapClient

蓝牙通讯录主要包含联系人和通话记录
一、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{

   @Override
   public void onServiceConnected(int profile, BluetoothProfile proxy) {
      Log.d(TAG,"Bluetooth service connected profile == " + profile);
      if (profile == BluetoothProfile.PBAP_CLIENT) {
         mPbapClient = (BluetoothPbapClient) proxy;
         isPbapProfileReady = true;
      }
   }

   @Override
   public 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;

    @Override
    public 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);
    }

    @Override
    public 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;
    }

    @Override
    public void exit() {
        mSdpReceiver.unregister();
        mSdpReceiver = null;
    }

    private class SDPBroadcastReceiver extends BroadcastReceiver {
        @Override
        public 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.2
        BluetoothPbapRequestPullPhoneBook 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;
    }
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值