/****************************************************************************** * * Copyright 2018 The Android Open Source Project * * 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 #include #include #include #include #include #include "bt_target.h" // Must be first to define build configuration #include "bta/gatt/database.h" #include "bta/gatt/database_builder.h" #include "stack/include/gattdefs.h" #include "types/bluetooth/uuid.h" using bluetooth::Uuid; namespace gatt { void DatabaseBuilder::AddService(uint16_t handle, uint16_t end_handle, const Uuid& uuid, bool is_primary) { // general case optimization - we add services in order if (database.services.empty() || database.services.back().end_handle < handle) { database.services.emplace_back(Service{ .handle = handle, .uuid = uuid, .is_primary = is_primary, .end_handle = end_handle, }); } else { auto& vec = database.services; // Find first service whose start handle is bigger than new service handle auto it = std::lower_bound( vec.begin(), vec.end(), handle, [](Service s, uint16_t handle) { return s.end_handle < handle; }); // Insert new service just before it vec.emplace(it, Service{ .handle = handle, .uuid = uuid, .is_primary = is_primary, .end_handle = end_handle, }); } services_to_discover.insert({handle, end_handle}); } void DatabaseBuilder::AddIncludedService(uint16_t handle, const Uuid& uuid, uint16_t start_handle, uint16_t end_handle) { Service* service = FindService(database.services, handle); if (!service) { LOG(ERROR) << "Illegal action to add to non-existing service!"; return; } /* We discover all Primary Services first. If included service was not seen * before, it must be a Secondary Service */ if (!FindService(database.services, start_handle)) { AddService(start_handle, end_handle, uuid, false /* not primary */); } service->included_services.push_back(IncludedService{ .handle = handle, .uuid = uuid, .start_handle = start_handle, .end_handle = end_handle, }); } void DatabaseBuilder::AddCharacteristic(uint16_t handle, uint16_t value_handle, const Uuid& uuid, uint8_t properties) { Service* service = FindService(database.services, handle); if (!service) { LOG(ERROR) << "Illegal action to add to non-existing service!"; return; } if (service->end_handle < value_handle) LOG(WARNING) << "Remote device violates spec: value_handle=" << loghex(value_handle) << " is after service end_handle=" << loghex(service->end_handle); service->characteristics.emplace_back(Characteristic{ .declaration_handle = handle, .uuid = uuid, .value_handle = value_handle, .properties = properties, }); return; } void DatabaseBuilder::AddDescriptor(uint16_t handle, const Uuid& uuid) { Service* service = FindService(database.services, handle); if (!service) { LOG(ERROR) << "Illegal action to add to non-existing service!"; return; } if (service->characteristics.empty()) { LOG(ERROR) << __func__ << ": Illegal action to add to non-existing characteristic!"; return; } Characteristic* char_node = &service->characteristics.front(); for (auto it = service->characteristics.begin(); it != service->characteristics.end(); it++) { if (it->declaration_handle > handle) break; char_node = &(*it); } char_node->descriptors.emplace_back( gatt::Descriptor{.handle = handle, .uuid = uuid}); // We must read value for Characteristic Extended Properties if (uuid == Uuid::From16Bit(GATT_UUID_CHAR_EXT_PROP)) { descriptor_handles_to_read.emplace_back(handle); } } bool DatabaseBuilder::StartNextServiceExploration() { while (!services_to_discover.empty()) { auto handle_range = services_to_discover.begin(); pending_service = *handle_range; services_to_discover.erase(handle_range); // Empty service declaration, nothing to explore, skip to next. if (pending_service.first == pending_service.second) continue; pending_characteristic = HANDLE_MIN; return true; } return false; } const std::pair& DatabaseBuilder::CurrentlyExploredService() { return pending_service; } std::pair DatabaseBuilder::NextDescriptorRangeToExplore() { Service* service = FindService(database.services, pending_service.first); if (!service || service->characteristics.empty()) { return {HANDLE_MAX, HANDLE_MAX}; } for (auto it = service->characteristics.cbegin(); it != service->characteristics.cend(); it++) { if (it->declaration_handle > pending_characteristic) { auto next = std::next(it); /* Characteristic Declaration is followed by Characteristic Value * Declaration, first descriptor is after that, see BT Spect 5.0 Vol 3, * Part G 3.3.2 and 3.3.3 */ uint16_t start = it->declaration_handle + 2; uint16_t end; if (next != service->characteristics.end()) end = next->declaration_handle - 1; else end = service->end_handle; // No place for descriptor - skip to next characteristic if (start > end) continue; pending_characteristic = start; return {start, end}; } } pending_characteristic = HANDLE_MAX; return {HANDLE_MAX, HANDLE_MAX}; } Descriptor* FindDescriptorByHandle(std::list& services, uint16_t handle) { Service* service = FindService(services, handle); if (!service) return nullptr; Characteristic* char_node = &service->characteristics.front(); for (auto it = service->characteristics.begin(); it != service->characteristics.end(); it++) { if (it->declaration_handle > handle) break; char_node = &(*it); } for (auto& descriptor : char_node->descriptors) { if (descriptor.handle == handle) return &descriptor; } return nullptr; } bool DatabaseBuilder::SetValueOfDescriptors( const std::vector& values) { if (values.size() > descriptor_handles_to_read.size()) { LOG(ERROR) << "values.size() <= descriptors.size() expected"; descriptor_handles_to_read.clear(); return false; } for (size_t i = 0; i < values.size(); i++) { Descriptor* d = FindDescriptorByHandle(database.services, descriptor_handles_to_read[i]); if (!d) { LOG(ERROR) << __func__ << "non-existing descriptor!"; descriptor_handles_to_read.clear(); return false; } d->characteristic_extended_properties = values[i]; } descriptor_handles_to_read.erase( descriptor_handles_to_read.begin(), descriptor_handles_to_read.begin() + values.size()); return true; } bool DatabaseBuilder::InProgress() const { return !database.services.empty(); } Database DatabaseBuilder::Build() { Database tmp = database; database.Clear(); return tmp; } void DatabaseBuilder::Clear() { database.Clear(); } std::string DatabaseBuilder::ToString() const { return database.ToString(); } } // namespace gatt