blob: 6bf41e46fe7fea399eff64d704c1080d62b1860a [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2009-2013 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_server.c
*
* Description: GATT server implementation
*
***********************************************************************************/
#define LOG_TAG "bt_btif_gatt"
#include <base/bind.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hardware/bluetooth.h>
#include <hardware/bt_gatt.h>
#include "btif_common.h"
#include "btif_util.h"
#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
#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 "bt_common.h"
#include "osi/include/log.h"
using base::Bind;
using base::Owned;
extern bt_status_t do_in_jni_thread(const base::Closure& task);
/************************************************************************************
** Constants & Macros
************************************************************************************/
#define CHECK_BTGATT_INIT() if (bt_gatt_callbacks == NULL)\
{\
LOG_WARN(LOG_TAG, "%s: BTGATT not initialized", __FUNCTION__);\
return BT_STATUS_NOT_READY;\
} else {\
LOG_VERBOSE(LOG_TAG, "%s", __FUNCTION__);\
}
/************************************************************************************
** Static variables
************************************************************************************/
extern const btgatt_callbacks_t *bt_gatt_callbacks;
/************************************************************************************
** Static functions
************************************************************************************/
static void btapp_gatts_copy_req_data(UINT16 event, char *p_dest, char *p_src)
{
tBTA_GATTS *p_dest_data = (tBTA_GATTS*) p_dest;
tBTA_GATTS *p_src_data = (tBTA_GATTS*) p_src;
if (!p_src_data || !p_dest_data)
return;
// Copy basic structure first
maybe_non_aligned_memcpy(p_dest_data, p_src_data, sizeof(*p_src_data));
// Allocate buffer for request data if necessary
switch (event)
{
case BTA_GATTS_READ_EVT:
case BTA_GATTS_WRITE_EVT:
case BTA_GATTS_EXEC_WRITE_EVT:
case BTA_GATTS_MTU_EVT:
p_dest_data->req_data.p_data = (tBTA_GATTS_REQ_DATA *)osi_malloc(sizeof(tBTA_GATTS_REQ_DATA));
memcpy(p_dest_data->req_data.p_data, p_src_data->req_data.p_data,
sizeof(tBTA_GATTS_REQ_DATA));
break;
default:
break;
}
}
static void btapp_gatts_free_req_data(UINT16 event, tBTA_GATTS *p_data)
{
switch (event)
{
case BTA_GATTS_READ_EVT:
case BTA_GATTS_WRITE_EVT:
case BTA_GATTS_EXEC_WRITE_EVT:
case BTA_GATTS_MTU_EVT:
if (p_data != NULL)
osi_free_and_reset((void **)&p_data->req_data.p_data);
break;
default:
break;
}
}
static void btapp_gatts_handle_cback(uint16_t event, char* p_param)
{
LOG_VERBOSE(LOG_TAG, "%s: Event %d", __FUNCTION__, event);
tBTA_GATTS *p_data = (tBTA_GATTS*)p_param;
switch (event)
{
case BTA_GATTS_REG_EVT:
{
bt_uuid_t app_uuid;
bta_to_btif_uuid(&app_uuid, &p_data->reg_oper.uuid);
HAL_CBACK(bt_gatt_callbacks, server->register_server_cb
, p_data->reg_oper.status
, p_data->reg_oper.server_if
, &app_uuid
);
break;
}
case BTA_GATTS_DEREG_EVT:
break;
case BTA_GATTS_CONNECT_EVT:
{
bt_bdaddr_t bda;
bdcpy(bda.address, p_data->conn.remote_bda);
btif_gatt_check_encrypted_link(p_data->conn.remote_bda, p_data->conn.transport);
HAL_CBACK(bt_gatt_callbacks, server->connection_cb,
p_data->conn.conn_id, p_data->conn.server_if, TRUE, &bda);
break;
}
case BTA_GATTS_DISCONNECT_EVT:
{
bt_bdaddr_t bda;
bdcpy(bda.address, p_data->conn.remote_bda);
HAL_CBACK(bt_gatt_callbacks, server->connection_cb,
p_data->conn.conn_id, p_data->conn.server_if, FALSE, &bda);
break;
}
case BTA_GATTS_CREATE_EVT:
{
btgatt_srvc_id_t srvc_id;
srvc_id.is_primary = p_data->create.is_primary;
srvc_id.id.inst_id = p_data->create.svc_instance;
bta_to_btif_uuid(&srvc_id.id.uuid, &p_data->create.uuid);
HAL_CBACK(bt_gatt_callbacks, server->service_added_cb,
p_data->create.status, p_data->create.server_if, &srvc_id,
p_data->create.service_id
);
}
break;
case BTA_GATTS_ADD_INCL_SRVC_EVT:
HAL_CBACK(bt_gatt_callbacks, server->included_service_added_cb,
p_data->add_result.status,
p_data->add_result.server_if,
p_data->add_result.service_id,
p_data->add_result.attr_id);
break;
case BTA_GATTS_ADD_CHAR_EVT:
{
bt_uuid_t uuid;
bta_to_btif_uuid(&uuid, &p_data->add_result.char_uuid);
HAL_CBACK(bt_gatt_callbacks, server->characteristic_added_cb,
p_data->add_result.status,
p_data->add_result.server_if,
&uuid,
p_data->add_result.service_id,
p_data->add_result.attr_id);
break;
}
case BTA_GATTS_ADD_CHAR_DESCR_EVT:
{
bt_uuid_t uuid;
bta_to_btif_uuid(&uuid, &p_data->add_result.char_uuid);
HAL_CBACK(bt_gatt_callbacks, server->descriptor_added_cb,
p_data->add_result.status,
p_data->add_result.server_if,
&uuid,
p_data->add_result.service_id,
p_data->add_result.attr_id);
break;
}
case BTA_GATTS_START_EVT:
HAL_CBACK(bt_gatt_callbacks, server->service_started_cb,
p_data->srvc_oper.status,
p_data->srvc_oper.server_if,
p_data->srvc_oper.service_id);
break;
case BTA_GATTS_STOP_EVT:
HAL_CBACK(bt_gatt_callbacks, server->service_stopped_cb,
p_data->srvc_oper.status,
p_data->srvc_oper.server_if,
p_data->srvc_oper.service_id);
break;
case BTA_GATTS_DELELTE_EVT:
HAL_CBACK(bt_gatt_callbacks, server->service_deleted_cb,
p_data->srvc_oper.status,
p_data->srvc_oper.server_if,
p_data->srvc_oper.service_id);
break;
case BTA_GATTS_READ_EVT:
{
bt_bdaddr_t bda;
bdcpy(bda.address, p_data->req_data.remote_bda);
HAL_CBACK(bt_gatt_callbacks, server->request_read_cb,
p_data->req_data.conn_id,p_data->req_data.trans_id, &bda,
p_data->req_data.p_data->read_req.handle,
p_data->req_data.p_data->read_req.offset,
p_data->req_data.p_data->read_req.is_long);
break;
}
case BTA_GATTS_WRITE_EVT:
{
bt_bdaddr_t bda;
bdcpy(bda.address, p_data->req_data.remote_bda);
HAL_CBACK(bt_gatt_callbacks, server->request_write_cb,
p_data->req_data.conn_id,p_data->req_data.trans_id, &bda,
p_data->req_data.p_data->write_req.handle,
p_data->req_data.p_data->write_req.offset,
p_data->req_data.p_data->write_req.len,
p_data->req_data.p_data->write_req.need_rsp,
p_data->req_data.p_data->write_req.is_prep,
p_data->req_data.p_data->write_req.value);
break;
}
case BTA_GATTS_EXEC_WRITE_EVT:
{
bt_bdaddr_t bda;
bdcpy(bda.address, p_data->req_data.remote_bda);
HAL_CBACK(bt_gatt_callbacks, server->request_exec_write_cb,
p_data->req_data.conn_id,p_data->req_data.trans_id, &bda,
p_data->req_data.p_data->exec_write);
break;
}
case BTA_GATTS_CONF_EVT:
HAL_CBACK(bt_gatt_callbacks, server->indication_sent_cb,
p_data->req_data.conn_id, p_data->req_data.status);
break;
case BTA_GATTS_CONGEST_EVT:
HAL_CBACK(bt_gatt_callbacks, server->congestion_cb
, p_data->congest.conn_id
, p_data->congest.congested
);
break;
case BTA_GATTS_MTU_EVT:
HAL_CBACK(bt_gatt_callbacks, server->mtu_changed_cb
, p_data->req_data.conn_id
, p_data->req_data.p_data->mtu
);
break;
case BTA_GATTS_OPEN_EVT:
case BTA_GATTS_CANCEL_OPEN_EVT:
case BTA_GATTS_CLOSE_EVT:
LOG_DEBUG(LOG_TAG, "%s: Empty event (%d)!", __FUNCTION__, event);
break;
default:
LOG_ERROR(LOG_TAG, "%s: Unhandled event (%d)!", __FUNCTION__, event);
break;
}
btapp_gatts_free_req_data(event, p_data);
}
static void btapp_gatts_cback(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
{
bt_status_t status;
status = btif_transfer_context(btapp_gatts_handle_cback, (uint16_t) event,
(char*)p_data, sizeof(tBTA_GATTS), btapp_gatts_copy_req_data);
ASSERTC(status == BT_STATUS_SUCCESS, "Context transfer failed!", status);
}
/************************************************************************************
** Server API Functions
************************************************************************************/
static bt_status_t btif_gatts_register_app(bt_uuid_t *bt_uuid) {
CHECK_BTGATT_INIT();
tBT_UUID *uuid = new tBT_UUID;
btif_to_bta_uuid(uuid, bt_uuid);
return do_in_jni_thread(
Bind(&BTA_GATTS_AppRegister, base::Owned(uuid), &btapp_gatts_cback));
}
static bt_status_t btif_gatts_unregister_app(int server_if) {
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(&BTA_GATTS_AppDeregister, server_if));
}
static void btif_gatts_open_impl(int server_if, BD_ADDR address, bool is_direct,
int transport_param) {
// Ensure device is in inquiry database
int addr_type = 0;
int device_type = 0;
tBTA_GATT_TRANSPORT 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);
}
// Mark background connections
if (!is_direct) BTA_DmBleSetBgConnType(BTM_BLE_CONN_AUTO, NULL);
// Determine transport
if (transport_param != GATT_TRANSPORT_AUTO) {
transport = transport_param;
} 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_param == GATT_TRANSPORT_LE)
transport = BTA_GATT_TRANSPORT_LE;
else
transport = BTA_GATT_TRANSPORT_BR_EDR;
break;
default:
BTIF_TRACE_ERROR("%s: Invalid device type %d", __func__, device_type);
return;
}
}
// Connect!
BTA_GATTS_Open(server_if, address, is_direct, transport);
}
static bt_status_t btif_gatts_open(int server_if, const bt_bdaddr_t *bd_addr,
bool is_direct, int transport) {
CHECK_BTGATT_INIT();
uint8_t *address = new BD_ADDR;
bdcpy(address, bd_addr->address);
return do_in_jni_thread(Bind(&btif_gatts_open_impl, server_if,
base::Owned(address), is_direct, transport));
}
static void btif_gatts_close_impl(int server_if, BD_ADDR address,
int conn_id) {
// Cancel pending foreground/background connections
BTA_GATTS_CancelOpen(server_if, address, TRUE);
BTA_GATTS_CancelOpen(server_if, address, FALSE);
// Close active connection
if (conn_id != 0) BTA_GATTS_Close(conn_id);
}
static bt_status_t btif_gatts_close(int server_if, const bt_bdaddr_t *bd_addr,
int conn_id) {
CHECK_BTGATT_INIT();
uint8_t *address = new BD_ADDR;
bdcpy(address, bd_addr->address);
return do_in_jni_thread(
Bind(&btif_gatts_close_impl, server_if, base::Owned(address), conn_id));
}
static bt_status_t btif_gatts_add_service(int server_if,
btgatt_srvc_id_t *srvc_id,
int num_handles) {
CHECK_BTGATT_INIT();
tBT_UUID *bt_uuid = new tBT_UUID;
btif_to_bta_uuid(bt_uuid, &srvc_id->id.uuid);
return do_in_jni_thread(
Bind(base::IgnoreResult(&BTA_GATTS_CreateService), server_if,
base::Owned(bt_uuid), (uint8_t)srvc_id->id.inst_id,
(uint16_t)num_handles, (bool)srvc_id->is_primary));
}
static bt_status_t btif_gatts_add_included_service(int server_if,
int service_handle,
int included_handle) {
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(&BTA_GATTS_AddIncludeService, service_handle, included_handle));
}
static bt_status_t btif_gatts_add_characteristic(int server_if,
int service_handle,
bt_uuid_t *uuid,
int properties,
int permissions) {
CHECK_BTGATT_INIT();
tBT_UUID *bt_uuid = new tBT_UUID;
btif_to_bta_uuid(bt_uuid, uuid);
return do_in_jni_thread(Bind(&BTA_GATTS_AddCharacteristic, service_handle,
base::Owned(bt_uuid), permissions, properties));
}
static bt_status_t btif_gatts_add_descriptor(int server_if, int service_handle,
bt_uuid_t *uuid, int permissions) {
CHECK_BTGATT_INIT();
tBT_UUID *bt_uuid = new tBT_UUID;
btif_to_bta_uuid(bt_uuid, uuid);
return do_in_jni_thread(Bind(&BTA_GATTS_AddCharDescriptor, service_handle,
permissions, base::Owned(bt_uuid)));
}
static bt_status_t btif_gatts_start_service(int server_if, int service_handle,
int transport) {
CHECK_BTGATT_INIT();
return do_in_jni_thread(
Bind(&BTA_GATTS_StartService, service_handle, transport));
}
static bt_status_t btif_gatts_stop_service(int server_if, int service_handle)
{
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(&BTA_GATTS_StopService, service_handle));
}
static bt_status_t btif_gatts_delete_service(int server_if, int service_handle)
{
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(&BTA_GATTS_DeleteService, service_handle));
}
static bt_status_t btif_gatts_send_indication(int server_if,
int attribute_handle, int conn_id,
int len, int confirm,
char *p_value) {
CHECK_BTGATT_INIT();
len = len > BTGATT_MAX_ATTR_LEN ? BTGATT_MAX_ATTR_LEN : len;
uint8_t *value = new uint8_t[len];
memcpy(value, p_value, len);
return do_in_jni_thread(Bind(&BTA_GATTS_HandleValueIndication, conn_id,
attribute_handle, len, base::Owned(value), confirm));
// TODO: Might need to send an ACK if handle value indication is
// invoked without need for confirmation.
}
static void btif_gatts_send_response_impl(int conn_id, int trans_id, int status,
btgatt_response_t response) {
tBTA_GATTS_RSP rsp_struct;
btif_to_bta_response(&rsp_struct, &response);
BTA_GATTS_SendRsp(conn_id, trans_id, status, &rsp_struct);
HAL_CBACK(bt_gatt_callbacks, server->response_confirmation_cb, 0,
rsp_struct.attr_value.handle);
}
static bt_status_t btif_gatts_send_response(int conn_id, int trans_id,
int status,
btgatt_response_t *response) {
CHECK_BTGATT_INIT();
return do_in_jni_thread(Bind(&btif_gatts_send_response_impl, conn_id,
trans_id, status, *response));
}
const btgatt_server_interface_t btgattServerInterface = {
btif_gatts_register_app,
btif_gatts_unregister_app,
btif_gatts_open,
btif_gatts_close,
btif_gatts_add_service,
btif_gatts_add_included_service,
btif_gatts_add_characteristic,
btif_gatts_add_descriptor,
btif_gatts_start_service,
btif_gatts_stop_service,
btif_gatts_delete_service,
btif_gatts_send_indication,
btif_gatts_send_response
};
#endif