blob: 3a7d88112c9f7b253d152949bb35e622cfc46637 [file] [log] [blame]
/*
* 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;
}