// // 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. // #define LOG_TAG "bt_gatts" #include "gatt_server_old.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "service/hal/bluetooth_interface.h" #include "service/logging_helpers.h" #include "osi/include/log.h" #include "osi/include/osi.h" namespace { const size_t kMaxGattAttributeSize = 512; std::vector pending_svc_decl; std::unordered_set blob_index; // 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 blob; // Support synchronized blob updates by latching under mutex. std::vector next_blob; bool next_blob_pending; bool notify; }; struct ServerInternals { ServerInternals(); ~ServerInternals(); int Initialize(); bt_status_t AddCharacteristic(const Uuid& uuid, uint8_t properties, uint16_t permissions); // This maps API attribute Uuids to BlueDroid handles. std::map uuid_to_attribute; // The attribute cache, indexed by BlueDroid handles. std::unordered_map characteristics; // Associate a control attribute with its value attribute. std::unordered_map controlled_blobs; ScanResults scan_results; Uuid last_write; const btgatt_interface_t* gatt; int server_if; int client_if; int service_handle; std::set 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, const bluetooth::Uuid& app_uuid) { LOG_INFO("%s: status:%d server_if:%d app_uuid:%p", __func__, status, server_if, &app_uuid); g_internal->server_if = server_if; pending_svc_decl.push_back({ .uuid = app_uuid, .type = BTGATT_DB_PRIMARY_SERVICE, }); } void ServiceAddedCallback(int status, int server_if, std::vector service) { LOG_INFO("%s: status:%d server_if:%d count:%zu svc_handle:%d", __func__, status, server_if, service.size(), service[0].attribute_handle); std::lock_guard lock(g_internal->lock); g_internal->server_if = server_if; g_internal->service_handle = service[0].attribute_handle; uint16_t prev_char_handle = 0; uint16_t prev_char_properties = 0; for (size_t i = 1; i < service.size(); i++) { const btgatt_db_element_t& el = service[i]; if (el.type == BTGATT_DB_DESCRIPTOR) { LOG_INFO("%s: descr_handle:%d", __func__, el.attribute_handle); } else if (el.type == BTGATT_DB_CHARACTERISTIC) { bluetooth::Uuid id(el.uuid); uint16_t char_handle = el.attribute_handle; LOG_INFO("%s: char_handle:%d", __func__, char_handle); g_internal->uuid_to_attribute[id] = char_handle; g_internal->characteristics[char_handle].uuid = id; g_internal->characteristics[char_handle].blob_section = 0; // If the added characteristic is blob if (blob_index.find(i) != blob_index.end()) { // Finally, associate the control attribute with the value attribute. // Also, initialize the control attribute to a readable zero. const uint16_t control_attribute = char_handle; const uint16_t blob_attribute = prev_char_handle; g_internal->controlled_blobs[control_attribute] = blob_attribute; g_internal->characteristics[blob_attribute].notify = prev_char_properties & bluetooth::gatt::kPropertyNotify; bluetooth::gatt::Characteristic& ctrl = g_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; } prev_char_handle = char_handle; prev_char_properties = el.properties; } } pending_svc_decl.clear(); blob_index.clear(); // The Uuid provided here is unimportant, and is only used to satisfy // BlueDroid. // It must be different than any other registered Uuid. bluetooth::Uuid client_id = bluetooth::Uuid::GetRandom(); bt_status_t btstat = g_internal->gatt->client->register_client(client_id, false); if (btstat != BT_STATUS_SUCCESS) { LOG_ERROR("%s: Failed to register client", __func__); } } void RequestReadCallback(int conn_id, int trans_id, const RawAddress& bda, int attr_handle, int attribute_offset_octets, bool is_long) { std::lock_guard 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( "%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(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, const RawAddress& bda, int attr_handle, int attribute_offset, bool need_rsp, bool is_prep, std::vector value) { std::string addr(BtAddrString(&bda)); LOG_INFO( "%s: connection:%d (%s:trans:%d) write attr:%d attribute_offset:%d " "length:%zu " "need_resp:%u is_prep:%u", __func__, conn_id, addr.c_str(), trans_id, attr_handle, attribute_offset, value.size(), need_rsp, is_prep); std::lock_guard lock(g_internal->lock); bluetooth::gatt::Characteristic& ch = g_internal->characteristics[attr_handle]; ch.blob.resize(attribute_offset + value.size()); std::copy(value.begin(), value.end(), 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("%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.To128BitBE(); ssize_t status; OSI_NO_INTR(status = write(g_internal->pipefd[kPipeWriteEnd], attr_uuid.data(), attr_uuid.size())); if (-1 == status) LOG_ERROR("%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 = value.size(); 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.begin(), value.end(), response.attr_value.value); g_internal->gatt->server->send_response(conn_id, trans_id, 0, response); } void RequestExecWriteCallback(int conn_id, int trans_id, const RawAddress& bda, int exec_write) { std::string addr(BtAddrString(&bda)); LOG_INFO("%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 lock(g_internal->lock); // Communicate the attribute Uuid as notification of a write update. const bluetooth::Uuid::UUID128Bit uuid = g_internal->last_write.To128BitBE(); ssize_t status; OSI_NO_INTR(status = write(g_internal->pipefd[kPipeWriteEnd], uuid.data(), uuid.size())); if (-1 == status) LOG_ERROR("%s: write failed: %s", __func__, strerror(errno)); } void ConnectionCallback(int conn_id, int server_if, int connected, const RawAddress& bda) { std::string addr(BtAddrString(&bda)); LOG_INFO("%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 EnableAdvertisingCallback(uint8_t status) { LOG_INFO("%s: status:%d", __func__, status); // This terminates a Start call. std::lock_guard lock(g_internal->lock); g_internal->api_synchronize.notify_one(); } void RegisterClientCallback(int status, int client_if, const bluetooth::Uuid& app_uuid) { LOG_INFO("%s: status:%d client_if:%d uuid[0]:%s", __func__, status, client_if, app_uuid.ToString().c_str()); g_internal->client_if = client_if; // Setup our advertisement. This has no callback. g_internal->gatt->advertiser->SetData(0 /* std_inst */, false, {/*TODO: put inverval 2,2 here*/}, base::DoNothing()); g_internal->gatt->advertiser->Enable( 0 /* std_inst */, true, base::Bind(&EnableAdvertisingCallback), 0 /* no duration */, 0 /* no maxExtAdvEvent*/, base::DoNothing()); } void ServiceStoppedCallback(int status, int server_if, int srvc_handle) { LOG_INFO("%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 lock(g_internal->lock); g_internal->api_synchronize.notify_one(); } void ScanResultCallback(uint16_t ble_evt_type, uint8_t addr_type, RawAddress* bda, uint8_t ble_primary_phy, uint8_t ble_secondary_phy, uint8_t ble_advertising_sid, int8_t ble_tx_power, int8_t rssi, uint16_t ble_periodic_adv_int, std::vector adv_data) { std::string addr(BtAddrString(bda)); std::lock_guard lock(g_internal->lock); g_internal->scan_results[addr] = rssi; } void ClientConnectCallback(int conn_id, int status, int client_if, const RawAddress& bda) { std::string addr(BtAddrString(&bda)); LOG_INFO("%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, const RawAddress& bda) { std::string addr(BtAddrString(&bda)); LOG_INFO("%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, ServiceStoppedCallback, nullptr, /* service_deleted_cb */ RequestReadCallback, RequestReadCallback, RequestWriteCallback, RequestWriteCallback, RequestExecWriteCallback, ResponseConfirmationCallback, IndicationSentCallback, nullptr, /* congestion_cb*/ nullptr, /* mtu_changed_cb */ nullptr, /* phy_update_cb */ nullptr, /* conn_update_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, 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; */ nullptr, /* configure_mtu_cb; */ nullptr, /* congestion_cb; */ nullptr, /* get_gatt_db_cb; */ nullptr, /* services_removed_cb */ nullptr, /* services_added_cb */ nullptr, /* phy_update_cb */ nullptr, /* conn_update_cb */ nullptr, /* service_changed_cb*/ }; const btgatt_scanner_callbacks_t gatt_scanner_callbacks = { ScanResultCallback, nullptr, /* batchscan_reports_cb; */ nullptr, /* batchscan_threshold_cb; */ nullptr, /* track_adv_event_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, /** GATT Server callbacks */ &gatt_scanner_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( bt_iface->get_profile_interface(BT_PROFILE_GATT_ID)); if (!gatt) { LOG_ERROR("Error getting GATT interface"); return -1; } bt_status_t btstat = gatt->init(&gatt_callbacks); if (btstat != BT_STATUS_SUCCESS) { LOG_ERROR("Failed to initialize gatt interface"); return -1; } int status = pipe(pipefd); if (status == -1) { LOG_ERROR("pipe creation failed: %s", strerror(errno)); return -1; } return 0; } bt_status_t ServerInternals::AddCharacteristic(const Uuid& uuid, uint8_t properties, uint16_t permissions) { pending_svc_decl.push_back({.uuid = uuid, .type = BTGATT_DB_CHARACTERISTIC, .properties = properties, .permissions = permissions}); return BT_STATUS_SUCCESS; } 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("Error creating internals"); return false; } g_internal = internal_.get(); std::unique_lock lock(internal_->lock); int status = internal_->Initialize(); if (status) { LOG_ERROR("Error initializing internals"); return false; } bt_status_t btstat = internal_->gatt->server->register_server(service_id, false); if (btstat != BT_STATUS_SUCCESS) { LOG_ERROR("Failed to register server"); return false; } internal_->api_synchronize.wait(lock); // TODO(icoolidge): Better error handling. if (internal_->server_if == 0) { LOG_ERROR("Initialization of server failed"); return false; } *gatt_pipe = internal_->pipefd[kPipeReadEnd]; LOG_INFO("Server Initialize succeeded"); return true; } bool Server::SetAdvertisement(const std::vector& ids, const std::vector& service_data, const std::vector& manufacturer_data, bool transmit_name) { // std::vector id_data; // const auto& mutable_manufacturer_data = manufacturer_data; // const auto& mutable_service_data = service_data; // for (const Uuid &id : ids) { // const auto le_id = id.To128BitLE(); // id_data.insert(id_data.end(), le_id.begin(), le_id.end()); // } std::lock_guard lock(internal_->lock); // Setup our advertisement. This has no callback. internal_->gatt->advertiser->SetData(0, false, /* beacon, not scan response */ {}, base::DoNothing()); // transmit_name, /* name */ // 2, 2, interval // mutable_manufacturer_data, // mutable_service_data, // id_data); return true; } bool Server::SetScanResponse(const std::vector& ids, const std::vector& service_data, const std::vector& manufacturer_data, bool transmit_name) { // std::vector id_data; // const auto& mutable_manufacturer_data = manufacturer_data; // const auto& mutable_service_data = service_data; // for (const Uuid &id : ids) { // const auto le_id = id.To128BitLE(); // id_data.insert(id_data.end(), le_id.begin(), le_id.end()); // } std::lock_guard lock(internal_->lock); // Setup our advertisement. This has no callback. internal_->gatt->advertiser->SetData(0, true, /* scan response */ {}, base::DoNothing()); // transmit_name, /* name */ // false, /* no txpower */ // 2, 2, interval // 0, /* appearance */ // mutable_manufacturer_data, // mutable_service_data, // id_data); return true; } bool Server::AddCharacteristic(const Uuid& id, int properties, int permissions) { std::unique_lock lock(internal_->lock); bt_status_t btstat = internal_->AddCharacteristic(id, properties, permissions); if (btstat != BT_STATUS_SUCCESS) { LOG_ERROR("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 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("Failed to set scan response data"); return false; } // Next, add the secondary attribute (blob control). // Control attributes have fixed permissions/properties. // Remember position at which blob was added. blob_index.insert(pending_svc_decl.size()); btstat = internal_->AddCharacteristic(control_id, kPropertyRead | kPropertyWrite, kPermissionRead | kPermissionWrite); return true; } bool Server::Start() { std::unique_lock lock(internal_->lock); bt_status_t btstat = internal_->gatt->server->add_service( internal_->server_if, pending_svc_decl); if (btstat != BT_STATUS_SUCCESS) { LOG_ERROR("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 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("Failed to stop service with handle: 0x%04x", internal_->service_handle); return false; } internal_->api_synchronize.wait(lock); return true; } bool Server::ScanEnable() { internal_->gatt->scanner->Scan(true); return true; } bool Server::ScanDisable() { internal_->gatt->scanner->Scan(false); return true; } bool Server::GetScanResults(ScanResults* results) { std::lock_guard lock(internal_->lock); *results = internal_->scan_results; return true; } bool Server::SetCharacteristicValue(const Uuid& id, const std::vector& value) { std::lock_guard 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) { internal_->gatt->server->send_indication(internal_->server_if, attribute_id, connection, true, {0}); } return true; } bool Server::GetCharacteristicValue(const Uuid& id, std::vector* value) { std::lock_guard lock(internal_->lock); const int attribute_id = internal_->uuid_to_attribute[id]; *value = internal_->characteristics[attribute_id].blob; return true; } } // namespace gatt } // namespace bluetooth