问题点8:如何理解原生Android BT,在Framework层执行监听动作后但当被连接时二次执行“alloc_rfc_slot”的行为
---在之前的checking flow中我们了解到:
当Framework层执行listenUsingRfcommWithServiceRecord 实现监听动作时,
其在BlueDroid中触发btsock_listen --> btsock_rfc_listen--> alloc_rfc_slot并建立socketpair,
并把“slot->app_fd = fds[1];”返回给Java层用于数据监听;
但在实际中发现,如车机端通过listenUsingRfcommWithServiceRecord 注册SPP 监听后,当SPP被连接时的flow是:
-->rfcomm_cback 被触发,Event ID 是34: BTA_JV_RFCOMM_SRV_OPEN_EVT
-->API on_srv_rfc_connect,这个API就是当前问题讨论的重点;
on_srv_rfc_connect函数中进行多种处理,且当此函数返回时,其返回虽是“return srv_rs->id;”但其已经在第2步操作中变成了Slot B 的id;使得bta_jv_port_mgmt_sr_cback中的“p_pcb_new_listen->rfcomm_slot_id”变成了Slot B的id 值;
on_srv_rfc_connect函数内部:
-->第1步:先通过find_rfc_slot_by_id 找到之前的slot (简称为 Slot A);
-->第2步:使用create_srv_accept_rfc_slot 创建新的Slot (简称为 Slot B),此函数我们看到其把Slot A 作为入参传入;在函数内部可以看到Slot A参数赋值给了Slot B,但最难理解的是函数最后:Slot A和B的id值进行了交换(答案在后面);
Note:交换id后,当使用find_rfc_slot_by_id时,原来的Slot A id 会改为返回Slot B;
同样,Slot B的id会返回Slot A;
-->第3步:通过btsock_thread_add_fd 对Slot A 设置异常标志位,
使得sock_poll_thread中的 poll能检测到socket 关闭时触发的exception;
-->第4步:通过btsock_thread_add_fd 对Slot B 设置读取标志位,使得当Slot B socket有数据写入时,poll 函数能检测到;
-->第5步:使用函数send_app_connect_signal对Slot A的app_fd发出20Byte 数据(表示当前RFCOMM channel 连接上),但这里需留意的是最后一个参数(Slot B 的app_fd):我们对比发现当Socket 作为客户端主动连接时(on_cli_rfc_connect),最后一个参数是-1;
延伸问题点:为何要把Slot B 的app_fd值发送给Socket Server;
以上截图可以看到,其使用“cmsghdr”存储Slot B 的app_fd值,连同连接上的20Byte 数据一起,使用API sendmsg 发送给了Slot A 的add_fd;
在BluetoothSocket.java中,监听20Byte 连接数据的方法是waitSocketSignal
-->waitSocketSignal,中调用readall;
-->readall中可以看到read 方法,但这里如之前问题描述过,不能直接理解为原来的InputStream 中的read方法,
原因是其已经在“mSocketIS = mSocket.getInputStream();”中被重写,最终执行JNI API
readba_native (位置在android_net_LocalSocketImpl.cpp (frameworks\base\core\jni))
JNI层:socket_readba -->socket_read_all-->通过recvmsg 进行message接收;
-->当接收到20Byte数据后,执行“socket_process_cmsg”
通过Framework层BluetoothSocket.java中的方法acceptSocket,或许我们可以理解
为何在监听连接上后二次执行“alloc_rfc_slot”
以下截图,accept 方法内,当通过waitSocketSignal 阻塞获取到20Byte数据表明已经
连接上,此时会看到其继续执行方法acceptSocket,并且返回了一个新的socket 实例;
基于此我们可以理解为何二次“alloc_rfc_slot”并且在create_srv_accept_rfc_slot 内交换了
Id值;
原因(推断):Framework中的监听并非是“一次监听一次有效”,即“监听-->接受-->连接-->读/
写-->断开-->重新执行监听API…”,而应该是“一次监听,后续不需要再次监听就可以继续等
待被连接(在官方描述中,二次“alloc_rfc_slot”用意并非是为
了“一次监听多次使用”,其建议“一次监听一次使用,虽然监听可以被多次使用”,当侦查到被连接后,官方建议关闭侦听Socket)”;所以在执行btsock_rfc_listen触发的第一次
“alloc_rfc_slot”只是同于通知连接消息,当被连接上时,会在create_srv_accept_rfc_slot内
进行第二次“alloc_rfc_slot”,此时得到Slot B 的app_fd才是当前RFCOMM Linked的操作
fd, 上层Framework在方法acceptSocket返回新的socket 操作实例,用于当前RFCOMM
Linked的读写;原来的Slot A 的app_fd继续同于监听被连接消息;
延伸问题点:为何在原生BT Framework中,在socketpair 返回的fd[1] (即app_fd)时,其不直接返回fd 值,而是返回类ParcelFileDescriptor,且上层实际操作时的fd也已经不是fd[1] (即app_fd)
---在原生BT的建立监听实现的“bindListen”, 其对应执行的是JNI 函数“createSocketChannel”
-->在JNI 函数createSocketChannel中,在此函数中得到了btsock_listen返回的app_fd值,但并不是直接返回fd值给上层,而是通过parcel 形式写入到了ParcelFileDescriptor 类中;
但此时并没有直接返回app_fd值,而是使用了智能指针unique_ptr 进行处理;