| /* |
| * Copyright 2020 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 <string> |
| |
| #include "dumpsys/internal/filter_internal.h" |
| #include "flatbuffers/flatbuffers.h" |
| #include "flatbuffers/idl.h" |
| #include "os/log.h" |
| |
| #define DBG 0 |
| |
| using namespace bluetooth; |
| using namespace dumpsys; |
| |
| constexpr flatbuffers::voffset_t kErasedFromTable = 0; |
| constexpr bool kFieldIsNotPopulated = true; |
| constexpr bool kFieldHasBeenFiltered = true; |
| constexpr bool kFieldContinueFiltering = false; |
| |
| void internal::ScrubFromTable(flatbuffers::Table* table, flatbuffers::voffset_t field_offset) { |
| ASSERT(table != nullptr); |
| uint8_t* vtable = const_cast<uint8_t*>(table->GetVTable()); |
| vtable[field_offset] = kErasedFromTable; |
| } |
| |
| void internal::ReplaceInString(flatbuffers::String* string, int c) { |
| uint8_t* p = const_cast<uint8_t*>(string->Data()); |
| memset(p, c, string->size()); |
| } |
| |
| void internal::RandomizeInString(flatbuffers::String* string) { |
| std::size_t hash = std::hash<std::string>{}(string->str()); |
| std::string hashed_string = std::to_string(hash); |
| ReplaceInString(string, ' '); |
| size_t len = std::min(static_cast<size_t>(string->size()), hashed_string.size()); |
| uint8_t* p = const_cast<uint8_t*>(string->Data()); |
| memcpy(p, hashed_string.c_str(), len); |
| } |
| |
| const char* internal::PrivacyLevelName(PrivacyLevel privacy_level) { |
| switch (privacy_level) { |
| case kPrivate: |
| return "Private"; |
| break; |
| case kOpaque: |
| return "Opaque"; |
| break; |
| case kAnonymized: |
| return "Anonymized"; |
| break; |
| case kAny: |
| return "Any"; |
| break; |
| } |
| }; |
| internal::PrivacyLevel internal::GetPrivacyLevelAttribute(const std::string& string) { |
| if (string == "Any") { |
| return kAny; |
| } else if (string == "Anonymized") { |
| return kAnonymized; |
| } else if (string == "Opaque") { |
| return kOpaque; |
| } else if (string == "Private") { |
| return kPrivate; |
| } |
| return kDefaultPrivacyLevel; |
| } |
| |
| internal::PrivacyLevel internal::FindFieldPrivacyLevel(const reflection::Field& field) { |
| PrivacyLevel privacy_level = kDefaultPrivacyLevel; |
| |
| if (field.attributes() != nullptr) { |
| auto key = field.attributes()->LookupByKey(kPrivacyAttributeKeyword); |
| if (key != nullptr) { |
| privacy_level = internal::GetPrivacyLevelAttribute(key->value()->str()); |
| } |
| } |
| return privacy_level; |
| } |
| |
| const reflection::Object* internal::FindReflectionObject( |
| const flatbuffers::Vector<flatbuffers::Offset<reflection::Object>>* objects, const flatbuffers::String* name) { |
| ASSERT(objects != nullptr); |
| ASSERT(name != nullptr); |
| for (auto it = objects->cbegin(); it != objects->cend(); ++it) { |
| if (it->name()->str() == name->str()) { |
| return *it; |
| } |
| } |
| return nullptr; |
| } |
| |
| bool internal::FilterTypeBool(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) { |
| ASSERT(table != nullptr); |
| |
| // TODO(cmanton) Figure out boolean filtering |
| return kFieldHasBeenFiltered; |
| } |
| |
| bool internal::FilterTypeInteger( |
| const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) { |
| ASSERT(table != nullptr); |
| ASSERT(flatbuffers::IsInteger(field.type()->base_type())); |
| |
| int32_t default_val = flatbuffers::GetFieldDefaultI<int32_t>(field); |
| flatbuffers::voffset_t field_offset = field.offset(); |
| [[maybe_unused]] int32_t val = table->GetField<int32_t>(field_offset, default_val); |
| |
| switch (privacy_level) { |
| case kPrivate: |
| flatbuffers::SetField<int32_t>(table, field, default_val); |
| internal::ScrubFromTable(table, field_offset); |
| break; |
| case kOpaque: |
| flatbuffers::SetField<int32_t>(table, field, default_val); |
| break; |
| case kAnonymized: { |
| auto target_field = flatbuffers::GetFieldI<int32_t>(*table, field); |
| int32_t new_val = static_cast<int32_t>(std::hash<std::string>{}(std::to_string(target_field))); |
| flatbuffers::SetField<int32_t>(table, field, new_val); |
| } break; |
| default: |
| case kAny: |
| break; |
| } |
| |
| if (DBG) { |
| LOG_INFO( |
| "Integer Field_name:%s privacy_level:%s old_value:%d / 0x%x ==> new_value:%d\n", |
| field.name()->c_str(), |
| PrivacyLevelName(privacy_level), |
| val, |
| val, |
| table->GetField<int32_t>(field_offset, default_val)); |
| } |
| return kFieldHasBeenFiltered; |
| } |
| |
| bool internal::FilterTypeFloat(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) { |
| ASSERT(table != nullptr); |
| ASSERT(flatbuffers::IsFloat(field.type()->base_type())); |
| |
| float default_val = flatbuffers::GetFieldDefaultI<float>(field); |
| flatbuffers::voffset_t field_offset = field.offset(); |
| [[maybe_unused]] float val = table->GetField<float>(field_offset, default_val); |
| switch (privacy_level) { |
| case kPrivate: |
| flatbuffers::SetField<float>(table, field, default_val); |
| internal::ScrubFromTable(table, field_offset); |
| break; |
| case kOpaque: |
| flatbuffers::SetField<float>(table, field, default_val); |
| break; |
| case kAnonymized: { |
| auto target_field = flatbuffers::GetFieldF<float>(*table, field); |
| int32_t new_val = static_cast<float>(std::hash<std::string>{}(std::to_string(target_field))); |
| flatbuffers::SetField<float>(table, field, new_val); |
| } break; |
| default: |
| case kAny: |
| break; |
| } |
| if (DBG) { |
| LOG_INFO( |
| "Float Field_name:%s privacy_level:%s old_value:%f ==> new_value:%f", |
| field.name()->c_str(), |
| PrivacyLevelName(privacy_level), |
| val, |
| table->GetField<float>(field_offset, default_val)); |
| } |
| return kFieldHasBeenFiltered; |
| } |
| |
| bool internal::FilterTypeString(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) { |
| ASSERT(table != nullptr); |
| ASSERT(field.type()->base_type() == reflection::BaseType::String); |
| |
| flatbuffers::voffset_t field_offset = field.offset(); |
| |
| const flatbuffers::String* string = flatbuffers::GetFieldS(*table, field); |
| if (string == nullptr) { |
| return kFieldIsNotPopulated; |
| // Field is not populated |
| } |
| ASSERT(string != nullptr); |
| flatbuffers::String* mutable_string = const_cast<flatbuffers::String*>(string); |
| |
| [[maybe_unused]] std::string old_string(string->str()); |
| switch (privacy_level) { |
| case kPrivate: |
| internal::ReplaceInString(mutable_string, '*'); |
| internal::ScrubFromTable(table, field_offset); |
| break; |
| case kOpaque: |
| internal::ReplaceInString(mutable_string, '*'); |
| break; |
| case kAnonymized: |
| internal::RandomizeInString(mutable_string); |
| break; |
| default: |
| case kAny: |
| break; |
| } |
| if (DBG) { |
| LOG_INFO( |
| "%s Field_name:%s size:%u privacy_level:%s old_string:%s ==> new_string:%s", |
| __func__, |
| field.name()->c_str(), |
| string->size(), |
| PrivacyLevelName(privacy_level), |
| old_string.c_str(), |
| string->c_str()); |
| } |
| return kFieldHasBeenFiltered; |
| } |
| |
| bool internal::FilterTypeStruct(const reflection::Field& field, flatbuffers::Table* table, PrivacyLevel privacy_level) { |
| ASSERT(table != nullptr); |
| ASSERT(!flatbuffers::IsScalar(field.type()->base_type())); |
| |
| flatbuffers::voffset_t field_offset = field.offset(); |
| |
| if (privacy_level != kAny) { |
| flatbuffers::SetFieldT(table, field, nullptr); |
| internal::ScrubFromTable(table, field_offset); |
| if (DBG) { |
| LOG_INFO( |
| " Table Removing field name:%s privacy_level:%s", field.name()->c_str(), PrivacyLevelName(privacy_level)); |
| } |
| } |
| return kFieldContinueFiltering; |
| } |