| /****************************************************************************** |
| * |
| * 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.h" |
| |
| #include <base/logging.h> |
| |
| #include <algorithm> |
| #include <list> |
| #include <sstream> |
| |
| #include "crypto_toolbox/crypto_toolbox.h" |
| #include "internal_include/bt_trace.h" |
| #include "stack/include/bt_types.h" |
| #include "stack/include/gattdefs.h" |
| #include "types/bluetooth/uuid.h" |
| |
| using bluetooth::Uuid; |
| |
| namespace gatt { |
| |
| namespace { |
| const Uuid PRIMARY_SERVICE = Uuid::From16Bit(GATT_UUID_PRI_SERVICE); |
| const Uuid SECONDARY_SERVICE = Uuid::From16Bit(GATT_UUID_SEC_SERVICE); |
| const Uuid INCLUDE = Uuid::From16Bit(GATT_UUID_INCLUDE_SERVICE); |
| const Uuid CHARACTERISTIC = Uuid::From16Bit(GATT_UUID_CHAR_DECLARE); |
| const Uuid CHARACTERISTIC_EXTENDED_PROPERTIES = |
| Uuid::From16Bit(GATT_UUID_CHAR_EXT_PROP); |
| |
| bool HandleInRange(const Service& svc, uint16_t handle) { |
| return handle >= svc.handle && handle <= svc.end_handle; |
| } |
| } // namespace |
| |
| static size_t UuidSize(const Uuid& uuid) { |
| size_t len = uuid.GetShortestRepresentationSize(); |
| return (len == Uuid::kNumBytes32) ? Uuid::kNumBytes128 : len; |
| } |
| |
| Service* FindService(std::list<Service>& services, uint16_t handle) { |
| for (Service& service : services) { |
| if (handle >= service.handle && handle <= service.end_handle) |
| return &service; |
| } |
| |
| return nullptr; |
| } |
| |
| std::string Database::ToString() const { |
| std::stringstream tmp; |
| |
| for (const Service& service : services) { |
| tmp << "Service: handle=" << loghex(service.handle) |
| << ", end_handle=" << loghex(service.end_handle) |
| << ", uuid=" << service.uuid << "\n"; |
| |
| for (const auto& is : service.included_services) { |
| tmp << "\t Included service: handle=" << loghex(is.handle) |
| << ", start_handle=" << loghex(is.start_handle) |
| << ", end_handle=" << loghex(is.end_handle) << ", uuid=" << is.uuid |
| << "\n"; |
| } |
| |
| for (const Characteristic& c : service.characteristics) { |
| tmp << "\t Characteristic: declaration_handle=" |
| << loghex(c.declaration_handle) |
| << ", value_handle=" << loghex(c.value_handle) << ", uuid=" << c.uuid |
| << ", prop=" << loghex(c.properties) << "\n"; |
| |
| for (const Descriptor& d : c.descriptors) { |
| tmp << "\t\t Descriptor: handle=" << loghex(d.handle) |
| << ", uuid=" << d.uuid << "\n"; |
| } |
| } |
| } |
| return tmp.str(); |
| } |
| |
| std::vector<StoredAttribute> Database::Serialize() const { |
| std::vector<StoredAttribute> nv_attr; |
| |
| if (services.empty()) return std::vector<StoredAttribute>(); |
| |
| for (const Service& service : services) { |
| // TODO: add constructor to NV_ATTR, use emplace_back |
| nv_attr.push_back({service.handle, |
| service.is_primary ? PRIMARY_SERVICE : SECONDARY_SERVICE, |
| {.service = {.uuid = service.uuid, |
| .end_handle = service.end_handle}}}); |
| } |
| |
| for (const Service& service : services) { |
| for (const IncludedService& p_isvc : service.included_services) { |
| nv_attr.push_back({p_isvc.handle, |
| INCLUDE, |
| {.included_service = {.handle = p_isvc.start_handle, |
| .end_handle = p_isvc.end_handle, |
| .uuid = p_isvc.uuid}}}); |
| } |
| |
| for (const Characteristic& charac : service.characteristics) { |
| nv_attr.push_back( |
| {charac.declaration_handle, |
| CHARACTERISTIC, |
| {.characteristic = {.properties = charac.properties, |
| .value_handle = charac.value_handle, |
| .uuid = charac.uuid}}}); |
| |
| for (const Descriptor& desc : charac.descriptors) { |
| if (desc.uuid == CHARACTERISTIC_EXTENDED_PROPERTIES) { |
| nv_attr.push_back({desc.handle, |
| desc.uuid, |
| {.characteristic_extended_properties = |
| desc.characteristic_extended_properties}}); |
| } else { |
| nv_attr.push_back({desc.handle, desc.uuid, {}}); |
| } |
| } |
| } |
| } |
| |
| return nv_attr; |
| } |
| |
| Database Database::Deserialize(const std::vector<StoredAttribute>& nv_attr, |
| bool* success) { |
| // clear reallocating |
| Database result; |
| auto it = nv_attr.cbegin(); |
| |
| for (; it != nv_attr.cend(); ++it) { |
| const auto& attr = *it; |
| if (attr.type != PRIMARY_SERVICE && attr.type != SECONDARY_SERVICE) break; |
| result.services.emplace_back(Service{ |
| .handle = attr.handle, |
| .uuid = attr.value.service.uuid, |
| .is_primary = (attr.type == PRIMARY_SERVICE), |
| .end_handle = attr.value.service.end_handle, |
| .included_services = {}, |
| .characteristics = {}, |
| }); |
| } |
| |
| auto current_service_it = result.services.begin(); |
| for (; it != nv_attr.cend(); it++) { |
| const auto& attr = *it; |
| |
| // go to the service this attribute belongs to; attributes are stored in |
| // order, so iterating just forward is enough |
| while (current_service_it != result.services.end() && |
| current_service_it->end_handle < attr.handle) { |
| current_service_it++; |
| } |
| |
| if (current_service_it == result.services.end() || |
| !HandleInRange(*current_service_it, attr.handle)) { |
| LOG(ERROR) << "Can't find service for attribute with handle: " |
| << loghex(attr.handle); |
| *success = false; |
| return result; |
| } |
| |
| if (attr.type == INCLUDE) { |
| Service* included_service = |
| FindService(result.services, attr.value.included_service.handle); |
| if (!included_service) { |
| LOG(ERROR) << __func__ << ": Non-existing included service!"; |
| *success = false; |
| return result; |
| } |
| current_service_it->included_services.push_back(IncludedService{ |
| .handle = attr.handle, |
| .uuid = attr.value.included_service.uuid, |
| .start_handle = attr.value.included_service.handle, |
| .end_handle = attr.value.included_service.end_handle, |
| }); |
| } else if (attr.type == CHARACTERISTIC) { |
| current_service_it->characteristics.emplace_back(Characteristic{ |
| .declaration_handle = attr.handle, |
| .uuid = attr.value.characteristic.uuid, |
| .value_handle = attr.value.characteristic.value_handle, |
| .properties = attr.value.characteristic.properties, |
| .descriptors = {}, |
| }); |
| |
| } else { |
| if (attr.type == CHARACTERISTIC_EXTENDED_PROPERTIES) { |
| current_service_it->characteristics.back().descriptors.emplace_back( |
| Descriptor{.handle = attr.handle, |
| .uuid = attr.type, |
| .characteristic_extended_properties = |
| attr.value.characteristic_extended_properties}); |
| |
| } else { |
| current_service_it->characteristics.back().descriptors.emplace_back( |
| Descriptor{ |
| .handle = attr.handle, |
| .uuid = attr.type, |
| .characteristic_extended_properties = {}, |
| }); |
| } |
| } |
| } |
| *success = true; |
| return result; |
| } |
| |
| Octet16 Database::Hash() const { |
| int len = 0; |
| // Compute how much space we need to actually hold the data. |
| for (const Service& service : services) { |
| len += 4 + UuidSize(service.uuid); |
| |
| for (const auto& is : service.included_services) { |
| len += 8 + UuidSize(is.uuid); |
| } |
| |
| for (const Characteristic& c : service.characteristics) { |
| len += 7 + UuidSize(c.uuid); |
| |
| for (const Descriptor& d : c.descriptors) { |
| if (UuidSize(d.uuid) != Uuid::kNumBytes16) { |
| continue; |
| } |
| uint16_t value = d.uuid.As16Bit(); |
| if (value == GATT_UUID_CHAR_DESCRIPTION || |
| value == GATT_UUID_CHAR_CLIENT_CONFIG || |
| value == GATT_UUID_CHAR_SRVR_CONFIG || |
| value == GATT_UUID_CHAR_PRESENT_FORMAT || |
| value == GATT_UUID_CHAR_AGG_FORMAT) { |
| len += 2 + UuidSize(d.uuid); |
| } else if (value == GATT_UUID_CHAR_EXT_PROP) { |
| len += 4 + UuidSize(d.uuid); |
| } |
| } |
| } |
| } |
| |
| std::vector<uint8_t> serialized(len); |
| uint8_t* p = serialized.data(); |
| for (const Service& service : services) { |
| UINT16_TO_STREAM(p, service.handle); |
| if (service.is_primary) { |
| UINT16_TO_STREAM(p, GATT_UUID_PRI_SERVICE); |
| } else { |
| UINT16_TO_STREAM(p, GATT_UUID_SEC_SERVICE); |
| } |
| |
| if (UuidSize(service.uuid) == Uuid::kNumBytes16) { |
| UINT16_TO_STREAM(p, service.uuid.As16Bit()); |
| } else { |
| ARRAY_TO_STREAM(p, service.uuid.To128BitLE(), (int)Uuid::kNumBytes128); |
| } |
| |
| for (const auto& is : service.included_services) { |
| UINT16_TO_STREAM(p, is.handle); |
| UINT16_TO_STREAM(p, GATT_UUID_INCLUDE_SERVICE); |
| UINT16_TO_STREAM(p, is.start_handle); |
| UINT16_TO_STREAM(p, is.end_handle); |
| |
| if (UuidSize(is.uuid) == Uuid::kNumBytes16) { |
| UINT16_TO_STREAM(p, is.uuid.As16Bit()); |
| } else { |
| ARRAY_TO_STREAM(p, is.uuid.To128BitLE(), (int)Uuid::kNumBytes128); |
| } |
| } |
| |
| for (const Characteristic& c : service.characteristics) { |
| UINT16_TO_STREAM(p, c.declaration_handle); |
| UINT16_TO_STREAM(p, GATT_UUID_CHAR_DECLARE); |
| UINT8_TO_STREAM(p, c.properties); |
| UINT16_TO_STREAM(p, c.value_handle); |
| |
| if (UuidSize(c.uuid) == Uuid::kNumBytes16) { |
| UINT16_TO_STREAM(p, c.uuid.As16Bit()); |
| } else { |
| ARRAY_TO_STREAM(p, c.uuid.To128BitLE(), (int)Uuid::kNumBytes128); |
| } |
| |
| for (const Descriptor& d : c.descriptors) { |
| if (UuidSize(d.uuid) != Uuid::kNumBytes16) continue; |
| uint16_t value = d.uuid.As16Bit(); |
| if (value == GATT_UUID_CHAR_DESCRIPTION || |
| value == GATT_UUID_CHAR_CLIENT_CONFIG || |
| value == GATT_UUID_CHAR_SRVR_CONFIG || |
| value == GATT_UUID_CHAR_PRESENT_FORMAT || |
| value == GATT_UUID_CHAR_AGG_FORMAT) { |
| UINT16_TO_STREAM(p, d.handle); |
| UINT16_TO_STREAM(p, d.uuid.As16Bit()); |
| } else if (value == GATT_UUID_CHAR_EXT_PROP) { |
| UINT16_TO_STREAM(p, d.handle); |
| UINT16_TO_STREAM(p, d.uuid.As16Bit()); |
| UINT16_TO_STREAM(p, d.characteristic_extended_properties); |
| } |
| } |
| } |
| } |
| |
| std::reverse(serialized.begin(), serialized.end()); |
| return crypto_toolbox::aes_cmac(Octet16{0}, serialized.data(), |
| serialized.size()); |
| } |
| } // namespace gatt |