Android readCharacteristic源码分析

一、前言

Android中的readCharacteristic是蓝牙低功耗(BLE)通信中的一个重要操作,它允许客户端(如智能手机、智能手表等)读取远程BLE设备(如心率传感器、血压计等)中特定特征(Characteristic)的数据。这个过程遵循通用属性规范(GATT)协议,该协议负责BLE通信过程中的数据传输管理。

1.1. 概念与用途

  • 概念readCharacteristic是BLE API中的一个方法,用于从已连接的BLE设备的特定服务(Service)中获取特征(Characteristic)的数据。
  • 用途:通过读取BLE设备上的特征,可以获取各种类型的数据,如健康数据(心率、步数等)、环境数据(温度、湿度等)或其他自定义数据。这些数据可用于实现数据交换、协议触发、状态监控等功能。

1.2. 使用场景

readCharacteristic方法属于 BluetoothGatt 类,是 Android BLE API 的一部分,想要从连接的 BLE 设备中获取某个特征(Characteristic)的当前值时会使用这个方法。例如,想要读取一个心率监测器的当前心率值,或者从温度传感器获取温度读数。

boolean readCharacteristic(BluetoothGattCharacteristic characteristic)
  • ​​​​​​characteristicBluetoothGattCharacteristic 类型的对象,代表想要读取的蓝牙特征。这个对象应该已经被发现并且属于一个已经连接的蓝牙设备。

​​​​1.3. 操作过程(以BLE通信为例)

  1. 建立连接:首先,客户端需要与BLE设备建立连接。这通常通过调用BluetoothGatt的connect()方法实现,并在BluetoothGattCallback的onConnectionStateChange()回调中处理连接状态变化。
  2. 发现服务:连接成功后,客户端需要调用BluetoothGatt的discoverServices()方法来发现BLE设备上的所有服务。这些服务包含了设备的一组相关功能,如心率监测、电池状态等。
  3. 查找特征:在onServicesDiscovered()回调中,客户端会遍历所有发现的服务,并使用每个服务的getCharacteristic(UUID characteristicUuid)方法来查找包含目标数据的特征。UUID是特征的唯一标识符。
  4. 检查属性:在尝试读取特征之前,客户端应检查特征的属性(通过调用BluetoothGattCharacteristic.getProperties()),以确保该特征支持读操作(如BluetoothGattCharacteristic.PROPERTY_READ)。
  5. 读取数据:如果特征支持读操作,客户端可以调用BluetoothGatt的readCharacteristic(BluetoothGattCharacteristic characteristic)方法来请求读取特征的数据。这是一个异步操作,结果将通过BluetoothGattCallback的onCharacteristicRead()回调返回。
  6. 处理结果:在onCharacteristicRead()回调中,客户端应检查返回的状态码。如果状态码为BluetoothGatt.GATT_SUCCESS,则表示读取操作成功,可以使用BluetoothGattCharacteristic.getValue()方法来获取特征的数据。数据以字节数组(byte[])的形式返回,可能需要根据特征的描述或设备的协议来解析。
  7. 错误处理: 如果读取操作失败,需要在onCharacteristicRead回调中处理错误情况。可能的错误原因包括设备不支持读取操作、设备已断开连接、或者发生了BLE协议栈错误等。

  8. 清理和后续操作:

  • 读取完成后,可能需要更新UI以显示数据,或者根据读取到的数据执行其他操作。

  • 如果有必要,记得在不再需要时关闭与BLE设备的连接,以释放资源。

1.4. 注意事项

  • 设备连接:在调用 readCharacteristic 方法之前,确保应用与 BLE 设备建立了连接。
  • 特征权限:确保有权限读取该特征。BLE 特征可能具有不同的属性(如只读、只写、通知等),需要检查特征的属性来确定是否可以读取它。
  • 异步操作:读取操作是异步的。意味着 readCharacteristic 方法会立即返回,而实际的读取操作会在后台进行。需要通过实现 BluetoothGattCallback 接口的 onCharacteristicRead 方法来接收读取结果。
  • 错误处理:如果读取操作失败(如,由于设备断开连接或特征不支持读取),onCharacteristicRead 方法中的 status 参数将包含错误代码。应该检查这个状态并相应地处理错误。

1.5. 示例

// 假设已经有了 BluetoothGatt 对象 gatt 和 BluetoothGattCharacteristic 对象 characteristic  
if (gatt.readCharacteristic(characteristic)) {  
    // 请求开始,但结果将在 onCharacteristicRead 中异步返回  
} else {  
    // 请求未能开始,可能是因为设备未连接或特征不支持读取  
}  
  
// 需要在BluetoothGattCallback中实现onCharacteristicRead方法来处理读取结果  
@Override  
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {  
    if (status == BluetoothGatt.GATT_SUCCESS) {  
        // 读取成功,处理 characteristic.getValue()  
    } else {  
        // 读取失败,处理错误  
    }  
}

1.6. 小结

readCharacteristic是Android BLE通信中用于读取远程设备数据的关键操作。通过遵循上述步骤和注意事项,我们可以高效地实现BLE设备之间的数据交换和状态监控功能。

二、源码分析 

2.1. BluetoothGatt.readCharacteristic

