connectGatt(2/4):btif_gattc_register_app

  • 依次执行两个函数:mService.registerClient()和mService.clientConnect()。对mService.registerClient(),主要两个操作。1)向GATT协议栈注册,并获得新的client_if。此过程只是在gatt_cb.cl_rcb得到一个可用位置,不会触发蓝牙数据传输。2)调用JNI注册的回调函数:btgattc_register_app_cb,进而回调BluetoothGatt.mBluetoothGattCallback.onClientRegistered。onClientRegistered不会回调到app,而是“向下”执行connectGatt的第二个函数:mService.clientConnect()。

不要忘记我们的目标:连接BLE设备。前面我们分析到了,发起 BLE 设备连接,首先是向底层协议栈注册客户端,也就是调用了“sGattIf->client->register_client(&uuid)”,现在知道了register_client()的实现是btif_gattc_register_app。

<aosp>/system/bt/btif/src/btif_gatt_client.cc
------
static bt_status_t btif_gattc_register_app(const Uuid& uuid,
                                           bool eatt_support) {
  CHECK_BTGATT_INIT();

  return do_in_jni_thread(Bind(
      [](const Uuid& uuid, bool eatt_support) {
        BTA_GATTC_AppRegister(
            bta_gattc_cback,
            base::Bind(
                [](const Uuid& uuid, uint8_t client_id, uint8_t status) {
                  do_in_jni_thread(Bind(
                      [](const Uuid& uuid, uint8_t client_id, uint8_t status) {
                        HAL_CBACK(bt_gatt_callbacks, client->register_client_cb,
                                  status, client_id, uuid);
                      },
                      uuid, client_id, status));
                },
                uuid),
            eatt_support);
      },
      uuid, eatt_support));
}

do_in_jni_thread使用了MessageLoopThread机制,这类似chormium线程模型,实现了可向线程a的message_loop_投递一个函数f,然后在将来某个时刻,线程a就会以f()方法调用函数f。do_in_jni_thread是将f发向了bt_jin_thread线程,除它外,do_in_main_thread也在使用MessageLoopThread机制,只是改发向bt_main_thread线程。

将函数f发向bt_jni_thread,意味着这些操作将在bt_jni_thread被依次序列化执行,这大大减少多线程并发,这自然就提高了bt协议栈稳定性、以及简化编程模型。

虽然btif_gattc_register_app内只有一条语句,但从外到内实现了三个函数。

  1. Bind([](const Uuid& uuid, bool eatt_support){...}, uuid, eatt_support)。
    最外层函数,最先执行。bt_jni_thread调用时是以不带参数的f(),可这函数需要两个参数,第二个参数uuid给uuid传值,第三个eatt_support则传给eatt_support。
  2. base::Bind([](const Uuid& uuid, uint8_t client_id, uint8_t status){...}, uuid)。
    它的结果将传给BTA_GATTC_AppRegister的第二个参数cb(类型BtaAppRegisterCallback)。外面调用时会f(client_id, status)进行调用,base::Bind的第二个参数uuid给lambda函数的第一个参数传值。
  3. Bind([](const Uuid& uuid, uint8_t client_id, uint8_t status){...}, uuid, client_id, status)。
    上面的base::Bind实现的数就一个操作,在bt_jni_thread线程执行这个lambad函数。简单来说,就是让在bt_jni_thread执行HAL_CBACK。

总之,btif_gattc_register_app就一个逻辑,在bt_jni_thread执行BTA_GATTC_AppRegiste。

<aosp>/system/bt/bta/gatt/bta_gattc_api.cc
------
void BTA_GATTC_AppRegister(tBTA_GATTC_CBACK* p_client_cb,
                           BtaAppRegisterCallback cb, bool eatt_support) {
  if (!bta_sys_is_register(BTA_ID_GATTC))
    bta_sys_register(BTA_ID_GATTC, &bta_gattc_reg);

  do_in_main_thread(
      FROM_HERE, base::Bind(&bta_gattc_register, Uuid::GetRandom(), p_client_cb,
                            std::move(cb), eatt_support));
}

