Android蓝牙源码分析——Gatt的Connection ID

我们注意到GattService中的所有GATT操作都会带两个参数,一个是clientIf,另一个是address,拿到这两个参数后,都会调用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源码分析
    原文地址: https://blog.csdn.net/dingjikerbo/article/details/52433481
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