Android蓝牙源码分析——Gatt连接(一)

来源:互联网 发布:最优化方法孙文瑜辅导 编辑:程序博客网 时间:2024/05/29 16:25

本文将重点描述Android蓝牙GATT连接的大致流程,不会过多地纠缠代码细节,只为了从架构上梳理清楚,为接下来深入研究底层机制奠定一个宏观认识。

首先建立GATT连接前,我们通常要扫描蓝牙设备,获得设备的BluetoothDevice对象,然后调用connectGatt去建立GATT连接并等待连接状态回调,接下来我们就开始分析这一过程,首先看看connectGatt的实现:

public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport) {    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();    IBluetoothManager managerService = adapter.getBluetoothManager();    try {        IBluetoothGatt iGatt = managerService.getBluetoothGatt();        if (iGatt == null) {            // BLE is not supported            return null;        }        BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport);        gatt.connect(autoConnect, callback);        return gatt;    } catch (RemoteException e) {Log.e(TAG, "", e);}    return null;}

这里主要是获取IBluetoothGatt,Gatt相关操作是单独抽出来的,没有都塞到IBluetoothManager中,否则会让IBluetoothManager显得很臃肿,作为蓝牙总管IBluetoothManager还是简洁一些为好。这个IBluetoothGatt的真正实现在GattService中,不过在进入GattService之前,我们先看看这个BluetoothGatt的connect函数,这里为了突出重点略去了一些代码。

boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {    if (!registerApp(callback)) {        return false;    }    return true;}

这里只调用了registerApp,从字面意思上理解貌似与连接无关,只是注册一个调用方,我们看看其实现:

private boolean registerApp(BluetoothGattCallback callback) {    mCallback = callback;    UUID uuid = UUID.randomUUID();    try {        mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);    } catch (RemoteException e) {        return false;    }    return true;}

这里给用户传进来的callback保存起来,生成了一个UUID作为调用方的标识,然后调用IBluetoothGatt的registerClient去注册,奇怪的是这里传入的是另外一个BluetoothGattCallback,这是个典型的静态代理,想必回调后还要做一些额外处理才会走到我们自己的callback。

private final IBluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallbackWrapper() {    public void onClientRegistered(int status, int clientIf) {        mClientIf = clientIf;        if (status != GATT_SUCCESS) {            mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE, BluetoothProfile.STATE_DISCONNECTED);            return;        }        try {            mService.clientConnect(mClientIf, mDevice.getAddress(), !mAutoConnect, mTransport);        } catch (RemoteException e) {            Log.e(TAG,"",e);        }    }    ......}

这个Callback是一个BluetoothGattCallbackWrapper对象,相当于在我们自己的callback基础上增加了一些别的接口,这些接口只是用于系统内部调用。这里的onClientRegistered就是新增的接口之一,也是我们上面调用registerClient之后的回调。这个回调会返回一个clientIf和status,如果status不是成功则直接返回失败,否则继续调用IBluetoothGatt的clientConnect去真正建立连接。

好了,到此为止我们清楚了Gatt连接是分为两步的,首先要获取一个ClientIf,然后再去连接。这两个操作的实现都是在GattService中,我们先看registerClient,如下:

void registerClient(UUID uuid, IBluetoothGattCallback callback) {    mClientMap.add(uuid, callback, this);    gattClientRegisterAppNative(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());}

这里给uuid和callback建立映射,等到需要回调的时候通过uuid就可以找到callback了。再来看看gattClientRegisterAppNative的实现,是在com_android_bluetooth_gatt.cpp中,如下:

static void gattClientRegisterAppNative(JNIEnv* env, jobject object, jlong app_uuid_lsb, jlong app_uuid_msb ){    bt_uuid_t uuid;    if (!sGattIf) return;    set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);    sGattIf->client->register_client(&uuid);}

这里sGattIf是在GattService启动的时候初始化的,对应的是一组Gatt操作的接口,包括初始化、清理、gatt client和server相关的接口。这里gatt连接作为client端调到了register_client,传入的是调用方的uuid。其实现是btif_gattc_register_app函数,如下:

static bt_status_t btif_gattc_register_app(bt_uuid_t *uuid) {    btif_gattc_cb_t btif_cb;    memcpy(&btif_cb.uuid, uuid, sizeof(bt_uuid_t));    return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_REGISTER_APP, (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);}