BTA_GATTC_AppRegister依次执行两个操作。

  1. 如果没有注册过BTA_ID_GATTC事件类处理函数,调用bta_sys_register执行注册。
  2. 生成一个随机uuid,在bt_main_thread执行bta_gattc_register。

第一个操作bta_sys_register,后面一并说。看第二个操作,bta_gattc_register,这函数真正执行着注册客户端。

<aosp>/system/bt/bta/gatt/bta_gattc_act.cc
------
/** Register a GATT client application with BTA */
void bta_gattc_register(const Uuid& app_uuid, tBTA_GATTC_CBACK* p_cback,
                        BtaAppRegisterCallback cb, bool eatt_suppport) {
  tGATT_STATUS status = GATT_NO_RESOURCES;
  uint8_t client_if = 0;

  // 检查 GATTC 模块是否开启;如果没有,就开启
  if (bta_gattc_cb.state == BTA_GATTC_STATE_DISABLED) {
    bta_gattc_enable();
  }

  // 这里遍历cl_rcb数组,cl_rcb存储着app的注册信息
  for (uint8_t i = 0; i < BTA_GATTC_CL_MAX; i++) {
    if (!bta_gattc_cb.cl_rcb[i].in_use) {
      // 找到一个未使用(in_use==false)插槽,该插槽将被用于此次访问。
      // 注意,client_if和插槽索引i无关。
      if ((bta_gattc_cb.cl_rcb[i].client_if = GATT_Register(
               app_uuid, "GattClient", &bta_gattc_cl_cback, eatt_suppport)) ==
          0) {
        LOG(ERROR) << "Register with GATT stack failed.";
        status = GATT_ERROR;
      } else {
        bta_gattc_cb.cl_rcb[i].in_use = true;
        // p_cback是传给BTA_GATTC_AppRegister的第一个参数,即bta_gattc_cback
        bta_gattc_cb.cl_rcb[i].p_cback = p_cback;
        bta_gattc_cb.cl_rcb[i].app_uuid = app_uuid;

        /* BTA use the same client interface as BTE GATT statck */
        client_if = bta_gattc_cb.cl_rcb[i].client_if;

        // 注册成功,发送 BTA_GATTC_INT_START_IF_EVT 事件
        do_in_main_thread(FROM_HERE,
                          base::Bind(&bta_gattc_start_if, client_if));

        status = GATT_SUCCESS;
        break;
      }
    }
  }
  // cb是传给BTA_GATTC_AppRegister的第二个参数,
  // 即btif_gattc_register_app内3个lambad的第二个
  // 通过发送 BTA_GATTC_REG_EVT 事件,把注册结果回调给上层
  if (!cb.is_null()) cb.Run(client_if, status);
}

bta_gattc_register依次执行四个操作。

  1. 遍历cl_rcb列表,找到一个未使用(in_use==false)插槽,然后调用GATT_Register算出client_if,告知我这个app使用这个插槽了。注意,client_if值是占用单元在一个数组内从1开始的索引,但这个数组不是插槽所在的cl_rcb,而是全局数组gatt_cb.cl_rcb[GATT_MAX_APPS]。因为GATT_MAX_APPS值是32,所以client_if范围是[1, 32]。
  2. 给bta_gattc_cb.cl_rcb[i]各字段赋值。client_if、in_use、p_cback、app_uuid。额外说下p_cback,它的值来自传给BTA_GATTC_AppRegister的第一个参数,即bta_gattc_cback。后面多个地方会发现,bt协议栈向回调bt-JNI中函数靠的就是它。
  3. 在bt_main_thread执行bta_gattc_start_if。
  4. cb.Run(client_if, status)。把此次注册结果返回给bt-JNI,返回说来就是回调bt-JNI注册的回调函数。

一、调用GATT_Register找到一个可用插槽,调用GATT_Register计算client_if

