Android蓝牙源码分析——Gatt的Connection ID
2016-09-04 17:21
513 查看
我们注意到GattService中的所有GATT操作都会带两个参数,一个是clientIf,另一个是address,拿到这两个参数后,都会调用
先看getById是怎么获取App的,这里传入的id是ClientIf:
这个mApps是一个链表,如下:
为什么这里要定义App呢,对于Android系统蓝牙服务来说,每个蓝牙请求都来自于不同的对象,所以这里定义这个类就是为了区分不同的对象,从字面意思上理解这个对象应该是App。而每个App都会被分配一个id,这个getById就是通过id来找到对应的App对象。什么时候给App分配id的呢,在注册完ClientIf的回调中,如下:
我们回忆设备的连接过程,首先是生成一个随机的UUID,然后去注册ClientIf,注册完后就会有这个回调了。我们看registerClient,如下:
这里必然是通过uuid和callback生成了一个App保存起来了,之所以要callback是因为这个callback是一个IBinder,通过关注其生死可以知道调用方是否还在,这样当调用方被杀掉时可以停止蓝牙扫描或断开连接。有了这个App之后在onClientRegistered时才能通过uuid取出app,然后给clientIf设置为app的id。
这里和我们设想的有点不一样,开始我以为是一个App对应一个id,而这里App的id是clientIf,而事实上一个App中可以有很多clientIf。不过想想也没关系,其实对系统来说不用管请求来自哪个App或是哪个进程,只要管连接就行了,而描述连接的句柄就是clientIf。如果连接都是同一个设备,但是来自于两个不同的App进程,那么ClientIf必然也是不一样的,就算同一个App里我们对同一个设备打开了两个Gatt,他们的ClientIf也是不一样的,不然对设备的读写就会串了。所以这个GattService里的App其实是从系统蓝牙服务来说的,就是一个Client。
回到connIdByAddress,获取App entry以后,遍历mConnections,这是描述已连接的设备,如下:
遍历mConnections,要同时满足appId和address都匹配上才返回。这样可以理解,可能某个设备分别在两个App中都连上了,那样在与设备通信的时候不能冲突了,所以必须要appId和address都匹配才行。
这个Connection是在设备连接上的时候注册的,并且设备连接上的回调中会传入connId,以后再与设备操作都不是用address或者clientIf,而是都用这个connId了。
到了这里connection id是怎么回事我们应该清楚了,但有一个最核心的问题还没搞清楚,那就是connection id是怎么生成的?我们只知道onConnected回调回来时就带上了connId,就是说这个connId是底层生成好的,然后传上来的。那我们要搞清楚底层是怎么生成这个connid的,是谁调的这个onConnected呢?
答案在com_android_bluetooth_gatt.cpp中的btgattc_open_cb函数,如下
这里的conn_id也是生成好的,我们在bluedroid中找,发现是在gatt_int.h中,如下:
这个tcb_idx和gatt_if是什么东西?我现在还不能给出答案,不过没关系,随着研究逐步展开,这些问题都会迎刃而解。
Integer connId = mClientMap.connIdByAddress(clientIf, address);获取对应的connId,然后之后的操作都是以这个connId为Key。我们看connIdByAddress的实现:
Integer connIdByAddress(int id, String address) { App entry = getById(id); if (entry == null) return null; Iterator<Connection> i = mConnections.iterator(); while (i.hasNext()) { Connection connection = i.next(); if (connection.address.equalsIgnoreCase(address) && connection.appId == id) return connection.connId; } return null; }
先看getById是怎么获取App的,这里传入的id是ClientIf:
App getById(int id) { Iterator<App> i = mApps.iterator(); while (i.hasNext()) { App entry = i.next(); if (entry.id == id) return entry; } Log.e(TAG, "Context not found for ID " + id); return null; }
这个mApps是一个链表,如下:
List<App> mApps = new ArrayList<App>();
为什么这里要定义App呢,对于Android系统蓝牙服务来说,每个蓝牙请求都来自于不同的对象,所以这里定义这个类就是为了区分不同的对象,从字面意思上理解这个对象应该是App。而每个App都会被分配一个id,这个getById就是通过id来找到对应的App对象。什么时候给App分配id的呢,在注册完ClientIf的回调中,如下:
void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb) throws RemoteException { UUID uuid = new UUID(uuidMsb, uuidLsb); ClientMap.App app = mClientMap.getByUuid(uuid); if (app != null) { if (status == 0) { app.id = clientIf; app.linkToDeath(new ClientDeathRecipient(clientIf)); } else { mClientMap.remove(uuid); } app.callback.onClientRegistered(status, clientIf); } }
我们回忆设备的连接过程,首先是生成一个随机的UUID,然后去注册ClientIf,注册完后就会有这个回调了。我们看registerClient,如下:
void registerClient(UUID uuid, IBluetoothGattCallback callback) { mClientMap.add(uuid, callback, this); gattClientRegisterAppNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); }
这里必然是通过uuid和callback生成了一个App保存起来了,之所以要callback是因为这个callback是一个IBinder,通过关注其生死可以知道调用方是否还在,这样当调用方被杀掉时可以停止蓝牙扫描或断开连接。有了这个App之后在onClientRegistered时才能通过uuid取出app,然后给clientIf设置为app的id。
这里和我们设想的有点不一样,开始我以为是一个App对应一个id,而这里App的id是clientIf,而事实上一个App中可以有很多clientIf。不过想想也没关系,其实对系统来说不用管请求来自哪个App或是哪个进程,只要管连接就行了,而描述连接的句柄就是clientIf。如果连接都是同一个设备,但是来自于两个不同的App进程,那么ClientIf必然也是不一样的,就算同一个App里我们对同一个设备打开了两个Gatt,他们的ClientIf也是不一样的,不然对设备的读写就会串了。所以这个GattService里的App其实是从系统蓝牙服务来说的,就是一个Client。
回到connIdByAddress,获取App entry以后,遍历mConnections,这是描述已连接的设备,如下:
/** Internal list of connected devices **/ Set<Connection> mConnections = new HashSet<Connection>();
遍历mConnections,要同时满足appId和address都匹配上才返回。这样可以理解,可能某个设备分别在两个App中都连上了,那样在与设备通信的时候不能冲突了,所以必须要appId和address都匹配才行。
这个Connection是在设备连接上的时候注册的,并且设备连接上的回调中会传入connId,以后再与设备操作都不是用address或者clientIf,而是都用这个connId了。
void onConnected(int clientIf, int connId, int status, String address) throws RemoteException { if (status == 0) mClientMap.addConnection(clientIf, connId, address); ClientMap.App app = mClientMap.getById(clientIf); if (app != null) { app.callback.onClientConnectionState(status, clientIf, (status==BluetoothGatt.GATT_SUCCESS), address); } }
到了这里connection id是怎么回事我们应该清楚了,但有一个最核心的问题还没搞清楚,那就是connection id是怎么生成的?我们只知道onConnected回调回来时就带上了connId,就是说这个connId是底层生成好的,然后传上来的。那我们要搞清楚底层是怎么生成这个connid的,是谁调的这个onConnected呢?
答案在com_android_bluetooth_gatt.cpp中的btgattc_open_cb函数,如下
void btgattc_open_cb(int conn_id, int status, int clientIf, bt_bdaddr_t* bda) { jstring address = bdaddr2newjstr(sCallbackEnv, bda); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnected, clientIf, conn_id, status, address); sCallbackEnv->DeleteLocalRef(address); }
这里的conn_id也是生成好的,我们在bluedroid中找,发现是在gatt_int.h中,如下:
#define GATT_CREATE_CONN_ID(tcb_idx, gatt_if) ((UINT16) ((((UINT8)(tcb_idx) ) << 8) | ((UINT8) (gatt_if))))
这个tcb_idx和gatt_if是什么东西?我现在还不能给出答案,不过没关系,随着研究逐步展开,这些问题都会迎刃而解。
相关文章推荐
- Android蓝牙源码分析——Gatt写设备
- Android蓝牙源码分析——Gatt连接(一)
- android 蓝牙源码分析
- [Android源码解析]蓝牙扫描结果反馈的分析
- Android 4.4以上使用HttpURLConnection底层使用OkHttp实现的源码分析
- [Android源码分析]蓝牙配对之jni之上的点点滴滴
- Android4.42-Setting源码分析之蓝牙模块Bluetooth(下)
- [Android源码分析]蓝牙打开分析--苦尽甘来之再次回到jni之上
- Android蓝牙源码分析——StateMachine状态机
- Android蓝牙源码分析——BTA层消息分发
- Android Gatt连接流程源码分析之ClientIf注册
- [Android源码分析]蓝牙打开流程分析——jni层之上的方方面面
- android蓝牙源码分析
- android 蓝牙源码分析
- Android GATT 连接过程源码分析
- Android蓝牙源码分析——GKI定时器
- 蓝牙文件传输之obex层之上的分析【Android源码解析】
- [Android源码分析]蓝牙搜索过程分析
- [Android源码分析]从蓝牙界面看设置中fragment的实现
- Android4.42-Settings源码分析之蓝牙模块Bluetooth(上)