1 /****************************************************************************** 2 * 3 * Copyright 2009-2014 Broadcom Corporation 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19 /******************************************************************************* 20 * 21 * Filename: btif_gatt_client.c 22 * 23 * Description: GATT client implementation 24 * 25 ******************************************************************************/ 26 27 #define LOG_TAG "bt_btif_gattc" 28 29 #include <base/at_exit.h> 30 #include <base/bind.h> 31 #include <base/threading/thread.h> 32 #include <errno.h> 33 #include <hardware/bluetooth.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include "device/include/controller.h" 37 38 #include "btif_common.h" 39 #include "btif_util.h" 40 41 #include <hardware/bt_gatt.h> 42 43 #include "bta_api.h" 44 #include "bta_closure_api.h" 45 #include "bta_gatt_api.h" 46 #include "btif_config.h" 47 #include "btif_dm.h" 48 #include "btif_gatt.h" 49 #include "btif_gatt_util.h" 50 #include "btif_storage.h" 51 #include "osi/include/log.h" 52 #include "vendor_api.h" 53 54 using base::Bind; 55 using base::Owned; 56 using bluetooth::Uuid; 57 using std::vector; 58 59 extern bt_status_t btif_gattc_test_command_impl( 60 int command, const btgatt_test_params_t* params); 61 extern const btgatt_callbacks_t* bt_gatt_callbacks; 62 63 /******************************************************************************* 64 * Constants & Macros 65 ******************************************************************************/ 66 67 #define CLI_CBACK_IN_JNI(P_CBACK, ...) \ 68 do { \ 69 if (bt_gatt_callbacks && bt_gatt_callbacks->client->P_CBACK) { \ 70 BTIF_TRACE_API("HAL bt_gatt_callbacks->client->%s", #P_CBACK); \ 71 do_in_jni_thread(Bind(bt_gatt_callbacks->client->P_CBACK, __VA_ARGS__)); \ 72 } else { \ 73 ASSERTC(0, "Callback is NULL", 0); \ 74 } \ 75 } while (0) 76 77 #define CHECK_BTGATT_INIT() \ 78 do { \ 79 if (bt_gatt_callbacks == NULL) { \ 80 LOG_WARN(LOG_TAG, "%s: BTGATT not initialized", __func__); \ 81 return BT_STATUS_NOT_READY; \ 82 } else { \ 83 LOG_VERBOSE(LOG_TAG, "%s", __func__); \ 84 } \ 85 } while (0) 86 87 #define BLE_RESOLVE_ADDR_MSB \ 88 0x40 /* bit7, bit6 is 01 to be resolvable random \ 89 */ 90 #define BLE_RESOLVE_ADDR_MASK 0xc0 /* bit 6, and bit7 */ 91 inline bool BTM_BLE_IS_RESOLVE_BDA(const RawAddress& x) { 92 return ((x.address)[0] & BLE_RESOLVE_ADDR_MASK) == BLE_RESOLVE_ADDR_MSB; 93 } 94 namespace { 95 96 uint8_t rssi_request_client_if; 97 98 void btif_gattc_upstreams_evt(uint16_t event, char* p_param) { 99 LOG_VERBOSE(LOG_TAG, "%s: Event %d", __func__, event); 100 101 tBTA_GATTC* p_data = (tBTA_GATTC*)p_param; 102 switch (event) { 103 case BTA_GATTC_DEREG_EVT: 104 break; 105 106 case BTA_GATTC_EXEC_EVT: { 107 HAL_CBACK(bt_gatt_callbacks, client->execute_write_cb, 108 p_data->exec_cmpl.conn_id, p_data->exec_cmpl.status); 109 break; 110 } 111 112 case BTA_GATTC_SEARCH_CMPL_EVT: { 113 HAL_CBACK(bt_gatt_callbacks, client->search_complete_cb, 114 p_data->search_cmpl.conn_id, p_data->search_cmpl.status); 115 break; 116 } 117 118 case BTA_GATTC_NOTIF_EVT: { 119 btgatt_notify_params_t data; 120 121 data.bda = p_data->notify.bda; 122 memcpy(data.value, p_data->notify.value, p_data->notify.len); 123 124 data.handle = p_data->notify.handle; 125 data.is_notify = p_data->notify.is_notify; 126 data.len = p_data->notify.len; 127 128 HAL_CBACK(bt_gatt_callbacks, client->notify_cb, p_data->notify.conn_id, 129 data); 130 131 if (!p_data->notify.is_notify) 132 BTA_GATTC_SendIndConfirm(p_data->notify.conn_id, p_data->notify.handle); 133 134 break; 135 } 136 137 case BTA_GATTC_OPEN_EVT: { 138 DVLOG(1) << "BTA_GATTC_OPEN_EVT " << p_data->open.remote_bda; 139 HAL_CBACK(bt_gatt_callbacks, client->open_cb, p_data->open.conn_id, 140 p_data->open.status, p_data->open.client_if, 141 p_data->open.remote_bda); 142 143 if (GATT_DEF_BLE_MTU_SIZE != p_data->open.mtu && p_data->open.mtu) { 144 HAL_CBACK(bt_gatt_callbacks, client->configure_mtu_cb, 145 p_data->open.conn_id, p_data->open.status, p_data->open.mtu); 146 } 147 148 if (p_data->open.status == GATT_SUCCESS) 149 btif_gatt_check_encrypted_link(p_data->open.remote_bda, 150 p_data->open.transport); 151 break; 152 } 153 154 case BTA_GATTC_CLOSE_EVT: { 155 HAL_CBACK(bt_gatt_callbacks, client->close_cb, p_data->close.conn_id, 156 p_data->status, p_data->close.client_if, 157 p_data->close.remote_bda); 158 break; 159 } 160 161 case BTA_GATTC_ACL_EVT: 162 LOG_DEBUG(LOG_TAG, "BTA_GATTC_ACL_EVT: status = %d", p_data->status); 163 /* Ignore for now */ 164 break; 165 166 case BTA_GATTC_CANCEL_OPEN_EVT: 167 break; 168 169 case BTA_GATTC_CFG_MTU_EVT: { 170 HAL_CBACK(bt_gatt_callbacks, client->configure_mtu_cb, 171 p_data->cfg_mtu.conn_id, p_data->cfg_mtu.status, 172 p_data->cfg_mtu.mtu); 173 break; 174 } 175 176 case BTA_GATTC_CONGEST_EVT: 177 HAL_CBACK(bt_gatt_callbacks, client->congestion_cb, 178 p_data->congest.conn_id, p_data->congest.congested); 179 break; 180 181 case BTA_GATTC_PHY_UPDATE_EVT: 182 HAL_CBACK(bt_gatt_callbacks, client->phy_updated_cb, 183 p_data->phy_update.conn_id, p_data->phy_update.tx_phy, 184 p_data->phy_update.rx_phy, p_data->phy_update.status); 185 break; 186 187 case BTA_GATTC_CONN_UPDATE_EVT: 188 HAL_CBACK(bt_gatt_callbacks, client->conn_updated_cb, 189 p_data->conn_update.conn_id, p_data->conn_update.interval, 190 p_data->conn_update.latency, p_data->conn_update.timeout, 191 p_data->conn_update.status); 192 break; 193 194 default: 195 LOG_ERROR(LOG_TAG, "%s: Unhandled event (%d)!", __func__, event); 196 break; 197 } 198 } 199 200 void bta_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) { 201 bt_status_t status = 202 btif_transfer_context(btif_gattc_upstreams_evt, (uint16_t)event, 203 (char*)p_data, sizeof(tBTA_GATTC), NULL); 204 ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status); 205 } 206 207 void btm_read_rssi_cb(void* p_void) { 208 tBTM_RSSI_RESULT* p_result = (tBTM_RSSI_RESULT*)p_void; 209 210 if (!p_result) return; 211 212 CLI_CBACK_IN_JNI(read_remote_rssi_cb, rssi_request_client_if, 213 p_result->rem_bda, p_result->rssi, p_result->status); 214 } 215 216 /******************************************************************************* 217 * Client API Functions 218 ******************************************************************************/ 219 220 bt_status_t btif_gattc_register_app(const Uuid& uuid) { 221 CHECK_BTGATT_INIT(); 222 223 return do_in_jni_thread(Bind( 224 [](const Uuid& uuid) { 225 BTA_GATTC_AppRegister( 226 bta_gattc_cback, 227 base::Bind( 228 [](const Uuid& uuid, uint8_t client_id, uint8_t status) { 229 do_in_jni_thread(Bind( 230 [](const Uuid& uuid, uint8_t client_id, uint8_t status) { 231 HAL_CBACK(bt_gatt_callbacks, client->register_client_cb, 232 status, client_id, uuid); 233 }, 234 uuid, client_id, status)); 235 }, 236 uuid)); 237 }, 238 uuid)); 239 } 240 241 void btif_gattc_unregister_app_impl(int client_if) { 242 BTA_GATTC_AppDeregister(client_if); 243 } 244 245 bt_status_t btif_gattc_unregister_app(int client_if) { 246 CHECK_BTGATT_INIT(); 247 return do_in_jni_thread(Bind(&btif_gattc_unregister_app_impl, client_if)); 248 } 249 250 void btif_gattc_open_impl(int client_if, RawAddress address, bool is_direct, 251 int transport_p, bool opportunistic, 252 int initiating_phys) { 253 // Ensure device is in inquiry database 254 int addr_type = 0; 255 int device_type = 0; 256 tGATT_TRANSPORT transport = (tGATT_TRANSPORT)GATT_TRANSPORT_LE; 257 258 if (btif_get_address_type(address, &addr_type) && 259 btif_get_device_type(address, &device_type) && 260 device_type != BT_DEVICE_TYPE_BREDR) { 261 BTA_DmAddBleDevice(address, addr_type, device_type); 262 } 263 264 // Check for background connections 265 if (!is_direct) { 266 // Check for privacy 1.0 and 1.1 controller and do not start background 267 // connection if RPA offloading is not supported, since it will not 268 // connect after change of random address 269 if (!controller_get_interface()->supports_ble_privacy() && 270 (addr_type == BLE_ADDR_RANDOM) && BTM_BLE_IS_RESOLVE_BDA(address)) { 271 tBTM_BLE_VSC_CB vnd_capabilities; 272 BTM_BleGetVendorCapabilities(&vnd_capabilities); 273 if (!vnd_capabilities.rpa_offloading) { 274 HAL_CBACK(bt_gatt_callbacks, client->open_cb, 0, BT_STATUS_UNSUPPORTED, 275 client_if, address); 276 return; 277 } 278 } 279 BTA_DmBleStartAutoConn(); 280 } 281 282 // Determine transport 283 if (transport_p != GATT_TRANSPORT_AUTO) { 284 transport = transport_p; 285 } else { 286 switch (device_type) { 287 case BT_DEVICE_TYPE_BREDR: 288 transport = GATT_TRANSPORT_BR_EDR; 289 break; 290 291 case BT_DEVICE_TYPE_BLE: 292 transport = GATT_TRANSPORT_LE; 293 break; 294 295 case BT_DEVICE_TYPE_DUMO: 296 if (transport_p == GATT_TRANSPORT_LE) 297 transport = GATT_TRANSPORT_LE; 298 else 299 transport = GATT_TRANSPORT_BR_EDR; 300 break; 301 } 302 } 303 304 // Connect! 305 BTIF_TRACE_DEBUG("%s Transport=%d, device type=%d, phy=%d", __func__, 306 transport, device_type, initiating_phys); 307 BTA_GATTC_Open(client_if, address, is_direct, transport, opportunistic, 308 initiating_phys); 309 } 310 311 bt_status_t btif_gattc_open(int client_if, const RawAddress& bd_addr, 312 bool is_direct, int transport, bool opportunistic, 313 int initiating_phys) { 314 CHECK_BTGATT_INIT(); 315 // Closure will own this value and free it. 316 return do_in_jni_thread(Bind(&btif_gattc_open_impl, client_if, bd_addr, 317 is_direct, transport, opportunistic, 318 initiating_phys)); 319 } 320 321 void btif_gattc_close_impl(int client_if, RawAddress address, int conn_id) { 322 // Disconnect established connections 323 if (conn_id != 0) 324 BTA_GATTC_Close(conn_id); 325 else 326 BTA_GATTC_CancelOpen(client_if, address, true); 327 328 // Cancel pending background connections (remove from whitelist) 329 BTA_GATTC_CancelOpen(client_if, address, false); 330 } 331 332 bt_status_t btif_gattc_close(int client_if, const RawAddress& bd_addr, 333 int conn_id) { 334 CHECK_BTGATT_INIT(); 335 return do_in_jni_thread( 336 Bind(&btif_gattc_close_impl, client_if, bd_addr, conn_id)); 337 } 338 339 bt_status_t btif_gattc_refresh(int client_if, const RawAddress& bd_addr) { 340 CHECK_BTGATT_INIT(); 341 return do_in_jni_thread(Bind(&BTA_GATTC_Refresh, bd_addr)); 342 } 343 344 bt_status_t btif_gattc_search_service(int conn_id, const Uuid* filter_uuid) { 345 CHECK_BTGATT_INIT(); 346 347 if (filter_uuid) { 348 Uuid* uuid = new Uuid(*filter_uuid); 349 return do_in_jni_thread( 350 Bind(&BTA_GATTC_ServiceSearchRequest, conn_id, base::Owned(uuid))); 351 } else { 352 return do_in_jni_thread( 353 Bind(&BTA_GATTC_ServiceSearchRequest, conn_id, nullptr)); 354 } 355 } 356 357 void btif_gattc_discover_service_by_uuid(int conn_id, const Uuid& uuid) { 358 do_in_jni_thread(Bind(&BTA_GATTC_DiscoverServiceByUuid, conn_id, uuid)); 359 } 360 361 void btif_gattc_get_gatt_db_impl(int conn_id) { 362 btgatt_db_element_t* db = NULL; 363 int count = 0; 364 BTA_GATTC_GetGattDb(conn_id, 0x0000, 0xFFFF, &db, &count); 365 366 HAL_CBACK(bt_gatt_callbacks, client->get_gatt_db_cb, conn_id, db, count); 367 osi_free(db); 368 } 369 370 bt_status_t btif_gattc_get_gatt_db(int conn_id) { 371 CHECK_BTGATT_INIT(); 372 return do_in_jni_thread(Bind(&btif_gattc_get_gatt_db_impl, conn_id)); 373 } 374 375 void read_char_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, 376 uint16_t len, uint8_t* value, void* data) { 377 btgatt_read_params_t* params = new btgatt_read_params_t; 378 params->value_type = 0x00 /* GATTC_READ_VALUE_TYPE_VALUE */; 379 params->status = status; 380 params->handle = handle; 381 params->value.len = len; 382 CHECK(len <= BTGATT_MAX_ATTR_LEN); 383 if (len > 0) memcpy(params->value.value, value, len); 384 385 // clang-tidy analyzer complains about |params| is leaked. It doesn't know 386 // that |param| will be freed by the callback function. 387 CLI_CBACK_IN_JNI(read_characteristic_cb, conn_id, status, /* NOLINT */ 388 base::Owned(params)); 389 } 390 391 bt_status_t btif_gattc_read_char(int conn_id, uint16_t handle, int auth_req) { 392 CHECK_BTGATT_INIT(); 393 return do_in_jni_thread(Bind(&BTA_GATTC_ReadCharacteristic, conn_id, handle, 394 auth_req, read_char_cb, nullptr)); 395 } 396 397 void read_using_char_uuid_cb(uint16_t conn_id, tGATT_STATUS status, 398 uint16_t handle, uint16_t len, uint8_t* value, 399 void* data) { 400 btgatt_read_params_t* params = new btgatt_read_params_t; 401 params->value_type = 0x00 /* GATTC_READ_VALUE_TYPE_VALUE */; 402 params->status = status; 403 params->handle = handle; 404 params->value.len = len; 405 CHECK(len <= BTGATT_MAX_ATTR_LEN); 406 if (len > 0) memcpy(params->value.value, value, len); 407 408 // clang-tidy analyzer complains about |params| is leaked. It doesn't know 409 // that |param| will be freed by the callback function. 410 CLI_CBACK_IN_JNI(read_characteristic_cb, conn_id, status, /* NOLINT */ 411 base::Owned(params)); 412 } 413 414 bt_status_t btif_gattc_read_using_char_uuid(int conn_id, const Uuid& uuid, 415 uint16_t s_handle, 416 uint16_t e_handle, int auth_req) { 417 CHECK_BTGATT_INIT(); 418 return do_in_jni_thread(Bind(&BTA_GATTC_ReadUsingCharUuid, conn_id, uuid, 419 s_handle, e_handle, auth_req, 420 read_using_char_uuid_cb, nullptr)); 421 } 422 423 void read_desc_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, 424 uint16_t len, uint8_t* value, void* data) { 425 btgatt_read_params_t params; 426 params.value_type = 0x00 /* GATTC_READ_VALUE_TYPE_VALUE */; 427 params.status = status; 428 params.handle = handle; 429 params.value.len = len; 430 CHECK(len <= BTGATT_MAX_ATTR_LEN); 431 if (len > 0) memcpy(params.value.value, value, len); 432 433 CLI_CBACK_IN_JNI(read_descriptor_cb, conn_id, status, params); 434 } 435 436 bt_status_t btif_gattc_read_char_descr(int conn_id, uint16_t handle, 437 int auth_req) { 438 CHECK_BTGATT_INIT(); 439 return do_in_jni_thread(Bind(&BTA_GATTC_ReadCharDescr, conn_id, handle, 440 auth_req, read_desc_cb, nullptr)); 441 } 442 443 void write_char_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, 444 void* data) { 445 CLI_CBACK_IN_JNI(write_characteristic_cb, conn_id, status, handle); 446 } 447 448 bt_status_t btif_gattc_write_char(int conn_id, uint16_t handle, int write_type, 449 int auth_req, vector<uint8_t> value) { 450 CHECK_BTGATT_INIT(); 451 452 if (value.size() > BTGATT_MAX_ATTR_LEN) value.resize(BTGATT_MAX_ATTR_LEN); 453 454 return do_in_jni_thread(Bind(&BTA_GATTC_WriteCharValue, conn_id, handle, 455 write_type, std::move(value), auth_req, 456 write_char_cb, nullptr)); 457 } 458 459 void write_descr_cb(uint16_t conn_id, tGATT_STATUS status, uint16_t handle, 460 void* data) { 461 CLI_CBACK_IN_JNI(write_descriptor_cb, conn_id, status, handle); 462 } 463 464 bt_status_t btif_gattc_write_char_descr(int conn_id, uint16_t handle, 465 int auth_req, vector<uint8_t> value) { 466 CHECK_BTGATT_INIT(); 467 468 if (value.size() > BTGATT_MAX_ATTR_LEN) value.resize(BTGATT_MAX_ATTR_LEN); 469 470 return do_in_jni_thread(Bind(&BTA_GATTC_WriteCharDescr, conn_id, handle, 471 std::move(value), auth_req, write_descr_cb, 472 nullptr)); 473 } 474 475 bt_status_t btif_gattc_execute_write(int conn_id, int execute) { 476 CHECK_BTGATT_INIT(); 477 return do_in_jni_thread( 478 Bind(&BTA_GATTC_ExecuteWrite, conn_id, (uint8_t)execute)); 479 } 480 481 void btif_gattc_reg_for_notification_impl(tGATT_IF client_if, 482 const RawAddress& bda, 483 uint16_t handle) { 484 tGATT_STATUS status = 485 BTA_GATTC_RegisterForNotifications(client_if, bda, handle); 486 487 // TODO(jpawlowski): conn_id is currently unused 488 HAL_CBACK(bt_gatt_callbacks, client->register_for_notification_cb, 489 /* conn_id */ 0, 1, status, handle); 490 } 491 492 bt_status_t btif_gattc_reg_for_notification(int client_if, 493 const RawAddress& bd_addr, 494 uint16_t handle) { 495 CHECK_BTGATT_INIT(); 496 497 return do_in_jni_thread( 498 Bind(base::IgnoreResult(&btif_gattc_reg_for_notification_impl), client_if, 499 bd_addr, handle)); 500 } 501 502 void btif_gattc_dereg_for_notification_impl(tGATT_IF client_if, 503 const RawAddress& bda, 504 uint16_t handle) { 505 tGATT_STATUS status = 506 BTA_GATTC_DeregisterForNotifications(client_if, bda, handle); 507 508 // TODO(jpawlowski): conn_id is currently unused 509 HAL_CBACK(bt_gatt_callbacks, client->register_for_notification_cb, 510 /* conn_id */ 0, 0, status, handle); 511 } 512 513 bt_status_t btif_gattc_dereg_for_notification(int client_if, 514 const RawAddress& bd_addr, 515 uint16_t handle) { 516 CHECK_BTGATT_INIT(); 517 518 return do_in_jni_thread( 519 Bind(base::IgnoreResult(&btif_gattc_dereg_for_notification_impl), 520 client_if, bd_addr, handle)); 521 } 522 523 bt_status_t btif_gattc_read_remote_rssi(int client_if, 524 const RawAddress& bd_addr) { 525 CHECK_BTGATT_INIT(); 526 rssi_request_client_if = client_if; 527 528 return do_in_jni_thread( 529 Bind(base::IgnoreResult(&BTM_ReadRSSI), bd_addr, btm_read_rssi_cb)); 530 } 531 532 bt_status_t btif_gattc_configure_mtu(int conn_id, int mtu) { 533 CHECK_BTGATT_INIT(); 534 return do_in_jni_thread( 535 Bind(base::IgnoreResult(&BTA_GATTC_ConfigureMTU), conn_id, mtu)); 536 } 537 538 void btif_gattc_conn_parameter_update_impl(RawAddress addr, int min_interval, 539 int max_interval, int latency, 540 int timeout, uint16_t min_ce_len, 541 uint16_t max_ce_len) { 542 if (BTA_DmGetConnectionState(addr)) 543 BTA_DmBleUpdateConnectionParams(addr, min_interval, max_interval, latency, 544 timeout, min_ce_len, max_ce_len); 545 else 546 BTA_DmSetBlePrefConnParams(addr, min_interval, max_interval, latency, 547 timeout); 548 } 549 550 bt_status_t btif_gattc_conn_parameter_update(const RawAddress& bd_addr, 551 int min_interval, int max_interval, 552 int latency, int timeout, 553 uint16_t min_ce_len, 554 uint16_t max_ce_len) { 555 CHECK_BTGATT_INIT(); 556 return do_in_jni_thread(Bind( 557 base::IgnoreResult(&btif_gattc_conn_parameter_update_impl), bd_addr, 558 min_interval, max_interval, latency, timeout, min_ce_len, max_ce_len)); 559 } 560 561 bt_status_t btif_gattc_set_preferred_phy(const RawAddress& bd_addr, 562 uint8_t tx_phy, uint8_t rx_phy, 563 uint16_t phy_options) { 564 CHECK_BTGATT_INIT(); 565 do_in_bta_thread(FROM_HERE, 566 Bind(&BTM_BleSetPhy, bd_addr, tx_phy, rx_phy, phy_options)); 567 return BT_STATUS_SUCCESS; 568 } 569 570 bt_status_t btif_gattc_read_phy( 571 const RawAddress& bd_addr, 572 base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb) { 573 CHECK_BTGATT_INIT(); 574 do_in_bta_thread(FROM_HERE, Bind(&BTM_BleReadPhy, bd_addr, 575 jni_thread_wrapper(FROM_HERE, cb))); 576 return BT_STATUS_SUCCESS; 577 } 578 579 int btif_gattc_get_device_type(const RawAddress& bd_addr) { 580 int device_type = 0; 581 582 if (btif_config_get_int(bd_addr.ToString().c_str(), "DevType", &device_type)) 583 return device_type; 584 return 0; 585 } 586 587 bt_status_t btif_gattc_test_command(int command, 588 const btgatt_test_params_t& params) { 589 return btif_gattc_test_command_impl(command, ¶ms); 590 } 591 592 } // namespace 593 594 const btgatt_client_interface_t btgattClientInterface = { 595 btif_gattc_register_app, 596 btif_gattc_unregister_app, 597 btif_gattc_open, 598 btif_gattc_close, 599 btif_gattc_refresh, 600 btif_gattc_search_service, 601 btif_gattc_discover_service_by_uuid, 602 btif_gattc_read_char, 603 btif_gattc_read_using_char_uuid, 604 btif_gattc_write_char, 605 btif_gattc_read_char_descr, 606 btif_gattc_write_char_descr, 607 btif_gattc_execute_write, 608 btif_gattc_reg_for_notification, 609 btif_gattc_dereg_for_notification, 610 btif_gattc_read_remote_rssi, 611 btif_gattc_get_device_type, 612 btif_gattc_configure_mtu, 613 btif_gattc_conn_parameter_update, 614 btif_gattc_set_preferred_phy, 615 btif_gattc_read_phy, 616 btif_gattc_test_command, 617 btif_gattc_get_gatt_db}; 618