typedef struct {
  tGATT_TCB tcb[GATT_MAX_PHY_CHANNEL];
  ...
  tGATT_REG cl_rcb[GATT_MAX_APPS];
  tGATT_CLCB clcb[GATT_CL_MAX_LCB]; /* connection link control block*/
  ...
} tGATT_CB;

tGATT_CB gatt_cb;

<apsp>/system/bt/stack/gatt/gatt_api.cc
------
tGATT_IF GATT_Register(const Uuid& app_uuid128, std::string name,
                       tGATT_CBACK* p_cb_info, bool eatt_support) {
  tGATT_REG* p_reg;
  uint8_t 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 && p_reg->app_uuid128 == app_uuid128) {
      // 不允许重复注册同一个app_uuid128
      return 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) {
      *p_reg = {};
      i_gatt_if++; /* one based number */
      p_reg->app_uuid128 = 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;
      p_reg->eatt_support = eatt_support;
      p_reg->name = name;
      LOG_INFO("Allocated name:%s uuid:%s gatt_if:%hhu eatt_support:%u",
               name.c_str(), app_uuid128.ToString().c_str(), gatt_if,
               eatt_support);
      return gatt_if;
    }
  }

  return 0;
}

gatt_cb是个全局变量,注册到gatt_cb.cl_rcb的用户有若干种,bta_gattc_register只是当中一种,作为易记,可认为name="GattClient"这这类用户的标识。一个用户占用一个单元后,GATT_Register会向logcat输出类似“Allocated name: ...”,让看一次android设备从开机开始的日志。

前面4个是在android启动阶阶段时输出的
-----------------
GATT_Register: Allocated name:GattProfileDb uuid:81818181-8181-8181-8181-818181818181 gatt_if:1 eatt_support:0
GATT_Register: Allocated name:Gap uuid:82828282-8282-8282-8282-828282828282 gatt_if:2 eatt_support:0
GATT_Register: Allocated name:GattClient uuid:7adebf0e-b4b7-c41b-4027-bd5e234ca56c gatt_if:3 eatt_support:0
GATT_Register: Allocated name:GattClient uuid:c88155b2-89e4-952e-1ee8-7bc03ba9f870 gatt_if:4 eatt_support:0

进入桌面,运行一个ble cenger app。开始扫描
GATT_Register: Allocated name:GattClient uuid:b8a12676-459f-3a5e-d747-e98a888002b4 gatt_if:5 eatt_support:0
在该app结束扫描,连接一个peripheral
GATT_Register: Allocated name:GattClient uuid:ba40e20a-c435-285b-d455-37205d56e962 gatt_if:5 eatt_support:0
在该app断开连接,再次开始扫描
GATT_Register: Allocated name:GattClient uuid:719a97b9-7291-207a-0e02-0d9cdfe3aec2 gatt_if:6 eatt_support:0

从上面logcat可得出几个结论

  • client_if/gatt_if是从1开始赋值。
  • android启动阶段会占用数个单元,这些可认为是bt协议栈系统占用。
  • 由于有bt协议栈系统占用,第一个ble cenger app得到的client_if不是1。

 

二、给bta_gattc_cb.cl_rcb[i]各字段赋值

client_if、in_use、p_cback、app_uuid。额外说下p_cback,它的值来自传给BTA_GATTC_AppRegister的第一个参数,即bta_gattc_cback。后面多个地方会发现,bt协议栈向回调bt-JNI中函数靠的就是它。

 

三、在bt_main_thread执行bta_gattc_start_if

<aosp>/system/bt/bta/gatt/bta_gattc_act.cc
------
void bta_gattc_start_if(uint8_t client_if) {
  GATT_StartIf(client_if);
}

