service: Add new GattServer abstraction
Added bluetooth::GattServer and bluetooth::GattServerFactory,
which conform the BluetoothClientInterface and
BluetoothClientInterfaceFactory interfaces. A GattServer represents
a per-application server instance that was registered with the stack
(a GATT server API "client").
The new code was added to the gatt_server.h|.cpp files, replacing
the old code that was added for cast. The old code has been moved to
the gatt_server_old.h|.cpp files.
Bug: 24245347
Change-Id: I3ca2eb53ab8320c9dba656859a4a89683607bb22
diff --git a/service/gatt_server.cpp b/service/gatt_server.cpp
index b5efebe..8c6c1f7 100644
--- a/service/gatt_server.cpp
+++ b/service/gatt_server.cpp
@@ -14,767 +14,95 @@
// limitations under the License.
//
-#define LOG_TAG "bt_gatts"
+#include "service/gatt_server.h"
-#include "gatt_server.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"
-#include "service/uuid.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
+using std::lock_guard;
+using std::mutex;
namespace bluetooth {
-namespace gatt {
-struct Characteristic {
- UUID uuid;
- int blob_section;
- std::vector<uint8_t> blob;
+// GattServer implementation
+// ========================================================
- // 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);
+GattServer::GattServer(const UUID& uuid, int server_if)
+ : app_identifier_(uuid),
+ server_if_(server_if) {
}
-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();
+GattServer::~GattServer() {
+ hal::BluetoothGattInterface::Get()->
+ GetServerHALInterface()->unregister_server(server_if_);
}
-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);
+const UUID& GattServer::GetAppIdentifier() const {
+ return app_identifier_;
+}
- bluetooth::gatt::Characteristic &ch = g_internal->characteristics[attr_handle];
+int GattServer::GetClientId() const {
+ return server_if_;
+}
- // 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;
+// GattServerFactory implementation
+// ========================================================
+
+GattServerFactory::GattServerFactory() {
+ hal::BluetoothGattInterface::Get()->AddServerObserver(this);
+}
+
+GattServerFactory::~GattServerFactory() {
+ hal::BluetoothGattInterface::Get()->RemoveServerObserver(this);
+}
+
+bool GattServerFactory::RegisterClient(const UUID& uuid,
+ const RegisterCallback& callback) {
+ VLOG(1) << __func__ << " - UUID: " << uuid.ToString();
+ lock_guard<mutex> lock(pending_calls_lock_);
+
+ if (pending_calls_.find(uuid) != pending_calls_.end()) {
+ LOG(ERROR) << "GATT-server client with given UUID already being registered "
+ << " - UUID: " << uuid.ToString();
+ return 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);
+ const btgatt_server_interface_t* hal_iface =
+ hal::BluetoothGattInterface::Get()->GetServerHALInterface();
+ bt_uuid_t app_uuid = uuid.GetBlueDroid();
- 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);
+ if (hal_iface->register_server(&app_uuid) != BT_STATUS_SUCCESS)
+ return false;
- btgatt_response_t response;
- response.attr_value.len = 0;
+ pending_calls_[uuid] = callback;
- 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);
+ return true;
}
-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);
+void GattServerFactory::RegisterServerCallback(
+ hal::BluetoothGattInterface* /* gatt_iface */,
+ int status, int server_if,
+ const bt_uuid_t& app_uuid) {
+ UUID uuid(app_uuid);
- std::lock_guard<std::mutex> lock(g_internal->lock);
+ VLOG(1) << __func__ << " - UUID: " << uuid.ToString();
+ lock_guard<mutex> lock(pending_calls_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();
- int 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();
- int 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");
+ auto iter = pending_calls_.find(uuid);
+ if (iter == pending_calls_.end()) {
+ VLOG(1) << "Ignoring callback for unknown app_id: " << uuid.ToString();
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, /* search_result_cb; */
- nullptr, /* get_characteristic_cb; */
- nullptr, /* get_descriptor_cb; */
- nullptr, /* get_included_service_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; */
-};
-
-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;
+ // No need to construct a server if the call wasn't successful.
+ std::unique_ptr<GattServer> server;
+ BLEStatus result = BLE_STATUS_FAILURE;
+ if (status == BT_STATUS_SUCCESS) {
+ server.reset(new GattServer(uuid, server_if));
+ result = BLE_STATUS_SUCCESS;
}
- bt_status_t btstat = gatt->init(&gatt_callbacks);
- if (btstat != BT_STATUS_SUCCESS) {
- LOG_ERROR(LOG_TAG, "Failed to initialize gatt interface");
- return -1;
- }
+ // Notify the result via the result callback.
+ iter->second(result, uuid, std::move(server));
- int status = pipe(pipefd);
- if (status == -1) {
- LOG_ERROR(LOG_TAG, "pipe creation failed: %s", strerror(errno));
- return -1;
- }
-
- return 0;
+ pending_calls_.erase(iter);
}
-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