123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- //
- // Copyright 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.
- //
- #include "service/gatt_server.h"
- #include <base/logging.h>
- #include "service/logging_helpers.h"
- #include "stack/include/bt_types.h"
- using std::lock_guard;
- using std::mutex;
- namespace bluetooth {
- // GattServer implementation
- // ========================================================
- GattServer::GattServer(const Uuid& uuid, int server_id)
- : app_identifier_(uuid), server_id_(server_id), delegate_(nullptr) {}
- GattServer::~GattServer() {
- // Automatically unregister the server.
- VLOG(1) << "GattServer unregistering: " << server_id_;
- // Unregister as observer so we no longer receive any callbacks.
- hal::BluetoothGattInterface::Get()->RemoveServerObserver(this);
- // Unregister this server, stop all services, and ignore the result.
- // TODO(armansito): stop and remove all services here? unregister_server
- // should really take care of that.
- hal::BluetoothGattInterface::Get()
- ->GetServerHALInterface()
- ->unregister_server(server_id_);
- }
- void GattServer::SetDelegate(Delegate* delegate) {
- lock_guard<mutex> lock(mutex_);
- delegate_ = delegate;
- }
- const Uuid& GattServer::GetAppIdentifier() const { return app_identifier_; }
- int GattServer::GetInstanceId() const { return server_id_; }
- bool GattServer::AddService(const bluetooth::Service& service,
- const ResultCallback& callback) {
- VLOG(1) << __func__ << " server_id: " << server_id_;
- lock_guard<mutex> lock(mutex_);
- if (!callback) {
- LOG(ERROR) << "|callback| cannot be NULL";
- return false;
- }
- std::vector<btgatt_db_element_t> svc;
- svc.push_back({.type = (service.primary() ? BTGATT_DB_PRIMARY_SERVICE
- : BTGATT_DB_SECONDARY_SERVICE),
- .uuid = service.uuid()});
- for (const auto& characteristic : service.characteristics()) {
- svc.push_back({.type = BTGATT_DB_CHARACTERISTIC,
- .uuid = characteristic.uuid(),
- .properties = characteristic.properties(),
- .permissions = characteristic.permissions()});
- for (const auto& descriptor : characteristic.descriptors())
- svc.push_back({.type = BTGATT_DB_DESCRIPTOR,
- .uuid = descriptor.uuid(),
- .permissions = descriptor.permissions()});
- }
- for (const auto& incl_svc : service.included_services())
- svc.push_back({.type = BTGATT_DB_INCLUDED_SERVICE,
- .attribute_handle = incl_svc.handle()});
- pending_end_decl_cb_ = callback;
- bt_status_t status =
- hal::BluetoothGattInterface::Get()->GetServerHALInterface()->add_service(
- server_id_, svc);
- if (status != BT_STATUS_SUCCESS) {
- LOG(ERROR) << "Failed to initiate call to populate GATT service";
- CleanUpPendingData();
- return false;
- }
- return true;
- }
- bool GattServer::SendResponse(const std::string& device_address, int request_id,
- GATTError error, int offset,
- const std::vector<uint8_t>& value) {
- VLOG(1) << __func__ << " - server_id: " << server_id_
- << " device_address: " << device_address
- << " request_id: " << request_id << " error: " << error
- << " offset: " << offset;
- lock_guard<mutex> lock(mutex_);
- RawAddress addr;
- if (!RawAddress::FromString(device_address, addr)) {
- LOG(ERROR) << "Invalid device address given: " << device_address;
- return false;
- }
- if (value.size() + offset > BTGATT_MAX_ATTR_LEN) {
- LOG(ERROR) << "Value is too large";
- return false;
- }
- // Find the correct connection ID for |device_address| and |request_id|.
- auto iter = conn_addr_map_.find(device_address);
- if (iter == conn_addr_map_.end()) {
- LOG(ERROR) << "No known connections for device address: " << device_address;
- return false;
- }
- std::shared_ptr<Connection> connection;
- for (const auto& tmp : iter->second) {
- if (tmp->request_id_to_handle.find(request_id) ==
- tmp->request_id_to_handle.end())
- continue;
- connection = tmp;
- }
- if (!connection) {
- LOG(ERROR) << "Pending request with ID " << request_id
- << " not found for device with BD_ADDR: " << device_address;
- return false;
- }
- btgatt_response_t response;
- memset(&response, 0, sizeof(response));
- // We keep -1 as the handle for "Execute Write Request". In that case,
- // there is no need to populate the response data. Just send zeros back.
- int handle = connection->request_id_to_handle[request_id];
- response.handle = handle;
- response.attr_value.handle = handle;
- if (handle != -1) {
- memcpy(response.attr_value.value, value.data(), value.size());
- response.attr_value.offset = offset;
- response.attr_value.len = value.size();
- }
- bt_status_t result =
- hal::BluetoothGattInterface::Get()
- ->GetServerHALInterface()
- ->send_response(connection->conn_id, request_id, error, response);
- if (result != BT_STATUS_SUCCESS) {
- LOG(ERROR) << "Failed to initiate call to send GATT response";
- return false;
- }
- connection->request_id_to_handle.erase(request_id);
- return true;
- }
- bool GattServer::SendNotification(const std::string& device_address,
- const uint16_t handle, bool confirm,
- const std::vector<uint8_t>& value,
- const GattCallback& callback) {
- VLOG(1) << " - server_id: " << server_id_
- << " device_address: " << device_address << " confirm: " << confirm;
- lock_guard<mutex> lock(mutex_);
- RawAddress addr;
- if (!RawAddress::FromString(device_address, addr)) {
- LOG(ERROR) << "Invalid device address given: " << device_address;
- return false;
- }
- // Get the connection IDs for which we will send this notification.
- auto conn_iter = conn_addr_map_.find(device_address);
- if (conn_iter == conn_addr_map_.end()) {
- LOG(ERROR) << "No known connections for device with address: "
- << device_address;
- return false;
- }
- std::shared_ptr<PendingIndication> pending_ind(
- new PendingIndication(callback));
- // Send the notification/indication on all matching connections.
- int send_count = 0;
- for (const auto& conn : conn_iter->second) {
- // Make sure that one isn't already pending for this connection.
- if (pending_indications_.find(conn->conn_id) !=
- pending_indications_.end()) {
- VLOG(1) << "A" << (confirm ? "n indication" : " notification")
- << " is already pending for connection: " << conn->conn_id;
- continue;
- }
- // The HAL API takes char* rather const char* for |value|, so we have to
- // cast away the const.
- // TODO(armansito): Make HAL accept const char*.
- bt_status_t status = hal::BluetoothGattInterface::Get()
- ->GetServerHALInterface()
- ->send_indication(server_id_, handle,
- conn->conn_id, confirm, value);
- // Increment the send count if this was successful. We don't immediately
- // fail if the HAL returned an error. It's better to report success as long
- // as we sent out at least one notification to this device as
- // multi-transport GATT connections from the same BD_ADDR will be rare
- // enough already.
- if (status != BT_STATUS_SUCCESS) continue;
- send_count++;
- pending_indications_[conn->conn_id] = pending_ind;
- }
- if (send_count == 0) {
- LOG(ERROR) << "Failed to send notifications/indications to device: "
- << device_address;
- return false;
- }
- return true;
- }
- void GattServer::ConnectionCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int server_id,
- int connected, const RawAddress& bda) {
- lock_guard<mutex> lock(mutex_);
- if (server_id != server_id_) return;
- std::string device_address = BtAddrString(&bda);
- VLOG(1) << __func__ << " conn_id: " << conn_id << " connected: " << connected
- << " BD_ADDR: " << device_address;
- if (!connected) {
- // Erase the entry if we were connected to it.
- VLOG(1) << "No longer connected: " << device_address;
- conn_id_map_.erase(conn_id);
- auto iter = conn_addr_map_.find(device_address);
- if (iter == conn_addr_map_.end()) return;
- // Remove the appropriate connection objects in the address.
- for (auto conn_iter = iter->second.begin(); conn_iter != iter->second.end();
- ++conn_iter) {
- if ((*conn_iter)->conn_id != conn_id) continue;
- iter->second.erase(conn_iter);
- break;
- }
- if (delegate_)
- delegate_->OnConnectionStateChanged(this, device_address, false);
- return;
- }
- if (conn_id_map_.find(conn_id) != conn_id_map_.end()) {
- LOG(WARNING) << "Connection entry already exists; "
- << "ignoring ConnectionCallback";
- return;
- }
- LOG(INFO) << "Added connection entry for conn_id: " << conn_id
- << " device address: " << device_address;
- std::shared_ptr<Connection> connection(new Connection(conn_id, bda));
- conn_id_map_[conn_id] = connection;
- conn_addr_map_[device_address].push_back(connection);
- if (delegate_)
- delegate_->OnConnectionStateChanged(this, device_address, true);
- }
- void GattServer::ServiceAddedCallback(hal::BluetoothGattInterface* gatt_iface,
- int status, int server_id,
- std::vector<btgatt_db_element_t> svc) {
- lock_guard<mutex> lock(mutex_);
- if (server_id != server_id_) return;
- VLOG(1) << __func__ << " - status: " << status << " server_id: " << server_id
- << " first handle: " << svc[0].attribute_handle
- << " service Uuid: " << Uuid(svc[0].uuid).ToString()
- << " count: " << svc.size();
- Service service(svc[0].attribute_handle, true, Uuid(svc[0].uuid), {}, {});
- for (size_t i = 1; i < svc.size(); i++) {
- const btgatt_db_element_t& curr = svc[i];
- VLOG(1) << " - processing item no: " << i
- << " handle: " << curr.attribute_handle;
- if (curr.type == BTGATT_DB_CHARACTERISTIC) {
- service.characteristics().push_back({curr.attribute_handle,
- Uuid(curr.uuid),
- curr.properties,
- curr.permissions,
- {}});
- } else if (curr.type == BTGATT_DB_DESCRIPTOR) {
- service.characteristics().back().descriptors().push_back(
- {curr.attribute_handle, Uuid(curr.uuid), curr.permissions});
- } else if (svc[i].type == BTGATT_DB_INCLUDED_SERVICE) {
- }
- }
- pending_end_decl_cb_((bluetooth::BLEStatus)status, service);
- CleanUpPendingData();
- }
- void GattServer::ServiceStoppedCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int /* status */,
- int /* server_id */, int /* service_handle */) {
- // TODO(armansito): Support stopping a service.
- }
- void GattServer::RequestReadCharacteristicCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int trans_id,
- const RawAddress& bda, int attribute_handle, int offset, bool is_long) {
- lock_guard<mutex> lock(mutex_);
- // Check to see if we know about this connection. Otherwise ignore the
- // request.
- auto conn = GetConnection(conn_id, bda, trans_id);
- if (!conn) return;
- std::string device_address = BtAddrString(&bda);
- VLOG(1) << __func__ << " - conn_id: " << conn_id << " trans_id: " << trans_id
- << " BD_ADDR: " << device_address
- << " attribute_handle: " << attribute_handle << " offset: " << offset
- << " is_long: " << is_long;
- conn->request_id_to_handle[trans_id] = attribute_handle;
- // If there is no delegate then there is nobody to handle request. The request
- // will eventually timeout and we should get a connection update that
- // terminates the connection.
- if (!delegate_) {
- // TODO(armansito): Require a delegate at server registration so that this
- // is never possible.
- LOG(WARNING) << "No delegate was assigned to GattServer. Incoming request "
- << "will time out.";
- return;
- }
- delegate_->OnCharacteristicReadRequest(this, device_address, trans_id, offset,
- is_long, attribute_handle);
- }
- void GattServer::RequestReadDescriptorCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int trans_id,
- const RawAddress& bda, int attribute_handle, int offset, bool is_long) {
- lock_guard<mutex> lock(mutex_);
- // Check to see if we know about this connection. Otherwise ignore the
- // request.
- auto conn = GetConnection(conn_id, bda, trans_id);
- if (!conn) return;
- std::string device_address = BtAddrString(&bda);
- VLOG(1) << __func__ << " - conn_id: " << conn_id << " trans_id: " << trans_id
- << " BD_ADDR: " << device_address
- << " attribute_handle: " << attribute_handle << " offset: " << offset
- << " is_long: " << is_long;
- conn->request_id_to_handle[trans_id] = attribute_handle;
- // If there is no delegate then there is nobody to handle request. The request
- // will eventually timeout and we should get a connection update that
- // terminates the connection.
- if (!delegate_) {
- // TODO(armansito): Require a delegate at server registration so that this
- // is never possible.
- LOG(WARNING) << "No delegate was assigned to GattServer. Incoming request "
- << "will time out.";
- return;
- }
- delegate_->OnDescriptorReadRequest(this, device_address, trans_id, offset,
- is_long, attribute_handle);
- }
- void GattServer::RequestWriteCharacteristicCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int trans_id,
- const RawAddress& bda, int attr_handle, int offset, bool need_rsp,
- bool is_prep, std::vector<uint8_t> value) {
- lock_guard<mutex> lock(mutex_);
- // Check to see if we know about this connection. Otherwise ignore the
- // request.
- auto conn = GetConnection(conn_id, bda, trans_id);
- if (!conn) return;
- std::string device_address = BtAddrString(&bda);
- VLOG(1) << __func__ << " - conn_id: " << conn_id << " trans_id: " << trans_id
- << " BD_ADDR: " << device_address << " attr_handle: " << attr_handle
- << " offset: " << offset << " length: " << value.size()
- << " need_rsp: " << need_rsp << " is_prep: " << is_prep;
- // Store the request ID only if this is not a write-without-response. If
- // another request occurs after this with the same request ID, then we'll
- // simply process it normally, though that shouldn't ever happen.
- if (need_rsp) conn->request_id_to_handle[trans_id] = attr_handle;
- // If there is no delegate then there is nobody to handle request. The request
- // will eventually timeout and we should get a connection update that
- // terminates the connection.
- if (!delegate_) {
- // TODO(armansito): Require a delegate at server registration so that this
- // is never possible.
- LOG(WARNING) << "No delegate was assigned to GattServer. Incoming request "
- << "will time out.";
- return;
- }
- delegate_->OnCharacteristicWriteRequest(this, device_address, trans_id,
- offset, is_prep, need_rsp,
- std::move(value), attr_handle);
- }
- void GattServer::RequestWriteDescriptorCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int trans_id,
- const RawAddress& bda, int attr_handle, int offset, bool need_rsp,
- bool is_prep, std::vector<uint8_t> value) {
- lock_guard<mutex> lock(mutex_);
- // Check to see if we know about this connection. Otherwise ignore the
- // request.
- auto conn = GetConnection(conn_id, bda, trans_id);
- if (!conn) return;
- std::string device_address = BtAddrString(&bda);
- VLOG(1) << __func__ << " - conn_id: " << conn_id << " trans_id: " << trans_id
- << " BD_ADDR: " << device_address << " attr_handle: " << attr_handle
- << " offset: " << offset << " length: " << value.size()
- << " need_rsp: " << need_rsp << " is_prep: " << is_prep;
- // Store the request ID only if this is not a write-without-response. If
- // another request occurs after this with the same request ID, then we'll
- // simply process it normally, though that shouldn't ever happen.
- if (need_rsp) conn->request_id_to_handle[trans_id] = attr_handle;
- // If there is no delegate then there is nobody to handle request. The request
- // will eventually timeout and we should get a connection update that
- // terminates the connection.
- if (!delegate_) {
- // TODO(armansito): Require a delegate at server registration so that this
- // is never possible.
- LOG(WARNING) << "No delegate was assigned to GattServer. Incoming request "
- << "will time out.";
- return;
- }
- delegate_->OnDescriptorWriteRequest(this, device_address, trans_id, offset,
- is_prep, need_rsp, std::move(value),
- attr_handle);
- }
- void GattServer::RequestExecWriteCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int trans_id,
- const RawAddress& bda, int exec_write) {
- lock_guard<mutex> lock(mutex_);
- // Check to see if we know about this connection. Otherwise ignore the
- // request.
- auto conn = GetConnection(conn_id, bda, trans_id);
- if (!conn) return;
- std::string device_address = BtAddrString(&bda);
- VLOG(1) << __func__ << " - conn_id: " << conn_id << " trans_id: " << trans_id
- << " BD_ADDR: " << device_address << " exec_write: " << exec_write;
- // Just store a dummy invalid handle as this request doesn't apply to a
- // specific handle.
- conn->request_id_to_handle[trans_id] = -1;
- // If there is no delegate then there is nobody to handle request. The request
- // will eventually timeout and we should get a connection update that
- // terminates the connection.
- if (!delegate_) {
- // TODO(armansito): Require a delegate at server registration so that this
- // is never possible.
- LOG(WARNING) << "No delegate was assigned to GattServer. Incoming request "
- << "will time out.";
- return;
- }
- delegate_->OnExecuteWriteRequest(this, device_address, trans_id, exec_write);
- }
- void GattServer::IndicationSentCallback(
- hal::BluetoothGattInterface* /* gatt_iface */, int conn_id, int status) {
- VLOG(1) << __func__ << " conn_id: " << conn_id << " status: " << status;
- lock_guard<mutex> lock(mutex_);
- const auto& pending_ind_iter = pending_indications_.find(conn_id);
- if (pending_ind_iter == pending_indications_.end()) {
- VLOG(1) << "Unknown connection: " << conn_id;
- return;
- }
- std::shared_ptr<PendingIndication> pending_ind = pending_ind_iter->second;
- pending_indications_.erase(pending_ind_iter);
- if (status == BT_STATUS_SUCCESS) pending_ind->has_success = true;
- // Invoke it if this was the last reference to the confirmation callback.
- if (pending_ind.unique() && pending_ind->callback) {
- pending_ind->callback(pending_ind->has_success
- ? GATT_ERROR_NONE
- : static_cast<GATTError>(status));
- }
- }
- void GattServer::CleanUpPendingData() {
- pending_end_decl_cb_ = ResultCallback();
- }
- std::shared_ptr<GattServer::Connection> GattServer::GetConnection(
- int conn_id, const RawAddress& bda, int request_id) {
- auto iter = conn_id_map_.find(conn_id);
- if (iter == conn_id_map_.end()) {
- VLOG(1) << "Connection doesn't belong to this server";
- return nullptr;
- }
- auto conn = iter->second;
- if (conn->bdaddr != bda) {
- LOG(WARNING) << "BD_ADDR: " << BtAddrString(&bda) << " doesn't match "
- << "connection ID: " << conn_id;
- return nullptr;
- }
- if (conn->request_id_to_handle.find(request_id) !=
- conn->request_id_to_handle.end()) {
- VLOG(1) << "Request with ID: " << request_id << " already exists for "
- << " connection: " << conn_id;
- return nullptr;
- }
- return conn;
- }
- // GattServerFactory implementation
- // ========================================================
- GattServerFactory::GattServerFactory() {
- hal::BluetoothGattInterface::Get()->AddServerObserver(this);
- }
- GattServerFactory::~GattServerFactory() {
- hal::BluetoothGattInterface::Get()->RemoveServerObserver(this);
- }
- bool GattServerFactory::RegisterInstance(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 btgatt_server_interface_t* hal_iface =
- hal::BluetoothGattInterface::Get()->GetServerHALInterface();
- if (hal_iface->register_server(uuid) != BT_STATUS_SUCCESS) return false;
- pending_calls_[uuid] = callback;
- return true;
- }
- void GattServerFactory::RegisterServerCallback(
- hal::BluetoothGattInterface* gatt_iface, int status, int server_id,
- const Uuid& app_uuid) {
- Uuid uuid(app_uuid);
- VLOG(1) << __func__ << " - Uuid: " << uuid.ToString();
- lock_guard<mutex> lock(pending_calls_lock_);
- auto iter = pending_calls_.find(uuid);
- if (iter == pending_calls_.end()) {
- VLOG(1) << "Ignoring callback for unknown app_id: " << uuid.ToString();
- return;
- }
- // 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_id));
- gatt_iface->AddServerObserver(server.get());
- result = BLE_STATUS_SUCCESS;
- }
- // Notify the result via the result callback.
- iter->second(result, uuid, std::move(server));
- pending_calls_.erase(iter);
- }
- } // namespace bluetooth
|