Andorid BluetoothSocket的工作原理之一

       这是2020年笔者在Android Framework层对接(替换)原生蓝牙实现时整理的内容,一直懒得发出来;关于笔者在Android Framework层的对接,在关于Android Bluetooth(安卓蓝牙)在车载产品中的使用变化_YingbinLi 蓝牙的博客-CSDN博客_android 车载蓝牙中有相关描述;

本文继续将以问答形式讲述Android BluetoothSocket的工作原理,通过理解整个BluetoothSocket工作原理,笔者替换的BluetoothSocket实现接口,可完全模拟原生BluetoothSocket行为;

以下描述中提到的“原生BT”指的是Android原生蓝牙。

本文截图中内容基于Android 9(P);

问题点1: 原生BT中,关于使用socket 作为服务端和客户端的接口定义?

Framework层提供socket接口的分别在以下4个Java files中:

BluetoothServerSocket.java---具体的服务端接口实现;

BluetoothSocket.java-----具体的客户端接口实现;

BluetoothAdapter.java提供服务端监听时需要的BluetoothServerSocket并完成bindListen动作,然后调用accept

 BluetoothDevice.java提供客户端连接时需要的BluetoothSocket,然后再调用connect

以上4file的关系是:

BluetoothAdapter.java提供服务端监听接口,当监听端口创建成功后,返回BluetoothServerSocket对象,而这些监听端口的核心都是调用“new BluetoothServerSocket”实现,如截图所示 

BluetoothDevice.java提供客户端连接接口,当主动连接端口创建成功后,返回BluetoothSocket对象,实现的核心是“new BluetoothSocket”

需要注意的是BluetoothServerSocketBluetoothSocket并不是完全独立的,BluetoothServerSocket的核心实现其实也是” new BluetoothSocket”,所以当我们需要替换Framework层的socket 实作时,只要修改BluetoothSocket中的方法即可;

 ​​​​

 

问题点2:原生BT中,Java层如何解析从底层BlueDroid socket 发送来的数据?

Note:在原生BT中,PBAP的连接是在Java层通过socket 形式和BlueDroid进行关联,其并没有像其他profile那样有对接的JNI接口,同时下载到的电话本数据也是通过socket进行传送所以当前具体问题点是:当BlueDroid通过bta_jv_port_data_co_cback 发出PBAP data时,此时Java层是如何处理的? 

 ----原生BT的电话本及通话记录,通过ContactsContract进行保存;

-->发起连接动作是connectSocket,最终调用frameworkBluetoothSocket.java 中的connect方法;

 -->连接上后,Java(BT Service)得到的反馈是MSG_CONNECTION_COMPLETE

 

而触发MSG_CONNECTION_COMPLETE的是connectObexSession留意里面的

HeaderSet connectionResponse = mObexSession.connect(connectionRequest);其应该是阻塞形式;

 

-->连接上后会自动触发下载指令:MSG_DOWNLOAD里面挂载的保存数据库操作是PhonebookPullRequest.java 的onPullComplete àapplyBatch也需留意CallLogPullRequest.java

-->当连接成功时,BlueDroid 通过bta_jv_port_data_co_cback 告知RFCOMM 连接成功;

最终连接状态也是通过socket send 函数发出;

问题点3原生BT中,由listenUsingRfcommWithServiceRecord触发的监听和SDP 注册流程;

在原生BT中,当需要注册特殊UUID 或者SPP 时,

使用的是listenUsingRfcommWithServiceRecord,此方法实现两个功能:

1 通过btsock_listen -->btsock_rfc_listen -->alloc_rfc_slot-->socketpair 建立一个套接字;用于实现JavalistenRead write

Note:需要理解的是:这里的socket通讯用于在BlueDroid层和Framework之间传送指令和data;而socket建立并不等同于Bluedroid中的RFCOMM 通道建立,这是两回事

2 在底层BlueDroidSDP中添加;

Java层的监听流程:

-->执行到bindListen  通过AIDL 接口调用到JNI层的createSocketChannel;

 

-->最终对应到btsock_listen;

 

 小结:listenUsingRfcommWithServiceRecord最终调用到JNI 层的btsock_listen 方法,把UUID 等传送,并在BlueDroidSDP中注册上此UUID

触发btsock_listen的具体流程:

-->BlueDroid 中触发btsock_listen;

Notebtsock_listenBlueDroid中是以profile 接口形式被注册

 

-->执行到btsock_rfc_listen

在里面执行“BTA_JvGetChannelId(BTA_JV_CONN_TYPE_RFCOMM

-->执行到BTA_JvGetChannelId;

-->执行到bta_jv_get_channel_id;

-->通过Message BTA_JV_GET_SCN_EVT, 定位到API  jv_dm_cback

在此case 里面分别执行send_app_scn BTA_JvCreateRecordByUser;

 以上截图中的“send_app_scn”是当前UUID BT 中对应的Channel ID,而Java层接收位置是:Framework 的BluetoothSocket.java 方法bindListen中:这里的监听有固定长度,所以发送端也是发送固定长度4Byte (rfc_slot_t 中的“int len)checking 发现客户端在执行BluetoothSocket.java中的connect内也会阻塞等待获取到这个Channel ID;

 

 

 

-->执行到BTA_JvCreateRecordByUser,并在里面执行bta_jv_create_record;

-->执行到bta_jv_create_record;

-->通过Message 定位到BTA_JV_CREATE_RECORD_EVT;

-->执行create_server_sdp_record;

-->执行到add_rfc_sdp_rec;

 -->最终执行到add_rfc_sdp_by_uuid,并开始脱离socket, 回归到执行Bluedroid SDP注册;

问题点延伸:在执行listenUsingRfcommWithServiceRecord得到socket 后,一般都会执行accept 动作,而这里的accept的具体含义

--原生BT Framework中的accept不能直接理解为socket中的accept指令,其内部使用read实现一直监听套接字端口,进而实现阻塞动作;

--通过tracing 原生Code 发现:

这里的waitSocketSignal监听固定长度数据20ByteSOCK_SIGNAL_SIZE”,这20Byte 内容对应的是结构体“sock_connect_signal_t”大小;

 

---所以Java层的accept实质是通过执行read阻塞等待BlueDroid 回传的BT Address等讯息,因socket早在listenUsingRfcommWithServiceRecord时就通过socketpair建立;

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值