780 lines
26 KiB
C++
780 lines
26 KiB
C++
//
|
|
// Copyright (C) 2015 Google, Inc.
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
#define LOG_TAG "bt_gatts"
|
|
|
|
#include "gatt_server_old.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <condition_variable>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <hardware/bluetooth.h>
|
|
#include <hardware/bt_gatt.h>
|
|
|
|
#include "service/hal/bluetooth_interface.h"
|
|
#include "service/logging_helpers.h"
|
|
|
|
extern "C" {
|
|
#include "osi/include/log.h"
|
|
#include "osi/include/osi.h"
|
|
} // extern "C"
|
|
|
|
namespace {
|
|
|
|
const size_t kMaxGattAttributeSize = 512;
|
|
// TODO(icoolidge): Difficult to generalize without knowing how many attributes.
|
|
const int kNumBlueDroidHandles = 60;
|
|
|
|
// TODO(icoolidge): Support multiple instances
|
|
// TODO(armansito): Remove this variable. No point of having this if
|
|
// each bluetooth::gatt::Server instance already keeps a pointer to the
|
|
// ServerInternals that is associated with it (which is much cleaner). It looks
|
|
// like this variable exists because the btif callbacks don't allow the
|
|
// upper-layer to pass user data to them. We could:
|
|
//
|
|
// 1. Fix the btif callbacks so that some sort of continuation can be
|
|
// attached to a callback. This might be a long shot since the callback
|
|
// interface doesn't allow more than one caller to register its own callbacks
|
|
// (which might be what we want though, since this would make the API more
|
|
// flexible).
|
|
//
|
|
// 2. Allow creation of Server objects using a factory method that returns
|
|
// the result asynchronously in a base::Callback. The RegisterServerCallback
|
|
// provides an |app_uuid|, which can be used to store callback structures in
|
|
// a map and lazily instantiate the Server and invoke the correct callback.
|
|
// This is a general pattern that we should use throughout the daemon, since
|
|
// all operations can timeout or fail and this is best reported in an
|
|
// asynchronous base::Callback.
|
|
//
|
|
static bluetooth::gatt::ServerInternals *g_internal = nullptr;
|
|
|
|
enum { kPipeReadEnd = 0, kPipeWriteEnd = 1, kPipeNumEnds = 2 };
|
|
|
|
} // namespace
|
|
|
|
namespace bluetooth {
|
|
namespace gatt {
|
|
|
|
struct Characteristic {
|
|
UUID uuid;
|
|
int blob_section;
|
|
std::vector<uint8_t> blob;
|
|
|
|
// Support synchronized blob updates by latching under mutex.
|
|
std::vector<uint8_t> next_blob;
|
|
bool next_blob_pending;
|
|
bool notify;
|
|
};
|
|
|
|
struct ServerInternals {
|
|
ServerInternals();
|
|
~ServerInternals();
|
|
int Initialize();
|
|
bt_status_t AddCharacteristic(
|
|
const UUID& uuid,
|
|
int properties,
|
|
int permissions);
|
|
|
|
// This maps API attribute UUIDs to BlueDroid handles.
|
|
std::map<UUID, int> uuid_to_attribute;
|
|
|
|
// The attribute cache, indexed by BlueDroid handles.
|
|
std::unordered_map<int, Characteristic> characteristics;
|
|
|
|
// Associate a control attribute with its value attribute.
|
|
std::unordered_map<int, int> controlled_blobs;
|
|
|
|
ScanResults scan_results;
|
|
|
|
UUID last_write;
|
|
const btgatt_interface_t *gatt;
|
|
int server_if;
|
|
int client_if;
|
|
int service_handle;
|
|
btgatt_srvc_id_t service_id;
|
|
std::set<int> connections;
|
|
|
|
std::mutex lock;
|
|
std::condition_variable api_synchronize;
|
|
int pipefd[kPipeNumEnds];
|
|
};
|
|
|
|
} // namespace gatt
|
|
} // namespace bluetooth
|
|
|
|
namespace {
|
|
|
|
/** Callback invoked in response to register_server */
|
|
void RegisterServerCallback(int status, int server_if, bt_uuid_t *app_uuid) {
|
|
LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d app_uuid:%p", __func__, status,
|
|
server_if, app_uuid);
|
|
|
|
g_internal->server_if = server_if;
|
|
|
|
btgatt_srvc_id_t service_id;
|
|
service_id.id.uuid = *app_uuid;
|
|
service_id.id.inst_id = 0;
|
|
service_id.is_primary = true;
|
|
|
|
g_internal->gatt->server->add_service(
|
|
server_if, &service_id, kNumBlueDroidHandles);
|
|
}
|
|
|
|
void ServiceAddedCallback(int status, int server_if, btgatt_srvc_id_t *srvc_id,
|
|
int srvc_handle) {
|
|
LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d gatt_srvc_id:%u srvc_handle:%d",
|
|
__func__, status, server_if, srvc_id->id.inst_id, srvc_handle);
|
|
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
g_internal->server_if = server_if;
|
|
g_internal->service_handle = srvc_handle;
|
|
g_internal->service_id = *srvc_id;
|
|
// This finishes the Initialize call.
|
|
g_internal->api_synchronize.notify_one();
|
|
}
|
|
|
|
void RequestReadCallback(int conn_id, int trans_id, bt_bdaddr_t *bda,
|
|
int attr_handle, int attribute_offset_octets,
|
|
bool is_long) {
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
|
|
bluetooth::gatt::Characteristic &ch = g_internal->characteristics[attr_handle];
|
|
|
|
// Latch next_blob to blob on a 'fresh' read.
|
|
if (ch.next_blob_pending && attribute_offset_octets == 0 &&
|
|
ch.blob_section == 0) {
|
|
std::swap(ch.blob, ch.next_blob);
|
|
ch.next_blob_pending = false;
|
|
}
|
|
|
|
const size_t blob_offset_octets =
|
|
std::min(ch.blob.size(), ch.blob_section * kMaxGattAttributeSize);
|
|
const size_t blob_remaining = ch.blob.size() - blob_offset_octets;
|
|
const size_t attribute_size = std::min(kMaxGattAttributeSize, blob_remaining);
|
|
|
|
std::string addr(BtAddrString(bda));
|
|
LOG_INFO(LOG_TAG,
|
|
"%s: connection:%d (%s) reading attr:%d attribute_offset_octets:%d "
|
|
"blob_section:%u (is_long:%u)",
|
|
__func__, conn_id, addr.c_str(), attr_handle, attribute_offset_octets,
|
|
ch.blob_section, is_long);
|
|
|
|
btgatt_response_t response;
|
|
response.attr_value.len = 0;
|
|
|
|
if (attribute_offset_octets < static_cast<int>(attribute_size)) {
|
|
std::copy(ch.blob.begin() + blob_offset_octets + attribute_offset_octets,
|
|
ch.blob.begin() + blob_offset_octets + attribute_size,
|
|
response.attr_value.value);
|
|
response.attr_value.len = attribute_size - attribute_offset_octets;
|
|
}
|
|
|
|
response.attr_value.handle = attr_handle;
|
|
response.attr_value.offset = attribute_offset_octets;
|
|
response.attr_value.auth_req = 0;
|
|
g_internal->gatt->server->send_response(conn_id, trans_id, 0, &response);
|
|
}
|
|
|
|
void RequestWriteCallback(int conn_id, int trans_id, bt_bdaddr_t *bda,
|
|
int attr_handle, int attribute_offset, int length,
|
|
bool need_rsp, bool is_prep, uint8_t *value) {
|
|
std::string addr(BtAddrString(bda));
|
|
LOG_INFO(LOG_TAG,
|
|
"%s: connection:%d (%s:trans:%d) write attr:%d attribute_offset:%d "
|
|
"length:%d "
|
|
"need_resp:%u is_prep:%u",
|
|
__func__, conn_id, addr.c_str(), trans_id, attr_handle, attribute_offset,
|
|
length, need_rsp, is_prep);
|
|
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
|
|
bluetooth::gatt::Characteristic &ch =
|
|
g_internal->characteristics[attr_handle];
|
|
|
|
ch.blob.resize(attribute_offset + length);
|
|
|
|
std::copy(value, value + length, ch.blob.begin() + attribute_offset);
|
|
|
|
auto target_blob = g_internal->controlled_blobs.find(attr_handle);
|
|
// If this is a control attribute, adjust offset of the target blob.
|
|
if (target_blob != g_internal->controlled_blobs.end() &&
|
|
ch.blob.size() == 1u) {
|
|
g_internal->characteristics[target_blob->second].blob_section = ch.blob[0];
|
|
LOG_INFO(LOG_TAG, "%s: updating attribute %d blob_section to %u", __func__,
|
|
target_blob->second, ch.blob[0]);
|
|
} else if (!is_prep) {
|
|
// This is a single frame characteristic write.
|
|
// Notify upwards because we're done now.
|
|
const bluetooth::UUID::UUID128Bit &attr_uuid = ch.uuid.GetFullBigEndian();
|
|
ssize_t status;
|
|
OSI_NO_INTR(status = write(g_internal->pipefd[kPipeWriteEnd],
|
|
attr_uuid.data(), attr_uuid.size()));
|
|
if (-1 == status)
|
|
LOG_ERROR(LOG_TAG, "%s: write failed: %s", __func__, strerror(errno));
|
|
} else {
|
|
// This is a multi-frame characteristic write.
|
|
// Wait for an 'RequestExecWriteCallback' to notify completion.
|
|
g_internal->last_write = ch.uuid;
|
|
}
|
|
|
|
// Respond only if needed.
|
|
if (!need_rsp) return;
|
|
|
|
btgatt_response_t response;
|
|
response.attr_value.handle = attr_handle;
|
|
response.attr_value.offset = attribute_offset;
|
|
response.attr_value.len = length;
|
|
response.attr_value.auth_req = 0;
|
|
// Provide written data back to sender for the response.
|
|
// Remote stacks use this to validate the success of the write.
|
|
std::copy(value, value + length, response.attr_value.value);
|
|
g_internal->gatt->server->send_response(conn_id, trans_id, 0, &response);
|
|
}
|
|
|
|
void RequestExecWriteCallback(int conn_id, int trans_id, bt_bdaddr_t *bda,
|
|
int exec_write) {
|
|
std::string addr(BtAddrString(bda));
|
|
LOG_INFO(LOG_TAG, "%s: connection:%d (%s:trans:%d) exec_write:%d", __func__,
|
|
conn_id, addr.c_str(), trans_id, exec_write);
|
|
|
|
// This 'response' data is unused for ExecWriteResponses.
|
|
// It is only used to pass BlueDroid argument validation.
|
|
btgatt_response_t response = {};
|
|
g_internal->gatt->server->send_response(conn_id, trans_id, 0, &response);
|
|
|
|
if (!exec_write)
|
|
return;
|
|
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
// Communicate the attribute UUID as notification of a write update.
|
|
const bluetooth::UUID::UUID128Bit uuid =
|
|
g_internal->last_write.GetFullBigEndian();
|
|
ssize_t status;
|
|
OSI_NO_INTR(status = write(g_internal->pipefd[kPipeWriteEnd], uuid.data(),
|
|
uuid.size()));
|
|
if (-1 == status)
|
|
LOG_ERROR(LOG_TAG, "%s: write failed: %s", __func__, strerror(errno));
|
|
}
|
|
|
|
void ConnectionCallback(int conn_id, int server_if, int connected,
|
|
bt_bdaddr_t *bda) {
|
|
std::string addr(BtAddrString(bda));
|
|
LOG_INFO(LOG_TAG, "%s: connection:%d server_if:%d connected:%d addr:%s",
|
|
__func__, conn_id, server_if, connected, addr.c_str());
|
|
if (connected == 1) {
|
|
g_internal->connections.insert(conn_id);
|
|
} else if (connected == 0) {
|
|
g_internal->connections.erase(conn_id);
|
|
}
|
|
}
|
|
|
|
void CharacteristicAddedCallback(int status, int server_if, bt_uuid_t *uuid,
|
|
int srvc_handle, int char_handle) {
|
|
LOG_INFO(LOG_TAG,
|
|
"%s: status:%d server_if:%d service_handle:%d char_handle:%d", __func__,
|
|
status, server_if, srvc_handle, char_handle);
|
|
|
|
bluetooth::UUID id(*uuid);
|
|
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
|
|
g_internal->uuid_to_attribute[id] = char_handle;
|
|
g_internal->characteristics[char_handle].uuid = id;
|
|
g_internal->characteristics[char_handle].blob_section = 0;
|
|
|
|
// This terminates an AddCharacteristic.
|
|
g_internal->api_synchronize.notify_one();
|
|
}
|
|
|
|
void DescriptorAddedCallback(int status, int server_if, bt_uuid_t *uuid,
|
|
int srvc_handle, int descr_handle) {
|
|
LOG_INFO(LOG_TAG,
|
|
"%s: status:%d server_if:%d service_handle:%d uuid[0]:%u "
|
|
"descr_handle:%d",
|
|
__func__, status, server_if, srvc_handle, uuid->uu[0], descr_handle);
|
|
}
|
|
|
|
void ServiceStartedCallback(int status, int server_if, int srvc_handle) {
|
|
LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d srvc_handle:%d", __func__,
|
|
status, server_if, srvc_handle);
|
|
|
|
// The UUID provided here is unimportant, and is only used to satisfy
|
|
// BlueDroid.
|
|
// It must be different than any other registered UUID.
|
|
bt_uuid_t client_id = g_internal->service_id.id.uuid;
|
|
++client_id.uu[15];
|
|
|
|
bt_status_t btstat = g_internal->gatt->client->register_client(&client_id);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "%s: Failed to register client", __func__);
|
|
}
|
|
}
|
|
|
|
void RegisterClientCallback(int status, int client_if, bt_uuid_t *app_uuid) {
|
|
LOG_INFO(LOG_TAG, "%s: status:%d client_if:%d uuid[0]:%u", __func__, status,
|
|
client_if, app_uuid->uu[0]);
|
|
g_internal->client_if = client_if;
|
|
|
|
// Setup our advertisement. This has no callback.
|
|
bt_status_t btstat = g_internal->gatt->client->set_adv_data(
|
|
client_if, false, /* beacon, not scan response */
|
|
false, /* name */
|
|
false, /* no txpower */
|
|
2, 2, /* interval */
|
|
0, /* appearance */
|
|
0, nullptr, /* no mfg data */
|
|
0, nullptr, /* no service data */
|
|
0, nullptr /* no service id yet */);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to set advertising data");
|
|
return;
|
|
}
|
|
|
|
// TODO(icoolidge): Deprecated, use multi-adv interface.
|
|
// This calls back to ListenCallback.
|
|
btstat = g_internal->gatt->client->listen(client_if, true);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to start listening");
|
|
}
|
|
}
|
|
|
|
void ListenCallback(int status, int client_if) {
|
|
LOG_INFO(LOG_TAG, "%s: status:%d client_if:%d", __func__, status, client_if);
|
|
// This terminates a Start call.
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
g_internal->api_synchronize.notify_one();
|
|
}
|
|
|
|
void ServiceStoppedCallback(int status, int server_if, int srvc_handle) {
|
|
LOG_INFO(LOG_TAG, "%s: status:%d server_if:%d srvc_handle:%d", __func__,
|
|
status, server_if, srvc_handle);
|
|
// This terminates a Stop call.
|
|
// TODO(icoolidge): make this symmetric with start
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
g_internal->api_synchronize.notify_one();
|
|
}
|
|
|
|
void ScanResultCallback(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) {
|
|
std::string addr(BtAddrString(bda));
|
|
(void)adv_data;
|
|
std::lock_guard<std::mutex> lock(g_internal->lock);
|
|
g_internal->scan_results[addr] = rssi;
|
|
}
|
|
|
|
void ClientConnectCallback(int conn_id, int status, int client_if,
|
|
bt_bdaddr_t *bda) {
|
|
std::string addr(BtAddrString(bda));
|
|
LOG_INFO(LOG_TAG, "%s: conn_id:%d status:%d client_if:%d %s", __func__,
|
|
conn_id, status, client_if, addr.c_str());
|
|
}
|
|
|
|
void ClientDisconnectCallback(int conn_id, int status, int client_if,
|
|
bt_bdaddr_t *bda) {
|
|
std::string addr(BtAddrString(bda));
|
|
LOG_INFO(LOG_TAG, "%s: conn_id:%d status:%d client_if:%d %s", __func__,
|
|
conn_id, status, client_if, addr.c_str());
|
|
}
|
|
|
|
void IndicationSentCallback(UNUSED_ATTR int conn_id,
|
|
UNUSED_ATTR int status) {
|
|
// TODO(icoolidge): what to do
|
|
}
|
|
|
|
void ResponseConfirmationCallback(UNUSED_ATTR int status,
|
|
UNUSED_ATTR int handle) {
|
|
// TODO(icoolidge): what to do
|
|
}
|
|
|
|
const btgatt_server_callbacks_t gatt_server_callbacks = {
|
|
RegisterServerCallback,
|
|
ConnectionCallback,
|
|
ServiceAddedCallback,
|
|
nullptr, /* included_service_added_cb */
|
|
CharacteristicAddedCallback,
|
|
DescriptorAddedCallback,
|
|
ServiceStartedCallback,
|
|
ServiceStoppedCallback,
|
|
nullptr, /* service_deleted_cb */
|
|
RequestReadCallback,
|
|
RequestWriteCallback,
|
|
RequestExecWriteCallback,
|
|
ResponseConfirmationCallback,
|
|
IndicationSentCallback,
|
|
nullptr, /* congestion_cb*/
|
|
nullptr, /* mtu_changed_cb */
|
|
};
|
|
|
|
// TODO(eisenbach): Refactor GATT interface to not require servers
|
|
// to refer to the client interface.
|
|
const btgatt_client_callbacks_t gatt_client_callbacks = {
|
|
RegisterClientCallback,
|
|
ScanResultCallback,
|
|
ClientConnectCallback,
|
|
ClientDisconnectCallback,
|
|
nullptr, /* search_complete_cb; */
|
|
nullptr, /* register_for_notification_cb; */
|
|
nullptr, /* notify_cb; */
|
|
nullptr, /* read_characteristic_cb; */
|
|
nullptr, /* write_characteristic_cb; */
|
|
nullptr, /* read_descriptor_cb; */
|
|
nullptr, /* write_descriptor_cb; */
|
|
nullptr, /* execute_write_cb; */
|
|
nullptr, /* read_remote_rssi_cb; */
|
|
ListenCallback,
|
|
nullptr, /* configure_mtu_cb; */
|
|
nullptr, /* scan_filter_cfg_cb; */
|
|
nullptr, /* scan_filter_param_cb; */
|
|
nullptr, /* scan_filter_status_cb; */
|
|
nullptr, /* multi_adv_enable_cb */
|
|
nullptr, /* multi_adv_update_cb; */
|
|
nullptr, /* multi_adv_data_cb*/
|
|
nullptr, /* multi_adv_disable_cb; */
|
|
nullptr, /* congestion_cb; */
|
|
nullptr, /* batchscan_cfg_storage_cb; */
|
|
nullptr, /* batchscan_enb_disable_cb; */
|
|
nullptr, /* batchscan_reports_cb; */
|
|
nullptr, /* batchscan_threshold_cb; */
|
|
nullptr, /* track_adv_event_cb; */
|
|
nullptr, /* scan_parameter_setup_completed_cb; */
|
|
nullptr, /* get_gatt_db_cb; */
|
|
nullptr, /* services_removed_cb */
|
|
nullptr, /* services_added_cb */
|
|
};
|
|
|
|
const btgatt_callbacks_t gatt_callbacks = {
|
|
/** Set to sizeof(btgatt_callbacks_t) */
|
|
sizeof(btgatt_callbacks_t),
|
|
|
|
/** GATT Client callbacks */
|
|
&gatt_client_callbacks,
|
|
|
|
/** GATT Server callbacks */
|
|
&gatt_server_callbacks};
|
|
|
|
} // namespace
|
|
|
|
namespace bluetooth {
|
|
namespace gatt {
|
|
|
|
int ServerInternals::Initialize() {
|
|
// Get the interface to the GATT profile.
|
|
const bt_interface_t* bt_iface =
|
|
hal::BluetoothInterface::Get()->GetHALInterface();
|
|
gatt = reinterpret_cast<const btgatt_interface_t *>(
|
|
bt_iface->get_profile_interface(BT_PROFILE_GATT_ID));
|
|
if (!gatt) {
|
|
LOG_ERROR(LOG_TAG, "Error getting GATT interface");
|
|
return -1;
|
|
}
|
|
|
|
bt_status_t btstat = gatt->init(&gatt_callbacks);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to initialize gatt interface");
|
|
return -1;
|
|
}
|
|
|
|
int status = pipe(pipefd);
|
|
if (status == -1) {
|
|
LOG_ERROR(LOG_TAG, "pipe creation failed: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bt_status_t ServerInternals::AddCharacteristic(
|
|
const UUID& uuid,
|
|
int properties,
|
|
int permissions) {
|
|
bt_uuid_t c_uuid = uuid.GetBlueDroid();
|
|
return gatt->server->add_characteristic(
|
|
server_if, service_handle, &c_uuid, properties, permissions);
|
|
}
|
|
|
|
ServerInternals::ServerInternals()
|
|
: gatt(nullptr),
|
|
server_if(0),
|
|
client_if(0),
|
|
service_handle(0),
|
|
pipefd{INVALID_FD, INVALID_FD} {}
|
|
|
|
ServerInternals::~ServerInternals() {
|
|
if (pipefd[0] != INVALID_FD)
|
|
close(pipefd[0]);
|
|
if (pipefd[1] != INVALID_FD)
|
|
close(pipefd[1]);
|
|
|
|
gatt->server->delete_service(server_if, service_handle);
|
|
gatt->server->unregister_server(server_if);
|
|
gatt->client->unregister_client(client_if);
|
|
}
|
|
|
|
Server::Server() : internal_(nullptr) {}
|
|
|
|
Server::~Server() {}
|
|
|
|
bool Server::Initialize(const UUID& service_id, int* gatt_pipe) {
|
|
internal_.reset(new ServerInternals);
|
|
if (!internal_) {
|
|
LOG_ERROR(LOG_TAG, "Error creating internals");
|
|
return false;
|
|
}
|
|
g_internal = internal_.get();
|
|
|
|
std::unique_lock<std::mutex> lock(internal_->lock);
|
|
int status = internal_->Initialize();
|
|
if (status) {
|
|
LOG_ERROR(LOG_TAG, "Error initializing internals");
|
|
return false;
|
|
}
|
|
|
|
bt_uuid_t uuid = service_id.GetBlueDroid();
|
|
|
|
bt_status_t btstat = internal_->gatt->server->register_server(&uuid);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to register server");
|
|
return false;
|
|
}
|
|
|
|
internal_->api_synchronize.wait(lock);
|
|
// TODO(icoolidge): Better error handling.
|
|
if (internal_->server_if == 0) {
|
|
LOG_ERROR(LOG_TAG, "Initialization of server failed");
|
|
return false;
|
|
}
|
|
|
|
*gatt_pipe = internal_->pipefd[kPipeReadEnd];
|
|
LOG_INFO(LOG_TAG, "Server Initialize succeeded");
|
|
return true;
|
|
}
|
|
|
|
bool Server::SetAdvertisement(const std::vector<UUID>& ids,
|
|
const std::vector<uint8_t>& service_data,
|
|
const std::vector<uint8_t>& manufacturer_data,
|
|
bool transmit_name) {
|
|
std::vector<uint8_t> id_data;
|
|
auto mutable_manufacturer_data = manufacturer_data;
|
|
auto mutable_service_data = service_data;
|
|
|
|
for (const UUID &id : ids) {
|
|
const auto le_id = id.GetFullLittleEndian();
|
|
id_data.insert(id_data.end(), le_id.begin(), le_id.end());
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(internal_->lock);
|
|
|
|
// Setup our advertisement. This has no callback.
|
|
bt_status_t btstat = internal_->gatt->client->set_adv_data(
|
|
internal_->client_if, false, /* beacon, not scan response */
|
|
transmit_name, /* name */
|
|
false, /* no txpower */
|
|
2, 2, /* interval */
|
|
0, /* appearance */
|
|
mutable_manufacturer_data.size(),
|
|
reinterpret_cast<char *>(mutable_manufacturer_data.data()),
|
|
mutable_service_data.size(),
|
|
reinterpret_cast<char *>(mutable_service_data.data()), id_data.size(),
|
|
reinterpret_cast<char *>(id_data.data()));
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to set advertising data");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Server::SetScanResponse(const std::vector<UUID>& ids,
|
|
const std::vector<uint8_t>& service_data,
|
|
const std::vector<uint8_t>& manufacturer_data,
|
|
bool transmit_name) {
|
|
std::vector<uint8_t> id_data;
|
|
auto mutable_manufacturer_data = manufacturer_data;
|
|
auto mutable_service_data = service_data;
|
|
|
|
for (const UUID &id : ids) {
|
|
const auto le_id = id.GetFullLittleEndian();
|
|
id_data.insert(id_data.end(), le_id.begin(), le_id.end());
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(internal_->lock);
|
|
|
|
// Setup our advertisement. This has no callback.
|
|
bt_status_t btstat = internal_->gatt->client->set_adv_data(
|
|
internal_->client_if, true, /* scan response */
|
|
transmit_name, /* name */
|
|
false, /* no txpower */
|
|
2, 2, /* interval */
|
|
0, /* appearance */
|
|
mutable_manufacturer_data.size(),
|
|
reinterpret_cast<char *>(mutable_manufacturer_data.data()),
|
|
mutable_service_data.size(),
|
|
reinterpret_cast<char *>(mutable_service_data.data()), id_data.size(),
|
|
reinterpret_cast<char *>(id_data.data()));
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to set scan response data");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Server::AddCharacteristic(
|
|
const UUID &id, int properties, int permissions) {
|
|
std::unique_lock<std::mutex> lock(internal_->lock);
|
|
bt_status_t btstat = internal_->AddCharacteristic(
|
|
id, properties, permissions);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to add characteristic to service: 0x%04x",
|
|
internal_->service_handle);
|
|
return false;
|
|
}
|
|
internal_->api_synchronize.wait(lock);
|
|
const int handle = internal_->uuid_to_attribute[id];
|
|
internal_->characteristics[handle].notify = properties & kPropertyNotify;
|
|
return true;
|
|
}
|
|
|
|
bool Server::AddBlob(const UUID &id, const UUID &control_id, int properties,
|
|
int permissions) {
|
|
std::unique_lock<std::mutex> lock(internal_->lock);
|
|
|
|
// First, add the primary attribute (characteristic value)
|
|
bt_status_t btstat = internal_->AddCharacteristic(
|
|
id, properties, permissions);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to set scan response data");
|
|
return false;
|
|
}
|
|
|
|
internal_->api_synchronize.wait(lock);
|
|
|
|
// Next, add the secondary attribute (blob control).
|
|
// Control attributes have fixed permissions/properties.
|
|
btstat = internal_->AddCharacteristic(
|
|
control_id,
|
|
kPropertyRead | kPropertyWrite,
|
|
kPermissionRead | kPermissionWrite);
|
|
internal_->api_synchronize.wait(lock);
|
|
|
|
// Finally, associate the control attribute with the value attribute.
|
|
// Also, initialize the control attribute to a readable zero.
|
|
const int control_attribute = internal_->uuid_to_attribute[control_id];
|
|
const int blob_attribute = internal_->uuid_to_attribute[id];
|
|
internal_->controlled_blobs[control_attribute] = blob_attribute;
|
|
internal_->characteristics[blob_attribute].notify =
|
|
properties & kPropertyNotify;
|
|
|
|
Characteristic &ctrl = internal_->characteristics[control_attribute];
|
|
ctrl.next_blob.clear();
|
|
ctrl.next_blob.push_back(0);
|
|
ctrl.next_blob_pending = true;
|
|
ctrl.blob_section = 0;
|
|
ctrl.notify = false;
|
|
return true;
|
|
}
|
|
|
|
bool Server::Start() {
|
|
std::unique_lock<std::mutex> lock(internal_->lock);
|
|
bt_status_t btstat = internal_->gatt->server->start_service(
|
|
internal_->server_if, internal_->service_handle, GATT_TRANSPORT_LE);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to start service with handle: 0x%04x",
|
|
internal_->service_handle);
|
|
return false;
|
|
}
|
|
internal_->api_synchronize.wait(lock);
|
|
return true;
|
|
}
|
|
|
|
bool Server::Stop() {
|
|
std::unique_lock<std::mutex> lock(internal_->lock);
|
|
bt_status_t btstat = internal_->gatt->server->stop_service(
|
|
internal_->server_if, internal_->service_handle);
|
|
if (btstat != BT_STATUS_SUCCESS) {
|
|
LOG_ERROR(LOG_TAG, "Failed to stop service with handle: 0x%04x",
|
|
internal_->service_handle);
|
|
return false;
|
|
}
|
|
internal_->api_synchronize.wait(lock);
|
|
return true;
|
|
}
|
|
|
|
bool Server::ScanEnable() {
|
|
bt_status_t btstat = internal_->gatt->client->scan(true);
|
|
if (btstat) {
|
|
LOG_ERROR(LOG_TAG, "Enable scan failed: %d", btstat);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Server::ScanDisable() {
|
|
bt_status_t btstat = internal_->gatt->client->scan(false);
|
|
if (btstat) {
|
|
LOG_ERROR(LOG_TAG, "Disable scan failed: %d", btstat);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Server::GetScanResults(ScanResults *results) {
|
|
std::lock_guard<std::mutex> lock(internal_->lock);
|
|
*results = internal_->scan_results;
|
|
return true;
|
|
}
|
|
|
|
bool Server::SetCharacteristicValue(const UUID &id,
|
|
const std::vector<uint8_t> &value) {
|
|
std::lock_guard<std::mutex> lock(internal_->lock);
|
|
const int attribute_id = internal_->uuid_to_attribute[id];
|
|
Characteristic &ch = internal_->characteristics[attribute_id];
|
|
ch.next_blob = value;
|
|
ch.next_blob_pending = true;
|
|
|
|
if (!ch.notify)
|
|
return true;
|
|
|
|
for (auto connection : internal_->connections) {
|
|
char dummy = 0;
|
|
internal_->gatt->server->send_indication(internal_->server_if,
|
|
attribute_id,
|
|
connection,
|
|
sizeof(dummy),
|
|
true,
|
|
&dummy);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Server::GetCharacteristicValue(const UUID &id, std::vector<uint8_t> *value) {
|
|
std::lock_guard<std::mutex> lock(internal_->lock);
|
|
const int attribute_id = internal_->uuid_to_attribute[id];
|
|
*value = internal_->characteristics[attribute_id].blob;
|
|
return true;
|
|
}
|
|
|
|
} // namespace gatt
|
|
} // namespace bluetooth
|