| /****************************************************************************** |
| * |
| * 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 "database_builder.h" |
| |
| #include "bt_trace.h" |
| |
| #include <base/logging.h> |
| #include <algorithm> |
| |
| 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, |
| .end_handle = end_handle, |
| .is_primary = is_primary, |
| .uuid = uuid}); |
| } 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, |
| .end_handle = end_handle, |
| .is_primary = is_primary, |
| .uuid = uuid}); |
| } |
| |
| 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, |
| .value_handle = value_handle, |
| .properties = properties, |
| .uuid = uuid}); |
| 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}); |
| } |
| |
| 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<uint16_t, uint16_t>& |
| DatabaseBuilder::CurrentlyExploredService() { |
| return pending_service; |
| } |
| |
| std::pair<uint16_t, uint16_t> 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}; |
| } |
| |
| 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 |