/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattService.java
public void readCharacteristic(int clientIf, String address, int handle, int authReq,
        AttributionSource attributionSource, SynchronousResultReceiver receiver) {
    try {
        readCharacteristic(clientIf, address, handle, authReq, attributionSource);
        receiver.send(null);
    } catch (RuntimeException e) {
        receiver.propagateException(e);
    }
}
private void readCharacteristic(int clientIf, String address, int handle, int authReq,
        AttributionSource attributionSource) {
    GattService service = getService();
    if (service == null) {
        return;
    }
    service.readCharacteristic(clientIf, address, handle, authReq, attributionSource);
}

void readCharacteristic(int clientIf, String address, int handle, int authReq,
        AttributionSource attributionSource) {
    if (!Utils.checkConnectPermissionForDataDelivery(
            this, attributionSource, "GattService readCharacteristic")) {
        return;
    }

    if (VDBG) {
        Log.d(TAG, "readCharacteristic() - address=" + address);
    }

    Integer connId = mClientMap.connIdByAddress(clientIf, address);
    if (connId == null) {
        Log.e(TAG, "readCharacteristic() - No connection for " + address + "...");
        return;
    }

    try {
        permissionCheck(connId, handle);
    } catch (SecurityException ex) {
        String callingPackage = attributionSource.getPackageName();
        // Only throws on apps with target SDK T+ as this old API did not throw prior to T
        if (checkCallerTargetSdk(this, callingPackage, Build.VERSION_CODES.TIRAMISU)) {
            throw ex;
        }
        Log.w(TAG, "readCharacteristic() - permission check failed!");
        return;
    }

    mNativeInterface.gattClientReadCharacteristic(connId, handle, authReq);
}

在Android系统中,readCharacteristic允许外部调用者请求读取某个蓝牙设备的特定GATT特征。涉及权限检查、连接管理、异常处理和本地方法调用等多个方面,以确保数据的安全传输和设备的正确交互。

执行读取操作:如果通过了所有检查,最终会调用mNativeInterface.gattClientReadCharacteristic方法,用于通过蓝牙协议栈发送读取GATT特征的请求。参数connId、handle和authReq分别代表连接ID、特征句柄和授权请求。

2.1.1. gattClientReadCharacteristic

/packages/modules/Bluetooth/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
/**
 * Read a characteristic by the given handle
 */
public void gattClientReadCharacteristic(int connId, int handle, int authReq) {
    gattClientReadCharacteristicNative(connId, handle, authReq);
}

2.1.2. gattClientReadCharacteristicNative 

static void gattClientReadCharacteristicNative(JNIEnv* /* env */,
                                               jobject /* object */,
                                               jint conn_id, jint handle,
                                               jint authReq) {
  if (!sGattIf) return;

  sGattIf->client->read_characteristic(conn_id, handle, authReq);
}

2.2. btif_gattc_read_char

/packages/modules/Bluetooth/system/btif/src/btif_gatt_client.cc
static bt_status_t btif_gattc_read_char(int conn_id, uint16_t handle,
                                        int auth_req) {
  CHECK_BTGATT_INIT();
  return do_in_jni_thread(Bind(&BTA_GATTC_ReadCharacteristic, conn_id, handle,
                               auth_req, read_char_cb, nullptr));
}

在 Bluedroid协议栈中,btif_gatt_client.cc是文件是负责处理蓝牙 GATT 客户端(即设备作为蓝牙客户端与其他蓝牙设备通信时)相关操作的源代码文件之一。btif_gattc_read_char 函数是 Android 蓝牙堆栈中用于从远程蓝牙设备读取特征值的接口。它通过 JNI 线程安全地调用底层的 BTA_GATTC_ReadCharacteristic 函数来执行实际的读取操作,并在操作完成时通过回调函数 read_char_cb 通知上层。这种设计的目的是允许蓝牙协议栈的开发者在 Java 和 C/C++ 之间灵活地切换,同时保持代码的清晰和可维护性。

  • do_in_jni_thread(...):这个宏的作用是将给定的操作(读取特征的操作)放入 JNI 线程中执行。JNI(Java Native Interface)是 Java 和本地代码(如 C/C++)之间的桥梁。在 Android 的蓝牙协议栈中,很多与 Java 层交互的操作都需要在 JNI 线程中执行,以确保线程安全和正确的交互。
  • Bind(&BTA_GATTC_ReadCharacteristic, ...):通过函数指针将 BTA_GATTC_ReadCharacteristic 函数与必要的参数(conn_idhandleauth_req)以及回调函数 read_char_cb(当读取操作完成时会被调用)和 nullptr绑定在一起。BTA_GATTC_ReadCharacteristic 是负责实际执行读取操作的函数。

2.2.1. BTA_GATTC_ReadCharacteristic

/packages/modules/Bluetooth/system/bta/gatt/bta_gattc_api.cc
void BTA_GATTC_ReadCharacteristic(uint16_t conn_id, uint16_t handle,
                                  tGATT_AUTH_REQ auth_req,
                                  GATT_READ_OP_CB callback, void* cb_data) {
  tBTA_GATTC_API_READ* p_buf =
      (tBTA_GATTC_API_READ*)osi_calloc(sizeof(tBTA_GATTC_API_READ));

  p_buf->hdr.event = BTA_GATTC_API_READ_EVT;
  p_buf->hdr.layer_specific = conn_id;
  p_buf->is_multi_read = fal
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值