<aosp>/system/bt/stack/gatt/gatt_api.cc
------
void GATT_StartIf(tGATT_IF gatt_if) {
  tGATT_REG* p_reg;
  tGATT_TCB* p_tcb;
  RawAddress bda;
  uint8_t start_idx, found_idx;
  uint16_t conn_id;
  tBT_TRANSPORT transport;

  LOG_DEBUG("Starting GATT interface gatt_if_:%hu", gatt_if);

  p_reg = gatt_get_regcb(gatt_if);
  if (p_reg != NULL) {
    start_idx = 0;
    while (
        gatt_find_the_connected_bda(start_idx, bda, &found_idx, &transport)) {
      p_tcb = gatt_find_tcb_by_addr(bda, transport);
      if (p_reg->app_cb.p_conn_cb && p_tcb) {
        conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if);
        (*p_reg->app_cb.p_conn_cb)(gatt_if, bda, conn_id, true,
                                   GATT_CONN_UNKNOWN, transport);
      }
      start_idx = ++found_idx;
    }
  }
}

这个函数的主要作用是,调用刚注册的客户端 gatt_if 的连接回调函数,上报所有的设备的连接状态。

 

四、cb.Run(client_if, status),回调bt-JNI注册的回调函数

cb是传给BTA_GATTC_AppRegister的第二个参数,即btif_gattc_register_app内3个lambad的第二个。

do_in_jni_thread(Bind(
    [](const Uuid& uuid, uint8_t client_id, uint8_t status) {
        HAL_CBACK(bt_gatt_callbacks, client->register_client_cb, status, client_id, uuid);
    },
    uuid, client_id, status));

以上代码将在bt_jni_thread线程调用位在bt-JNI内btgattc_register_app_cb(status, client_id, uuid)。

<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
------
void btgattc_register_app_cb(int status, int clientIf, const Uuid& app_uuid) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onClientRegistered, status,
                               clientIf, UUID_PARAMS(app_uuid));
}

这里通过JNI 调用了函数id为method_onClientRegistered函数。

<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
------
static void classInitNative(JNIEnv* env, jclass clazz) {
  // Client callbacks

  method_onClientRegistered =
      env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
  ...
}

这里就对应了 Java class 中的 onClientRegistered(...) 方法,这里 clazz 是谁呢?

<aosp>/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp
------
static JNINativeMethod sMethods[] = {  
  {"classInitNative", "()V", (void *) classInitNative},
  ...
}

我们在 GattService.java 中看到:

<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java
------
public class GattService extends ProfileService {  
  static {
        classInitNative();
  }
}

可见,上面的 clazz 就是 GattService,onClientRegistered 就是 GattService 的成员方法。我们在 GattService 类中找到其实现如下:

<aosp>/packages/apps/Bluetooth/src/com/android/bluetooth/gatt/GattService.java
------
    void onClientRegistered(int status, int clientIf, long uuidLsb, long uuidMsb)
            throws RemoteException {
        UUID uuid = new UUID(uuidMsb, uuidLsb);
        if (DBG) {
            Log.d(TAG, "onClientRegistered() - UUID=" + uuid + ", clientIf=" + clientIf);
        }
        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);
        }
    }

终于回到Java层代码。还记得我们前面的在分析调用 registerClient(...) 的时候,把上层客户端与 uuid 对应起来,保存在 mClientMap 中。 这里通过 uuid 找到对应的客户端App,然后调用对应的回调函数 app.callback.onClientRegistered(status, clientIf);。 如果你还记得前面的分析的话,应该知道这个 callback 就是 BluetoothGatt 在调用 registerApp(...) 的时候传递的 mBluetoothGattCallback,所以这里就调用了 mBluetoothGattCallback.onClientRegistered(...) 方法。 这个 onClientRegistered() 回调我们在之前就提到过,如果注册客户端完成,就会回调这里。如果成功,就会发起真正的连接请求(见第 1 节)。到这里,其实就回调了 Android framework 层了。

如果mService.registerClient成功,会以参数status=GATT_SUCCESS回调BluetoothGatt的onClientRegistered,当中会发起连接请求:mService.clientConnect(mClientIf, mDevice.getAddress(), !mAutoConnect)。clientConnect会调用JNI函数gattClientConnectNative,后者进一步调用BLE HAL中的btif_gattc_open

全部评论: 0

    写评论: