You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
7.9 KiB
253 lines
7.9 KiB
/******************************************************************************
|
|
*
|
|
* 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 <algorithm>
|
|
#include <cstdint>
|
|
#include <list>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#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<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};
|
|
}
|
|
|
|
Descriptor* FindDescriptorByHandle(std::list<Service>& 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<uint16_t>& 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
|