这里调到了btif_transfer_context,从字面上理解是改变上下文,其实际意义是切换运行线程到btif task。这里有两个问题,为什么要切换线程?如何切换线程?

首先看第一个问题,为什么要切换线程,因为调用方是跨进程调到GattService中的,所以会运行在GattService的Binder线程池中,这样就要考虑多线程同步的问题了,因为Gatt Native层实现中有大量的全局变量,多线程环境下肯定会出问题。这里有两种做法,要么到处上锁,要么切换运行线程。类似的在Java中,我们要么加synchronized,要么干脆给所有操作都post到统一的工作线程中。而这两种方案中显然后者更省心,都切到统一的线程内就不用操心多线程的事了。

再来看第二个问题,在Android Java中,我们要切换运行线程只要将逻辑封装到Runnable中然后Post到目标线程的消息队列即可。而这里是Native层该怎么做呢?其实核心思想大致相同,线程中有一个消息队列,我们将消息和操作封装成一个msg,丢到该消息队列中,再将线程唤醒去取消息执行即可。虽然说起来简单,做起来可比Java复杂得多,只是Java中很多底层细节都封装得很好了,我们上层不用再考虑而已。

在蓝牙模块初始化的时候,native层会启动两个线程,一个是btif task,另一个是btu task。上层下来的所有调用都要先丢到btif task中,然后再看情况继续丢到btu task中处理。

我们回到btif_gattc_register_app这个函数,这里虽然切换了上下文,但是要做的事还是不变的,只是改头换面了一下,变成了一个BTIF_GATTC_REGISTER_APP消息和btgattc_handle_event函数。这个函数从字面意思上理解是处理各类事件的,想必里面就是switch case了,我们只关心BTIF_GATTC_REGISTER_APP事件,其处理函数为BTA_GATTC_AppRegister,如下:

void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb) {    ......    if ((p_buf = (tBTA_GATTC_API_REG *) GKI_getbuf(sizeof(tBTA_GATTC_API_REG))) != NULL)    {        p_buf->hdr.event    = BTA_GATTC_API_REG_EVT;        if (p_app_uuid != NULL) {            memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID));        }        p_buf->p_cback      = p_client_cb;        bta_sys_sendmsg(p_buf);    }    return;}

到这里真让人无语了,简单的一个注册就像踢皮球一样被丢来丢去,又被封装成消息发射出去了,这回是被丢到了另一个线程中,就是传说中的btu task。怎么丢的我们暂时不管,还是先搞清楚怎么注册才最要紧,经过了千辛万苦终于到了真正的注册环节,就是bta_gattc_register函数了,如下:

void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data) {    tBTA_GATTC               cb_data;    memset(&cb_data, 0, sizeof(cb_data));    cb_data.reg_oper.status = BTA_GATT_NO_RESOURCES;    for (i = 0; i < BTA_GATTC_CL_MAX; i ++) {        if (!p_cb->cl_rcb[i].in_use) {            if ((p_cb->cl_rcb[i].client_if = GATT_Register(p_app_uuid, &bta_gattc_cl_cback)) == 0) {                status = BTA_GATT_ERROR;            } else {                p_cb->cl_rcb[i].in_use = TRUE;                p_cb->cl_rcb[i].p_cback = p_data->api_reg.p_cback;                memcpy(&p_cb->cl_rcb[i].app_uuid, p_app_uuid, sizeof(tBT_UUID));                /* BTA use the same client interface as BTE GATT statck */                cb_data.reg_oper.client_if = p_cb->cl_rcb[i].client_if;                if ((p_buf = (tBTA_GATTC_INT_START_IF *) GKI_getbuf(sizeof(tBTA_GATTC_INT_START_IF))) != NULL) {                    p_buf->hdr.event    = BTA_GATTC_INT_START_IF_EVT;                    p_buf->client_if    = p_cb->cl_rcb[i].client_if;                    bta_sys_sendmsg(p_buf);                    status = BTA_GATT_OK;                } else {                    GATT_Deregister(p_cb->cl_rcb[i].client_if);                    status = BTA_GATT_NO_RESOURCES;                    memset( &p_cb->cl_rcb[i], 0 , sizeof(tBTA_GATTC_RCB));                }                break;            }        }    }    if (p_data->api_reg.p_cback) {        if (p_app_uuid != NULL) {            memcpy(&(cb_data.reg_oper.app_uuid), p_app_uuid,sizeof(tBT_UUID));        }        cb_data.reg_oper.status = status;        (*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT,  (tBTA_GATTC *)&cb_data);    }}

这个函数稍微有点长,不过逻辑很简单,就是在一个for循环中遍历看是否有可用的clientif,如果没有就返回BTA_GATT_NO_RESOURCES,值为128。遍历的时候发现某个槽没有人用就会去调用GATT_Register注册,注册成功就会返回一个clientIf,然后就要开始往java层回调了。在往回走之前,我们先看看GATT_Register的实现:

tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info){    tGATT_REG    *p_reg;    UINT8        i_gatt_if=0;    tGATT_IF     gatt_if=0;    for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++)    {        if (!p_reg->in_use)        {            memset(p_reg, 0 , sizeof(tGATT_REG));            i_gatt_if++;              /* one based number */            p_reg->app_uuid128 =  *p_app_uuid128;            gatt_if            =            p_reg->gatt_if     = (tGATT_IF)i_gatt_if;            p_reg->app_cb      = *p_cb_info;            p_reg->in_use      = TRUE;            break;        }    }    return gatt_if;}

这里逻辑很简单,就是看哪个槽没有被人占用,不过注意的是这个槽和上面的槽是不同的,上面的槽是bta_gattc_cb中的cl_rcb,这的槽是gatt_cb的cl_rcb。不过两个槽大小都一样,都是32。这样我们了解到clientIf是有数量限制的,而且是系统全局的,而不是单个APP进程内的限制。每次用完之后要及时释放,否则别的人就没法再用了。

好了接下来我们踏上归途了,看看拿到这个clientIf之后是怎么回调回上层的。回到bta_gattc_register函数中,回调是从这一句开始的

(*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT,  (tBTA_GATTC *)&cb_data);

这个p_cback是什么呢,注册的时候被封装成消息转手了无数次,但还是给揪出来了,这个指针指向的是bta_gattc_cback,如下:

static void bta_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) {    bt_status_t status = btif_transfer_context(btif_gattc_upstreams_evt,                    (uint16_t) event, (void*) p_data, sizeof(tBTA_GATTC), btapp_gattc_req_data);}

这里可以理解,毕竟来的路上是怎么切过来的,回去的时候就得怎么切回去。之前是先切到btif task,再切到btu task,现在要从btu task切回到btif task了,回调是btif_gattc_upstreams_evt,事件是BTA_GATTC_REG_EVT,如下:

static void btif_gattc_upstreams_evt(uint16_t event, char* p_param){    tBTA_GATTC *p_data = (tBTA_GATTC*) p_param;    switch (event)    {        case BTA_GATTC_REG_EVT:        {            bt_uuid_t app_uuid;            bta_to_btif_uuid(&app_uuid, &p_data->reg_oper.app_uuid);            bt_gatt_callbacks->client->register_client_cb(p_data->reg_oper.status, p_data->reg_oper.client_if, &app_uuid);            break;        }        ......    }}

这里的register_client_cb最终指向的是btgattc_register_app_cb函数,这已经回到了gatt service的native中了,如下:

void btgattc_register_app_cb(int status, int clientIf, bt_uuid_t *app_uuid){    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,        clientIf, UUID_PARAMS(app_uuid));}

这里调用到了Java层的onClientRegistered函数,返回的正是clientIf和uuid。至此,整个clientIf的注册流程终于走通了,虽然中间有很多代码细节我们没有深究,不过那都不重要了,有了对总体的把握,以后遇到具体问题再细看也不迟。而且代码细节很可能在以后Android升级时有重大改动,但总体思想和大致流程基本不会变的。

总结一下调用流程,App发起的Gatt连接请求被丢到GattService中,分解为两步走,第一步是注册ClientIf,注册成功后再拿着ClientIf建立真正的连接。先看ClientIf的注册,会往下走到GattService的native层中,再往下走到BlueDroid层,注册ClientIf完之后,会回到GattService的native层,再回调到GattService java层,这时如果ClientIf是注册成功的,则继续走Gatt连接流程,否则直接回调失败给用户。

下文我们将继续分析蓝牙真正的Gatt连接流程。

1 0