| /****************************************************************************** |
| * |
| * Copyright (C) 2009-2014 Broadcom Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| ******************************************************************************/ |
| |
| /******************************************************************************* |
| * |
| * Filename: btif_gatt_client.c |
| * |
| * Description: GATT client implementation |
| * |
| ******************************************************************************/ |
| |
| #define LOG_TAG "bt_btif_gattc" |
| |
| #include <base/at_exit.h> |
| #include <base/bind.h> |
| #include <base/threading/thread.h> |
| #include <errno.h> |
| #include <hardware/bluetooth.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "device/include/controller.h" |
| |
| #include "btcore/include/bdaddr.h" |
| #include "btif_common.h" |
| #include "btif_util.h" |
| |
| #include <hardware/bt_gatt.h> |
| |
| #include "bta_api.h" |
| #include "bta_gatt_api.h" |
| #include "btif_config.h" |
| #include "btif_dm.h" |
| #include "btif_gatt.h" |
| #include "btif_gatt_util.h" |
| #include "btif_storage.h" |
| #include "osi/include/log.h" |
| #include "vendor_api.h" |
| |
| using base::Bind; |
| using base::Owned; |
| using std::vector; |
| |
| extern bt_status_t do_in_jni_thread(const base::Closure& task); |
| extern bt_status_t btif_gattc_test_command_impl(int command, |
| btgatt_test_params_t* params); |
| extern const btgatt_callbacks_t* bt_gatt_callbacks; |
| |
| /******************************************************************************* |
| * Constants & Macros |
| ******************************************************************************/ |
| |
| #define CLI_CBACK_IN_JNI(P_CBACK, ...) \ |
| do { \ |
| if (bt_gatt_callbacks && bt_gatt_callbacks->client->P_CBACK) { \ |
| BTIF_TRACE_API("HAL bt_gatt_callbacks->client->%s", #P_CBACK); \ |
| do_in_jni_thread(Bind(bt_gatt_callbacks->client->P_CBACK, __VA_ARGS__)); \ |
| } else { \ |
| ASSERTC(0, "Callback is NULL", 0); \ |
| } \ |
| } while (0) |
| |
| #define CHECK_BTGATT_INIT() \ |
| do { \ |
| if (bt_gatt_callbacks == NULL) { \ |
| LOG_WARN(LOG_TAG, "%s: BTGATT not initialized", __func__); \ |
| return BT_STATUS_NOT_READY; \ |
| } else { \ |
| LOG_VERBOSE(LOG_TAG, "%s", __func__); \ |
| } \ |
| } while (0) |
| |
| #define BLE_RESOLVE_ADDR_MSB \ |
| 0x40 /* bit7, bit6 is 01 to be resolvable random \ |
| */ |
| #define BLE_RESOLVE_ADDR_MASK 0xc0 /* bit 6, and bit7 */ |
| #define BTM_BLE_IS_RESOLVE_BDA(x) \ |
| (((x)[0] & BLE_RESOLVE_ADDR_MASK) == BLE_RESOLVE_ADDR_MSB) |
| |
| namespace { |
| |
| uint8_t rssi_request_client_if; |
| |
| void btif_gattc_upstreams_evt(uint16_t event, char* p_param) { |
| LOG_VERBOSE(LOG_TAG, "%s: Event %d", __func__, event); |
| |
| tBTA_GATTC* p_data = (tBTA_GATTC*)p_param; |
| switch (event) { |
| case BTA_GATTC_DEREG_EVT: |
| break; |
| |
| case BTA_GATTC_EXEC_EVT: { |
| HAL_CBACK(bt_gatt_callbacks, client->execute_write_cb, |
| p_data->exec_cmpl.conn_id, p_data->exec_cmpl.status); |
| break; |
| } |
| |
| case BTA_GATTC_SEARCH_CMPL_EVT: { |
| HAL_CBACK(bt_gatt_callbacks, client->search_complete_cb, |
| p_data->search_cmpl.conn_id, p_data->search_cmpl.status); |
| break; |
| } |
| |
| case BTA_GATTC_NOTIF_EVT: { |
| btgatt_notify_params_t data; |
| |
| bdcpy(data.bda.address, p_data->notify.bda); |
| memcpy(data.value, p_data->notify.value, p_data->notify.len); |
| |
| data.handle = p_data->notify.handle; |
| data.is_notify = p_data->notify.is_notify; |
| data.len = p_data->notify.len; |
| |
| HAL_CBACK(bt_gatt_callbacks, client->notify_cb, p_data->notify.conn_id, |
| &data); |
| |
| if (p_data->notify.is_notify == false) |
| BTA_GATTC_SendIndConfirm(p_data->notify.conn_id, p_data->notify.handle); |
| |
| break; |
| } |
| |
| case BTA_GATTC_OPEN_EVT: { |
| bt_bdaddr_t bda; |
| bdcpy(bda.address, p_data->open.remote_bda); |
| |
| HAL_CBACK(bt_gatt_callbacks, client->open_cb, p_data->open.conn_id, |
| p_data->open.status, p_data->open.client_if, &bda); |
| |
| if (GATT_DEF_BLE_MTU_SIZE != p_data->open.mtu && p_data->open.mtu) { |
| HAL_CBACK(bt_gatt_callbacks, client->configure_mtu_cb, |
| p_data->open.conn_id, p_data->open.status, p_data->open.mtu); |
| } |
| |
| if (p_data->open.status == BTA_GATT_OK) |
| btif_gatt_check_encrypted_link(p_data->open.remote_bda, |
| p_data->open.transport); |
| break; |
| } |
| |
| case BTA_GATTC_CLOSE_EVT: { |
| bt_bdaddr_t bda; |
| bdcpy(bda.address, p_data->close.remote_bda); |
| HAL_CBACK(bt_gatt_callbacks, client->close_cb, p_data->close.conn_id, |
| p_data->status, p_data->close.client_if, &bda); |
| break; |
| } |
| |
| case BTA_GATTC_ACL_EVT: |
| LOG_DEBUG(LOG_TAG, "BTA_GATTC_ACL_EVT: status = %d", p_data->status); |
| /* Ignore for now */ |
| break; |
| |
| case BTA_GATTC_CANCEL_OPEN_EVT: |
| break; |
| |
| case BTA_GATTC_CFG_MTU_EVT: { |
| HAL_CBACK(bt_gatt_callbacks, client->configure_mtu_cb, |
| p_data->cfg_mtu.conn_id, p_data->cfg_mtu.status, |
| p_data->cfg_mtu.mtu); |
| break; |
| } |
| |
| case BTA_GATTC_CONGEST_EVT: |
| HAL_CBACK(bt_gatt_callbacks, client->congestion_cb, |
| p_data->congest.conn_id, p_data->congest.congested); |
| break; |
| |
| default: |
| LOG_ERROR(LOG_TAG, "%s: Unhandled event (%d)!", __func__, event); |
| break; |
| } |
| } |
| |
| 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, |
| (char*)p_data, sizeof(tBTA_GATTC), NULL); |
| ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status); |
| } |
| |
| void btm_read_rssi_cb(tBTM_RSSI_RESULTS* p_result) { |
| if (!p_result) return; |
| |
| bt_bdaddr_t* addr = new bt_bdaddr_t; |
| bdcpy(addr->address, p_result->rem_bda); |
| CLI_CBACK_IN_JNI(read_remote_rssi_cb, rssi_request_client_if, |
| base::Owned(addr), p_result->rssi, p_result->status); |
| } |
| |
| /******************************************************************************* |
| * Client API Functions |
| ******************************************************************************/ |
| |
| bt_status_t btif_gattc_register_app(bt_uuid_t* uuid) { |
| CHECK_BTGATT_INIT(); |
| |
| tBT_UUID bt_uuid; |
| btif_to_bta_uuid(&bt_uuid, uuid); |
| |
| return do_in_jni_thread(Bind( |
| [](tBT_UUID bt_uuid) { |
| BTA_GATTC_AppRegister( |
| bta_gattc_cback, |
| base::Bind( |
| [](tBT_UUID bt_uuid, uint8_t client_id, uint8_t status) { |
| do_in_jni_thread(Bind( |
| [](tBT_UUID bt_uuid, uint8_t client_id, uint8_t status) { |
| bt_uuid_t app_uuid; |
| bta_to_btif_uuid(&app_uuid, &bt_uuid); |
| HAL_CBACK(bt_gatt_callbacks, client->register_client_cb, |
| status, client_id, &app_uuid); |
| }, |
| bt_uuid, client_id, status)); |
| }, |
| bt_uuid)); |
| }, |
| bt_uuid)); |
| } |
| |
| void btif_gattc_unregister_app_impl(int client_if) { |
| BTA_GATTC_AppDeregister(client_if); |
| } |
| |
| bt_status_t btif_gattc_unregister_app(int client_if) { |
| CHECK_BTGATT_INIT(); |
| return do_in_jni_thread(Bind(&btif_gattc_unregister_app_impl, client_if)); |
| } |
| |
| void btif_gattc_open_impl(int client_if, BD_ADDR address, bool is_direct, |
| int transport_p) { |
| // Ensure device is in inquiry database |
| int addr_type = 0; |
| int device_type = 0; |
| tBTA_GATT_TRANSPORT transport = (tBTA_GATT_TRANSPORT)BTA_GATT_TRANSPORT_LE; |
| |
| if (btif_get_address_type(address, &addr_type) && |
| btif_get_device_type(address, &device_type) && |
| device_type != BT_DEVICE_TYPE_BREDR) { |
| BTA_DmAddBleDevice(address, addr_type, device_type); |
| } |
| |
| // Check for background connections |
| if (!is_direct) { |
| // Check for privacy 1.0 and 1.1 controller and do not start background |
| // connection if RPA offloading is not supported, since it will not |
| // connect after change of random address |
| if (!controller_get_interface()->supports_ble_privacy() && |
| (addr_type == BLE_ADDR_RANDOM) && BTM_BLE_IS_RESOLVE_BDA(address)) { |
| tBTM_BLE_VSC_CB vnd_capabilities; |
| BTM_BleGetVendorCapabilities(&vnd_capabilities); |
| if (!vnd_capabilities.rpa_offloading) { |
| HAL_CBACK(bt_gatt_callbacks, client->open_cb, 0, BT_STATUS_UNSUPPORTED, |
| client_if, (bt_bdaddr_t*)&address); |
| return; |
| } |
| } |
| BTA_DmBleStartAutoConn(); |
| } |
| |
| // Determine transport |
| if (transport_p != GATT_TRANSPORT_AUTO) { |
| transport = transport_p; |
| } else { |
| switch (device_type) { |
| case BT_DEVICE_TYPE_BREDR: |
| transport = BTA_GATT_TRANSPORT_BR_EDR; |
| break; |
| |
| case BT_DEVICE_TYPE_BLE: |
| transport = BTA_GATT_TRANSPORT_LE; |
| break; |
| |
| case BT_DEVICE_TYPE_DUMO: |
| if (transport == GATT_TRANSPORT_LE) |
| transport = BTA_GATT_TRANSPORT_LE; |
| else |
| transport = BTA_GATT_TRANSPORT_BR_EDR; |
| break; |
| } |
| } |
| |
| // Connect! |
| BTIF_TRACE_DEBUG("%s Transport=%d, device type=%d", __func__, transport, |
| device_type); |
| BTA_GATTC_Open(client_if, address, is_direct, transport); |
| } |
| |
| bt_status_t btif_gattc_open(int client_if, const bt_bdaddr_t* bd_addr, |
| bool is_direct, int transport) { |
| CHECK_BTGATT_INIT(); |
| // Closure will own this value and free it. |
| uint8_t* address = new BD_ADDR; |
| bdcpy(address, bd_addr->address); |
| return do_in_jni_thread(Bind(&btif_gattc_open_impl, client_if, |
| base::Owned(address), is_direct, transport)); |
| } |
| |
| void btif_gattc_close_impl(int client_if, BD_ADDR address, int conn_id) { |
| // Disconnect established connections |
| if (conn_id != 0) |
| BTA_GATTC_Close(conn_id); |
| else |
| BTA_GATTC_CancelOpen(client_if, address, true); |
| |
| // Cancel pending background connections (remove from whitelist) |
| BTA_GATTC_CancelOpen(client_if, address, false); |
| } |
| |
| bt_status_t btif_gattc_close(int client_if, const bt_bdaddr_t* bd_addr, |
| int conn_id) { |
| CHECK_BTGATT_INIT(); |
| // Closure will own this value and free it. |
| uint8_t* address = new BD_ADDR; |
| bdcpy(address, bd_addr->address); |
| return do_in_jni_thread( |
| Bind(&btif_gattc_close_impl, client_if, base::Owned(address), conn_id)); |
| } |
| |
| bt_status_t btif_gattc_refresh(int client_if, const bt_bdaddr_t* bd_addr) { |
| CHECK_BTGATT_INIT(); |
| // Closure will own this value and free it. |
| uint8_t* address = new BD_ADDR; |
| bdcpy(address, bd_addr->address); |
| return do_in_jni_thread(Bind(&BTA_GATTC_Refresh, base::Owned(address))); |
| } |
| |
| bt_status_t btif_gattc_search_service(int conn_id, bt_uuid_t* filter_uuid) { |
| CHECK_BTGATT_INIT(); |
| |
| if (filter_uuid) { |
| tBT_UUID* uuid = new tBT_UUID; |
| btif_to_bta_uuid(uuid, filter_uuid); |
| return do_in_jni_thread( |
| Bind(&BTA_GATTC_ServiceSearchRequest, conn_id, base::Owned(uuid))); |
| } else { |
| return do_in_jni_thread( |
| Bind(&BTA_GATTC_ServiceSearchRequest, conn_id, nullptr)); |
| } |
| } |
| |
| void btif_gattc_get_gatt_db_impl(int conn_id) { |
| btgatt_db_element_t* db = NULL; |
| int count = 0; |
| BTA_GATTC_GetGattDb(conn_id, 0x0000, 0xFFFF, &db, &count); |
| |
| HAL_CBACK(bt_gatt_callbacks, client->get_gatt_db_cb, conn_id, db, count); |
| osi_free(db); |
| } |
| |
| bt_status_t btif_gattc_get_gatt_db(int conn_id) { |
| CHECK_BTGATT_INIT(); |
| return do_in_jni_thread(Bind(&btif_gattc_get_gatt_db_impl, conn_id)); |
| } |
| |
| void read_char_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, uint8_t* value, void* data) { |
| btgatt_read_params_t* params = new btgatt_read_params_t; |
| params->value_type = 0x00 /* GATTC_READ_VALUE_TYPE_VALUE */; |
| params->status = status; |
| params->handle = handle; |
| params->value.len = len; |
| CHECK(len <= BTGATT_MAX_ATTR_LEN); |
| if (len > 0) memcpy(params->value.value, value, len); |
| |
| CLI_CBACK_IN_JNI(read_characteristic_cb, conn_id, status, |
| base::Owned(params)); |
| } |
| |
| 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)); |
| } |
| |
| void read_desc_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| uint16_t len, uint8_t* value, void* data) { |
| btgatt_read_params_t* params = new btgatt_read_params_t; |
| params->value_type = 0x00 /* GATTC_READ_VALUE_TYPE_VALUE */; |
| params->status = status; |
| params->handle = handle; |
| params->value.len = len; |
| CHECK(len <= BTGATT_MAX_ATTR_LEN); |
| if (len > 0) memcpy(params->value.value, value, len); |
| |
| CLI_CBACK_IN_JNI(read_descriptor_cb, conn_id, status, base::Owned(params)); |
| } |
| |
| bt_status_t btif_gattc_read_char_descr(int conn_id, uint16_t handle, |
| int auth_req) { |
| CHECK_BTGATT_INIT(); |
| return do_in_jni_thread(Bind(&BTA_GATTC_ReadCharDescr, conn_id, handle, |
| auth_req, read_desc_cb, nullptr)); |
| } |
| |
| void write_char_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| void* data) { |
| CLI_CBACK_IN_JNI(write_characteristic_cb, conn_id, status, handle); |
| } |
| |
| bt_status_t btif_gattc_write_char(int conn_id, uint16_t handle, int write_type, |
| int auth_req, vector<uint8_t> value) { |
| CHECK_BTGATT_INIT(); |
| |
| if (value.size() > BTGATT_MAX_ATTR_LEN) value.resize(BTGATT_MAX_ATTR_LEN); |
| |
| return do_in_jni_thread(Bind(&BTA_GATTC_WriteCharValue, conn_id, handle, |
| write_type, std::move(value), auth_req, |
| write_char_cb, nullptr)); |
| } |
| |
| void write_descr_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, |
| void* data) { |
| CLI_CBACK_IN_JNI(write_descriptor_cb, conn_id, status, handle); |
| } |
| |
| bt_status_t btif_gattc_write_char_descr(int conn_id, uint16_t handle, |
| int auth_req, vector<uint8_t> value) { |
| CHECK_BTGATT_INIT(); |
| |
| if (value.size() > BTGATT_MAX_ATTR_LEN) value.resize(BTGATT_MAX_ATTR_LEN); |
| |
| return do_in_jni_thread(Bind(&BTA_GATTC_WriteCharDescr, conn_id, handle, |
| std::move(value), auth_req, write_descr_cb, |
| nullptr)); |
| } |
| |
| bt_status_t btif_gattc_execute_write(int conn_id, int execute) { |
| CHECK_BTGATT_INIT(); |
| return do_in_jni_thread( |
| Bind(&BTA_GATTC_ExecuteWrite, conn_id, (uint8_t)execute)); |
| } |
| |
| void btif_gattc_reg_for_notification_impl(tBTA_GATTC_IF client_if, |
| const BD_ADDR bda, uint16_t handle) { |
| tBTA_GATT_STATUS status = BTA_GATTC_RegisterForNotifications( |
| client_if, const_cast<uint8_t*>(bda), handle); |
| |
| // TODO(jpawlowski): conn_id is currently unused |
| HAL_CBACK(bt_gatt_callbacks, client->register_for_notification_cb, |
| /* conn_id */ 0, 1, status, handle); |
| } |
| |
| bt_status_t btif_gattc_reg_for_notification(int client_if, |
| const bt_bdaddr_t* bd_addr, |
| uint16_t handle) { |
| CHECK_BTGATT_INIT(); |
| |
| uint8_t* address = new BD_ADDR; |
| bdcpy(address, bd_addr->address); |
| return do_in_jni_thread( |
| Bind(base::IgnoreResult(&btif_gattc_reg_for_notification_impl), client_if, |
| base::Owned(address), handle)); |
| } |
| |
| void btif_gattc_dereg_for_notification_impl(tBTA_GATTC_IF client_if, |
| const BD_ADDR bda, |
| uint16_t handle) { |
| tBTA_GATT_STATUS status = BTA_GATTC_DeregisterForNotifications( |
| client_if, const_cast<uint8_t*>(bda), handle); |
| |
| // TODO(jpawlowski): conn_id is currently unused |
| HAL_CBACK(bt_gatt_callbacks, client->register_for_notification_cb, |
| /* conn_id */ 0, 0, status, handle); |
| } |
| |
| bt_status_t btif_gattc_dereg_for_notification(int client_if, |
| const bt_bdaddr_t* bd_addr, |
| uint16_t handle) { |
| CHECK_BTGATT_INIT(); |
| |
| uint8_t* address = new BD_ADDR; |
| bdcpy(address, bd_addr->address); |
| return do_in_jni_thread( |
| Bind(base::IgnoreResult(&btif_gattc_dereg_for_notification_impl), |
| client_if, base::Owned(address), handle)); |
| } |
| |
| bt_status_t btif_gattc_read_remote_rssi(int client_if, |
| const bt_bdaddr_t* bd_addr) { |
| CHECK_BTGATT_INIT(); |
| rssi_request_client_if = client_if; |
| // Closure will own this value and free it. |
| uint8_t* address = new BD_ADDR; |
| bdcpy(address, bd_addr->address); |
| return do_in_jni_thread(Bind(base::IgnoreResult(&BTM_ReadRSSI), |
| base::Owned(address), |
| (tBTM_CMPL_CB*)btm_read_rssi_cb)); |
| } |
| |
| bt_status_t btif_gattc_configure_mtu(int conn_id, int mtu) { |
| CHECK_BTGATT_INIT(); |
| return do_in_jni_thread( |
| Bind(base::IgnoreResult(&BTA_GATTC_ConfigureMTU), conn_id, mtu)); |
| } |
| |
| void btif_gattc_conn_parameter_update_impl(bt_bdaddr_t addr, int min_interval, |
| int max_interval, int latency, |
| int timeout) { |
| if (BTA_DmGetConnectionState(addr.address)) |
| BTA_DmBleUpdateConnectionParams(addr.address, min_interval, max_interval, |
| latency, timeout); |
| else |
| BTA_DmSetBlePrefConnParams(addr.address, min_interval, max_interval, |
| latency, timeout); |
| } |
| |
| bt_status_t btif_gattc_conn_parameter_update(const bt_bdaddr_t* bd_addr, |
| int min_interval, int max_interval, |
| int latency, int timeout) { |
| CHECK_BTGATT_INIT(); |
| return do_in_jni_thread( |
| Bind(base::IgnoreResult(&btif_gattc_conn_parameter_update_impl), *bd_addr, |
| min_interval, max_interval, latency, timeout)); |
| } |
| |
| int btif_gattc_get_device_type(const bt_bdaddr_t* bd_addr) { |
| int device_type = 0; |
| char bd_addr_str[18] = {0}; |
| |
| bdaddr_to_string(bd_addr, bd_addr_str, sizeof(bd_addr_str)); |
| if (btif_config_get_int(bd_addr_str, "DevType", &device_type)) |
| return device_type; |
| return 0; |
| } |
| |
| bt_status_t btif_gattc_test_command(int command, btgatt_test_params_t* params) { |
| return btif_gattc_test_command_impl(command, params); |
| } |
| |
| } // namespace |
| |
| const btgatt_client_interface_t btgattClientInterface = { |
| btif_gattc_register_app, |
| btif_gattc_unregister_app, |
| btif_gattc_open, |
| btif_gattc_close, |
| btif_gattc_refresh, |
| btif_gattc_search_service, |
| btif_gattc_read_char, |
| btif_gattc_write_char, |
| btif_gattc_read_char_descr, |
| btif_gattc_write_char_descr, |
| btif_gattc_execute_write, |
| btif_gattc_reg_for_notification, |
| btif_gattc_dereg_for_notification, |
| btif_gattc_read_remote_rssi, |
| btif_gattc_get_device_type, |
| btif_gattc_configure_mtu, |
| btif_gattc_conn_parameter_update, |
| btif_gattc_test_command, |
| btif_gattc_get_gatt_db}; |