Parse property contexts via a serialized trie

Currently, whenever a new program starts, libc initializes two data
structures related to properties from the raw property_context files.
There are two problems here,
1) This takes roughly 1.2ms on a trivial program to generate contents
   that could otherwise be cached.
2) One of the data structures is a descending list of prefixes, each
   of which needs to be checked, whereas a trie would be more
   efficient.

This change introduces two libraries,
1) libpropertycontextserializer meant to be used by property_service
   to create a serialized trie containing all of the property
   contexts.
2) libpropertycontextparser meant to be used by libc's property
   functions to parse this serialized trie during property lookup.

This new trie also contains the ability to have exact matches instead
of prefix matches for properties, which was not possible before.

Bug: 36001741
Change-Id: I42324f04c4d995a0e055e9685d79f40393dfca51
This commit is contained in:
Tom Cherry 2017-10-27 15:18:02 -07:00
parent d7edcc9bc4
commit d853f77ed3
15 changed files with 2004 additions and 0 deletions

View file

@ -0,0 +1 @@
../.clang-format-2

View file

@ -0,0 +1 @@
subdirs = ["*"]

View file

@ -0,0 +1,16 @@
cc_library_static {
name: "libpropertyinfoparser",
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
},
cppflags: [
"-Wall",
"-Wextra",
"-Werror",
],
stl: "none",
export_include_dirs: ["include"],
}

View file

@ -0,0 +1,221 @@
//
// Copyright (C) 2017 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.
//
#ifndef PROPERTY_INFO_PARSER_H
#define PROPERTY_INFO_PARSER_H
#include <stdint.h>
namespace android {
namespace properties {
// The below structs intentionally do not end with char name[0] or other tricks to allocate
// with a dynamic size, such that they can be added onto in the future without breaking
// backwards compatibility.
struct PropertyEntry {
uint32_t name_offset;
uint32_t namelen;
// This is the context match for this node_; ~0u if it doesn't correspond to any.
uint32_t context_index;
// This is the schema for this node_; ~0u if it doesn't correspond to any.
uint32_t schema_index;
};
struct TrieNodeInternal {
// This points to a property entry struct, which includes the name for this node
uint32_t property_entry;
// Children are a sorted list of child nodes_; binary search them.
uint32_t num_child_nodes;
uint32_t child_nodes;
// Prefixes are terminating prefix matches at this node, sorted longest to smallest
// Take the first match sequentially found with StartsWith().
uint32_t num_prefixes;
uint32_t prefix_entries;
// Exact matches are a sorted list of exact matches at this node_; binary search them.
uint32_t num_exact_matches;
uint32_t exact_match_entries;
};
struct PropertyInfoAreaHeader {
// The current version of this data as created by property service.
uint32_t current_version;
// The lowest version of libc that can properly parse this data.
uint32_t minimum_supported_version;
uint32_t size;
uint32_t contexts_offset;
uint32_t schemas_offset;
uint32_t root_offset;
};
class SerializedData {
public:
uint32_t size() const {
return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base_)->size;
}
const char* c_string(uint32_t offset) const {
if (offset != 0 && offset > size()) return nullptr;
return static_cast<const char*>(data_base_ + offset);
}
const uint32_t* uint32_array(uint32_t offset) const {
if (offset != 0 && offset > size()) return nullptr;
return reinterpret_cast<const uint32_t*>(data_base_ + offset);
}
uint32_t uint32(uint32_t offset) const {
if (offset != 0 && offset > size()) return ~0u;
return *reinterpret_cast<const uint32_t*>(data_base_ + offset);
}
const char* data_base() const { return data_base_; }
private:
const char data_base_[0];
};
class TrieNode {
public:
TrieNode() : serialized_data_(nullptr), trie_node_base_(nullptr) {}
TrieNode(const SerializedData* data_base, const TrieNodeInternal* trie_node_base)
: serialized_data_(data_base), trie_node_base_(trie_node_base) {}
const char* name() const {
return serialized_data_->c_string(node_property_entry()->name_offset);
}
uint32_t context_index() const { return node_property_entry()->context_index; }
uint32_t schema_index() const { return node_property_entry()->schema_index; }
uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }
TrieNode child_node(int n) const {
uint32_t child_node_offset = serialized_data_->uint32_array(trie_node_base_->child_nodes)[n];
const TrieNodeInternal* trie_node_base =
reinterpret_cast<const TrieNodeInternal*>(serialized_data_->data_base() + child_node_offset);
return TrieNode(serialized_data_, trie_node_base);
}
bool FindChildForString(const char* input, uint32_t namelen, TrieNode* child) const;
uint32_t num_prefixes() const { return trie_node_base_->num_prefixes; }
const PropertyEntry* prefix(int n) const {
uint32_t prefix_entry_offset =
serialized_data_->uint32_array(trie_node_base_->prefix_entries)[n];
return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
prefix_entry_offset);
}
uint32_t num_exact_matches() const { return trie_node_base_->num_exact_matches; }
const PropertyEntry* exact_match(int n) const {
uint32_t exact_match_entry_offset =
serialized_data_->uint32_array(trie_node_base_->exact_match_entries)[n];
return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
exact_match_entry_offset);
}
private:
const PropertyEntry* node_property_entry() const {
return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
trie_node_base_->property_entry);
}
const SerializedData* serialized_data_;
const TrieNodeInternal* trie_node_base_;
};
class PropertyInfoArea : private SerializedData {
public:
void GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
uint32_t* schema_index) const;
void GetPropertyInfo(const char* property, const char** context, const char** schema) const;
int FindContextIndex(const char* context) const;
int FindSchemaIndex(const char* schema) const;
const char* context(uint32_t index) const {
uint32_t context_array_size_offset = contexts_offset();
const uint32_t* context_array = uint32_array(context_array_size_offset + sizeof(uint32_t));
return data_base() + context_array[index];
}
const char* schema(uint32_t index) const {
uint32_t schema_array_size_offset = schemas_offset();
const uint32_t* schema_array = uint32_array(schema_array_size_offset + sizeof(uint32_t));
return data_base() + schema_array[index];
}
uint32_t current_version() const { return header()->current_version; }
uint32_t minimum_supported_version() const { return header()->minimum_supported_version; }
uint32_t size() const { return SerializedData::size(); }
uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }
uint32_t num_schemas() const { return uint32_array(schemas_offset())[0]; }
TrieNode root_node() const { return trie(header()->root_offset); }
private:
const PropertyInfoAreaHeader* header() const {
return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());
}
uint32_t contexts_offset() const { return header()->contexts_offset; }
uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }
uint32_t schemas_offset() const { return header()->schemas_offset; }
uint32_t schemas_array_offset() const { return schemas_offset() + sizeof(uint32_t); }
TrieNode trie(uint32_t offset) const {
if (offset != 0 && offset > size()) return TrieNode();
const TrieNodeInternal* trie_node_base =
reinterpret_cast<const TrieNodeInternal*>(data_base() + offset);
return TrieNode(this, trie_node_base);
}
};
// This is essentially a smart pointer for read only mmap region for property contexts.
class PropertyInfoAreaFile {
public:
PropertyInfoAreaFile() : mmap_base_(nullptr), mmap_size_(0) {}
~PropertyInfoAreaFile() { Reset(); }
PropertyInfoAreaFile(const PropertyInfoAreaFile&) = delete;
void operator=(const PropertyInfoAreaFile&) = delete;
PropertyInfoAreaFile(PropertyInfoAreaFile&&) = default;
PropertyInfoAreaFile& operator=(PropertyInfoAreaFile&&) = default;
bool LoadDefaultPath();
bool LoadPath(const char* filename);
const PropertyInfoArea* operator->() const {
return reinterpret_cast<const PropertyInfoArea*>(mmap_base_);
}
explicit operator bool() const { return mmap_base_ != nullptr; }
void Reset();
private:
void* mmap_base_;
size_t mmap_size_;
};
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,220 @@
//
// Copyright (C) 2017 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 "property_info_parser/property_info_parser.h"
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace android {
namespace properties {
namespace {
// Binary search to find index of element in an array compared via f(search).
template <typename F>
int Find(uint32_t array_length, F&& f) {
int bottom = 0;
int top = array_length - 1;
while (top >= bottom) {
int search = (top + bottom) / 2;
auto cmp = f(search);
if (cmp == 0) return search;
if (cmp < 0) bottom = search + 1;
if (cmp > 0) top = search - 1;
}
return -1;
}
} // namespace
// Binary search the list of contexts to find the index of a given context string.
// Only should be used for TrieSerializer to construct the Trie.
int PropertyInfoArea::FindContextIndex(const char* context) const {
return Find(num_contexts(), [this, context](auto array_offset) {
auto string_offset = uint32_array(contexts_array_offset())[array_offset];
return strcmp(c_string(string_offset), context);
});
}
// Binary search the list of schemas to find the index of a given schema string.
// Only should be used for TrieSerializer to construct the Trie.
int PropertyInfoArea::FindSchemaIndex(const char* schema) const {
return Find(num_schemas(), [this, schema](auto array_offset) {
auto string_offset = uint32_array(schemas_array_offset())[array_offset];
return strcmp(c_string(string_offset), schema);
});
}
// Binary search the list of children nodes to find a TrieNode for a given property piece.
// Used to traverse the Trie in GetPropertyInfoIndexes().
bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {
auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {
const char* child_name = child_node(array_offset).name();
int cmp = strncmp(child_name, name, namelen);
if (cmp == 0 && child_name[namelen] != '\0') {
// We use strncmp() since name isn't null terminated, but we don't want to match only a
// prefix of a child node's name, so we check here if we did only match a prefix and
// return 1, to indicate to the binary search to search earlier in the array for the real
// match.
return 1;
}
return cmp;
});
if (node_index == -1) {
return false;
}
*child = child_node(node_index);
return true;
}
void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
uint32_t* schema_index) const {
uint32_t return_context_index = ~0u;
uint32_t return_schema_index = ~0u;
const char* remaining_name = name;
auto trie_node = root_node();
while (true) {
const char* sep = strchr(remaining_name, '.');
// Apply prefix match for prefix deliminated with '.'
if (trie_node.context_index() != ~0u) {
return_context_index = trie_node.context_index();
}
if (trie_node.schema_index() != ~0u) {
return_schema_index = trie_node.schema_index();
}
if (sep == nullptr) {
break;
}
const uint32_t substr_size = sep - remaining_name;
TrieNode child_node;
if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {
break;
}
trie_node = child_node;
remaining_name = sep + 1;
}
// We've made it to a leaf node, so check contents and return appropriately.
// Check exact matches
for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {
if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {
if (context_index != nullptr) *context_index = trie_node.exact_match(i)->context_index;
if (schema_index != nullptr) *schema_index = trie_node.exact_match(i)->schema_index;
return;
}
}
// Check prefix matches for prefixes not deliminated with '.'
const uint32_t remaining_name_size = strlen(remaining_name);
for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
auto prefix_len = trie_node.prefix(i)->namelen;
if (prefix_len > remaining_name_size) continue;
if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {
if (context_index != nullptr) *context_index = trie_node.prefix(i)->context_index;
if (schema_index != nullptr) *schema_index = trie_node.prefix(i)->schema_index;
return;
}
}
// Return previously found '.' deliminated prefix match.
if (context_index != nullptr) *context_index = return_context_index;
if (schema_index != nullptr) *schema_index = return_schema_index;
return;
}
void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,
const char** schema) const {
uint32_t context_index;
uint32_t schema_index;
GetPropertyInfoIndexes(property, &context_index, &schema_index);
if (context != nullptr) {
if (context_index == ~0u) {
*context = nullptr;
} else {
*context = this->context(context_index);
}
}
if (schema != nullptr) {
if (schema_index == ~0u) {
*schema = nullptr;
} else {
*schema = this->schema(schema_index);
}
}
}
bool PropertyInfoAreaFile::LoadDefaultPath() {
return LoadPath("/dev/__properties__/property_info");
}
bool PropertyInfoAreaFile::LoadPath(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) {
close(fd);
return false;
}
if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
(fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
close(fd);
return false;
}
auto mmap_size = fd_stat.st_size;
void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
if (map_result == MAP_FAILED) {
close(fd);
return false;
}
auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
if (property_info_area->minimum_supported_version() > 1 ||
property_info_area->size() != mmap_size) {
munmap(map_result, mmap_size);
close(fd);
return false;
}
close(fd);
mmap_base_ = map_result;
mmap_size_ = mmap_size;
return true;
}
void PropertyInfoAreaFile::Reset() {
if (mmap_size_ > 0) {
munmap(mmap_base_, mmap_size_);
}
mmap_base_ = nullptr;
mmap_size_ = 0;
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,38 @@
cc_defaults {
name: "propertyinfoserializer_defaults",
cpp_std: "experimental",
sanitize: {
misc_undefined: ["signed-integer-overflow"],
},
cppflags: [
"-Wall",
"-Wextra",
"-Werror",
],
static_libs: [
"libpropertyinfoparser",
"libbase",
],
}
cc_library_static {
name: "libpropertyinfoserializer",
defaults: ["propertyinfoserializer_defaults"],
srcs: [
"property_info_serializer.cpp",
"trie_builder.cpp",
"trie_serializer.cpp",
],
export_include_dirs: ["include"],
}
cc_test {
name: "propertyinfoserializer_tests",
defaults: ["propertyinfoserializer_defaults"],
srcs: [
"trie_builder_test.cpp",
"property_info_serializer_test.cpp",
],
static_libs: ["libpropertyinfoserializer"],
}

View file

@ -0,0 +1,47 @@
//
// Copyright (C) 2017 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.
//
#ifndef PROPERTY_INFO_SERIALIZER_H
#define PROPERTY_INFO_SERIALIZER_H
#include <string>
#include <vector>
namespace android {
namespace properties {
struct PropertyInfoEntry {
PropertyInfoEntry() {}
template <typename T, typename U, typename V>
PropertyInfoEntry(T&& name, U&& context, V&& schema, bool exact_match)
: name(std::forward<T>(name)),
context(std::forward<U>(context)),
schema(std::forward<V>(schema)),
exact_match(exact_match) {}
std::string name;
std::string context;
std::string schema;
bool exact_match;
};
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
const std::string& default_context, const std::string& default_schema,
std::string* serialized_trie, std::string* error);
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,47 @@
//
// Copyright (C) 2017 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 "property_info_serializer/property_info_serializer.h"
#include "property_info_parser/property_info_parser.h"
#include <set>
#include "trie_builder.h"
#include "trie_serializer.h"
namespace android {
namespace properties {
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
const std::string& default_context, const std::string& default_schema,
std::string* serialized_trie, std::string* error) {
// Check that names are legal first
auto trie_builder = TrieBuilder(default_context, default_schema);
for (const auto& [name, context, schema, is_exact] : property_info) {
if (!trie_builder.AddToTrie(name, context, schema, is_exact, error)) {
return false;
}
}
auto trie_serializer = TrieSerializer();
*serialized_trie = trie_serializer.SerializeTrie(trie_builder);
return true;
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,750 @@
//
// Copyright (C) 2017 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 "property_info_serializer/property_info_serializer.h"
#include "property_info_parser/property_info_parser.h"
#include <gtest/gtest.h>
namespace android {
namespace properties {
TEST(propertyinfoserializer, TrieNodeCheck) {
auto property_info = std::vector<PropertyInfoEntry>{
{"test.", "1st", "1st", false}, {"test.test", "2nd", "2nd", false},
{"test.test1", "3rd", "3rd", true}, {"test.test2", "3rd", "3rd", true},
{"test.test3", "3rd", "3rd", true}, {"this.is.a.long.string", "4th", "4th", true},
};
auto serialized_trie = std::string();
auto build_trie_error = std::string();
ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
<< build_trie_error;
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
// Initial checks for property area.
EXPECT_EQ(1U, property_info_area->current_version());
EXPECT_EQ(1U, property_info_area->minimum_supported_version());
// Check the root node
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
EXPECT_EQ(0U, root_node.num_prefixes());
EXPECT_EQ(0U, root_node.num_exact_matches());
ASSERT_EQ(2U, root_node.num_child_nodes());
// Check the 'test'. node
TrieNode test_node;
ASSERT_TRUE(root_node.FindChildForString("test", 4, &test_node));
EXPECT_STREQ("test", test_node.name());
EXPECT_STREQ("1st", property_info_area->context(test_node.context_index()));
EXPECT_STREQ("1st", property_info_area->schema(test_node.schema_index()));
EXPECT_EQ(0U, test_node.num_child_nodes());
EXPECT_EQ(1U, test_node.num_prefixes());
{
auto prefix = test_node.prefix(0);
EXPECT_STREQ("test", serialized_trie.data() + prefix->name_offset);
EXPECT_EQ(4U, prefix->namelen);
EXPECT_STREQ("2nd", property_info_area->context(prefix->context_index));
EXPECT_STREQ("2nd", property_info_area->schema(prefix->schema_index));
}
EXPECT_EQ(3U, test_node.num_exact_matches());
{
auto match1 = test_node.exact_match(0);
auto match2 = test_node.exact_match(1);
auto match3 = test_node.exact_match(2);
EXPECT_STREQ("test1", serialized_trie.data() + match1->name_offset);
EXPECT_STREQ("test2", serialized_trie.data() + match2->name_offset);
EXPECT_STREQ("test3", serialized_trie.data() + match3->name_offset);
EXPECT_STREQ("3rd", property_info_area->context(match1->context_index));
EXPECT_STREQ("3rd", property_info_area->context(match2->context_index));
EXPECT_STREQ("3rd", property_info_area->context(match3->context_index));
EXPECT_STREQ("3rd", property_info_area->schema(match1->schema_index));
EXPECT_STREQ("3rd", property_info_area->schema(match2->schema_index));
EXPECT_STREQ("3rd", property_info_area->schema(match3->schema_index));
}
// Check the long string node
auto expect_empty_one_child = [](auto& node) {
EXPECT_EQ(-1U, node.context_index());
EXPECT_EQ(0U, node.num_prefixes());
EXPECT_EQ(0U, node.num_exact_matches());
EXPECT_EQ(1U, node.num_child_nodes());
};
// Start with 'this'
TrieNode long_string_node;
ASSERT_TRUE(root_node.FindChildForString("this", 4, &long_string_node));
expect_empty_one_child(long_string_node);
// Move to 'is'
ASSERT_TRUE(long_string_node.FindChildForString("is", 2, &long_string_node));
expect_empty_one_child(long_string_node);
// Move to 'a'
ASSERT_TRUE(long_string_node.FindChildForString("a", 1, &long_string_node));
expect_empty_one_child(long_string_node);
// Move to 'long'
ASSERT_TRUE(long_string_node.FindChildForString("long", 4, &long_string_node));
EXPECT_EQ(0U, long_string_node.num_prefixes());
EXPECT_EQ(1U, long_string_node.num_exact_matches());
EXPECT_EQ(0U, long_string_node.num_child_nodes());
auto final_match = long_string_node.exact_match(0);
EXPECT_STREQ("string", serialized_trie.data() + final_match->name_offset);
EXPECT_STREQ("4th", property_info_area->context(final_match->context_index));
EXPECT_STREQ("4th", property_info_area->schema(final_match->schema_index));
}
TEST(propertyinfoserializer, GetPropertyInfo) {
auto property_info = std::vector<PropertyInfoEntry>{
{"test.", "1st", "1st", false}, {"test.test", "2nd", "2nd", false},
{"test.test2.", "6th", "6th", false}, {"test.test", "5th", "5th", true},
{"test.test1", "3rd", "3rd", true}, {"test.test2", "7th", "7th", true},
{"test.test3", "3rd", "3rd", true}, {"this.is.a.long.string", "4th", "4th", true},
};
auto serialized_trie = std::string();
auto build_trie_error = std::string();
ASSERT_TRUE(BuildTrie(property_info, "default", "default", &serialized_trie, &build_trie_error))
<< build_trie_error;
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
// Sanity check
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
const char* context;
const char* schema;
property_info_area->GetPropertyInfo("abc", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("abc.abc", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("123.abc", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("test.a", &context, &schema);
EXPECT_STREQ("1st", context);
EXPECT_STREQ("1st", schema);
property_info_area->GetPropertyInfo("test.b", &context, &schema);
EXPECT_STREQ("1st", context);
EXPECT_STREQ("1st", schema);
property_info_area->GetPropertyInfo("test.c", &context, &schema);
EXPECT_STREQ("1st", context);
EXPECT_STREQ("1st", schema);
property_info_area->GetPropertyInfo("test.test", &context, &schema);
EXPECT_STREQ("5th", context);
EXPECT_STREQ("5th", schema);
property_info_area->GetPropertyInfo("test.testa", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.testb", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.testc", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.test.a", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.test.b", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.test.c", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.test1", &context, &schema);
EXPECT_STREQ("3rd", context);
EXPECT_STREQ("3rd", schema);
property_info_area->GetPropertyInfo("test.test2", &context, &schema);
EXPECT_STREQ("7th", context);
EXPECT_STREQ("7th", schema);
property_info_area->GetPropertyInfo("test.test3", &context, &schema);
EXPECT_STREQ("3rd", context);
EXPECT_STREQ("3rd", schema);
property_info_area->GetPropertyInfo("test.test11", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.test22", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("test.test33", &context, &schema);
EXPECT_STREQ("2nd", context);
EXPECT_STREQ("2nd", schema);
property_info_area->GetPropertyInfo("this.is.a.long.string", &context, &schema);
EXPECT_STREQ("4th", context);
EXPECT_STREQ("4th", schema);
property_info_area->GetPropertyInfo("this.is.a.long", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("this.is.a", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("this.is", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("this", &context, &schema);
EXPECT_STREQ("default", context);
EXPECT_STREQ("default", schema);
property_info_area->GetPropertyInfo("test.test2.a", &context, &schema);
EXPECT_STREQ("6th", context);
EXPECT_STREQ("6th", schema);
}
TEST(propertyinfoserializer, RealProperties) {
auto property_info = std::vector<PropertyInfoEntry>{
// Contexts from system/sepolicy/private/property_contexts
{"net.rmnet", "u:object_r:net_radio_prop:s0", "string", false},
{"net.gprs", "u:object_r:net_radio_prop:s0", "string", false},
{"net.ppp", "u:object_r:net_radio_prop:s0", "string", false},
{"net.qmi", "u:object_r:net_radio_prop:s0", "string", false},
{"net.lte", "u:object_r:net_radio_prop:s0", "string", false},
{"net.cdma", "u:object_r:net_radio_prop:s0", "string", false},
{"net.dns", "u:object_r:net_dns_prop:s0", "string", false},
{"sys.usb.config", "u:object_r:system_radio_prop:s0", "string", false},
{"ril.", "u:object_r:radio_prop:s0", "string", false},
{"ro.ril.", "u:object_r:radio_prop:s0", "string", false},
{"gsm.", "u:object_r:radio_prop:s0", "string", false},
{"persist.radio", "u:object_r:radio_prop:s0", "string", false},
{"net.", "u:object_r:system_prop:s0", "string", false},
{"dev.", "u:object_r:system_prop:s0", "string", false},
{"ro.runtime.", "u:object_r:system_prop:s0", "string", false},
{"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0", "string", false},
{"hw.", "u:object_r:system_prop:s0", "string", false},
{"ro.hw.", "u:object_r:system_prop:s0", "string", false},
{"sys.", "u:object_r:system_prop:s0", "string", false},
{"sys.cppreopt", "u:object_r:cppreopt_prop:s0", "string", false},
{"sys.powerctl", "u:object_r:powerctl_prop:s0", "string", false},
{"sys.usb.ffs.", "u:object_r:ffs_prop:s0", "string", false},
{"service.", "u:object_r:system_prop:s0", "string", false},
{"dhcp.", "u:object_r:dhcp_prop:s0", "string", false},
{"dhcp.bt-pan.result", "u:object_r:pan_result_prop:s0", "string", false},
{"bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
{"debug.", "u:object_r:debug_prop:s0", "string", false},
{"debug.db.", "u:object_r:debuggerd_prop:s0", "string", false},
{"dumpstate.", "u:object_r:dumpstate_prop:s0", "string", false},
{"dumpstate.options", "u:object_r:dumpstate_options_prop:s0", "string", false},
{"log.", "u:object_r:log_prop:s0", "string", false},
{"log.tag", "u:object_r:log_tag_prop:s0", "string", false},
{"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0", "string", false},
{"security.perf_harden", "u:object_r:shell_prop:s0", "string", false},
{"service.adb.root", "u:object_r:shell_prop:s0", "string", false},
{"service.adb.tcp.port", "u:object_r:shell_prop:s0", "string", false},
{"persist.audio.", "u:object_r:audio_prop:s0", "string", false},
{"persist.bluetooth.", "u:object_r:bluetooth_prop:s0", "string", false},
{"persist.debug.", "u:object_r:persist_debug_prop:s0", "string", false},
{"persist.logd.", "u:object_r:logd_prop:s0", "string", false},
{"persist.logd.security", "u:object_r:device_logging_prop:s0", "string", false},
{"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
{"logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0", "string", false},
{"persist.log.tag", "u:object_r:log_tag_prop:s0", "string", false},
{"persist.mmc.", "u:object_r:mmc_prop:s0", "string", false},
{"persist.netd.stable_secret", "u:object_r:netd_stable_secret_prop:s0", "string", false},
{"persist.sys.", "u:object_r:system_prop:s0", "string", false},
{"persist.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
{"ro.sys.safemode", "u:object_r:safemode_prop:s0", "string", false},
{"persist.sys.audit_safemode", "u:object_r:safemode_prop:s0", "string", false},
{"persist.service.", "u:object_r:system_prop:s0", "string", false},
{"persist.service.bdroid.", "u:object_r:bluetooth_prop:s0", "string", false},
{"persist.security.", "u:object_r:system_prop:s0", "string", false},
{"persist.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
{"ro.boot.vendor.overlay.", "u:object_r:overlay_prop:s0", "string", false},
{"ro.boottime.", "u:object_r:boottime_prop:s0", "string", false},
{"ro.serialno", "u:object_r:serialno_prop:s0", "string", false},
{"ro.boot.btmacaddr", "u:object_r:bluetooth_prop:s0", "string", false},
{"ro.boot.serialno", "u:object_r:serialno_prop:s0", "string", false},
{"ro.bt.", "u:object_r:bluetooth_prop:s0", "string", false},
{"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0", "string", false},
{"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0", "string", false},
{"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0", "string", false},
{"ro.device_owner", "u:object_r:device_logging_prop:s0", "string", false},
{"selinux.restorecon_recursive", "u:object_r:restorecon_prop:s0", "string", false},
{"vold.", "u:object_r:vold_prop:s0", "string", false},
{"ro.crypto.", "u:object_r:vold_prop:s0", "string", false},
{"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0", "string", false},
{"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0", "string",
false},
{"ctl.bootanim", "u:object_r:ctl_bootanim_prop:s0", "string", false},
{"ctl.dumpstate", "u:object_r:ctl_dumpstate_prop:s0", "string", false},
{"ctl.fuse_", "u:object_r:ctl_fuse_prop:s0", "string", false},
{"ctl.mdnsd", "u:object_r:ctl_mdnsd_prop:s0", "string", false},
{"ctl.ril-daemon", "u:object_r:ctl_rildaemon_prop:s0", "string", false},
{"ctl.bugreport", "u:object_r:ctl_bugreport_prop:s0", "string", false},
{"ctl.console", "u:object_r:ctl_console_prop:s0", "string", false},
{"ctl.", "u:object_r:ctl_default_prop:s0", "string", false},
{"nfc.", "u:object_r:nfc_prop:s0", "string", false},
{"config.", "u:object_r:config_prop:s0", "string", false},
{"ro.config.", "u:object_r:config_prop:s0", "string", false},
{"dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
{"ro.dalvik.", "u:object_r:dalvik_prop:s0", "string", false},
{"wlan.", "u:object_r:wifi_prop:s0", "string", false},
{"lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
{"ro.lowpan.", "u:object_r:lowpan_prop:s0", "string", false},
{"hwservicemanager.", "u:object_r:hwservicemanager_prop:s0", "string", false},
// Contexts from device/lge/bullhead/sepolicy/property_contexts
{"wc_transport.", "u:object_r:wc_transport_prop:s0", "string", false},
{"sys.listeners.", "u:object_r:qseecomtee_prop:s0", "string", false},
{"sys.keymaster.", "u:object_r:qseecomtee_prop:s0", "string", false},
{"radio.atfwd.", "u:object_r:radio_atfwd_prop:s0", "string", false},
{"sys.ims.", "u:object_r:qcom_ims_prop:s0", "string", false},
{"sensors.contexthub.", "u:object_r:contexthub_prop:s0", "string", false},
{"net.r_rmnet", "u:object_r:net_radio_prop:s0", "string", false},
};
auto serialized_trie = std::string();
auto build_trie_error = std::string();
ASSERT_TRUE(BuildTrie(property_info, "u:object_r:default_prop:s0", "string", &serialized_trie,
&build_trie_error))
<< build_trie_error;
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
auto properties_and_contexts = std::vector<std::pair<std::string, std::string>>{
// Actual properties on bullhead via `getprop -Z`
{"af.fast_track_multiplier", "u:object_r:default_prop:s0"},
{"audio_hal.period_size", "u:object_r:default_prop:s0"},
{"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
{"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapmaxfree", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapminfree", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapsize", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapstartsize", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm64.features", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm64.variant", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.lockprof.threshold", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.stack-trace-file", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.usejit", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.usejitprofiles", "u:object_r:dalvik_prop:s0"},
{"debug.atrace.tags.enableflags", "u:object_r:debug_prop:s0"},
{"debug.force_rtl", "u:object_r:debug_prop:s0"},
{"dev.bootcomplete", "u:object_r:system_prop:s0"},
{"drm.service.enabled", "u:object_r:default_prop:s0"},
{"gsm.current.phone-type", "u:object_r:radio_prop:s0"},
{"gsm.network.type", "u:object_r:radio_prop:s0"},
{"gsm.operator.alpha", "u:object_r:radio_prop:s0"},
{"gsm.operator.iso-country", "u:object_r:radio_prop:s0"},
{"gsm.operator.isroaming", "u:object_r:radio_prop:s0"},
{"gsm.operator.numeric", "u:object_r:radio_prop:s0"},
{"gsm.sim.operator.alpha", "u:object_r:radio_prop:s0"},
{"gsm.sim.operator.iso-country", "u:object_r:radio_prop:s0"},
{"gsm.sim.operator.numeric", "u:object_r:radio_prop:s0"},
{"gsm.sim.state", "u:object_r:radio_prop:s0"},
{"gsm.version.baseband", "u:object_r:radio_prop:s0"},
{"gsm.version.ril-impl", "u:object_r:radio_prop:s0"},
{"hwservicemanager.ready", "u:object_r:hwservicemanager_prop:s0"},
{"init.svc.adbd", "u:object_r:default_prop:s0"},
{"init.svc.atfwd", "u:object_r:default_prop:s0"},
{"init.svc.audioserver", "u:object_r:default_prop:s0"},
{"init.svc.bootanim", "u:object_r:default_prop:s0"},
{"init.svc.bullhead-sh", "u:object_r:default_prop:s0"},
{"init.svc.cameraserver", "u:object_r:default_prop:s0"},
{"init.svc.cnd", "u:object_r:default_prop:s0"},
{"init.svc.cnss-daemon", "u:object_r:default_prop:s0"},
{"init.svc.cnss_diag", "u:object_r:default_prop:s0"},
{"init.svc.configstore-hal-1-0", "u:object_r:default_prop:s0"},
{"init.svc.console", "u:object_r:default_prop:s0"},
{"init.svc.devstart_sh", "u:object_r:default_prop:s0"},
{"init.svc.drm", "u:object_r:default_prop:s0"},
{"init.svc.dumpstate-1-0", "u:object_r:default_prop:s0"},
{"init.svc.flash-nanohub-fw", "u:object_r:default_prop:s0"},
{"init.svc.fps_hal", "u:object_r:default_prop:s0"},
{"init.svc.gatekeeperd", "u:object_r:default_prop:s0"},
{"init.svc.gralloc-2-0", "u:object_r:default_prop:s0"},
{"init.svc.healthd", "u:object_r:default_prop:s0"},
{"init.svc.hidl_memory", "u:object_r:default_prop:s0"},
{"init.svc.hostapd", "u:object_r:default_prop:s0"},
{"init.svc.hwservicemanager", "u:object_r:default_prop:s0"},
{"init.svc.imsdatadaemon", "u:object_r:default_prop:s0"},
{"init.svc.imsqmidaemon", "u:object_r:default_prop:s0"},
{"init.svc.installd", "u:object_r:default_prop:s0"},
{"init.svc.irsc_util", "u:object_r:default_prop:s0"},
{"init.svc.keystore", "u:object_r:default_prop:s0"},
{"init.svc.lmkd", "u:object_r:default_prop:s0"},
{"init.svc.loc_launcher", "u:object_r:default_prop:s0"},
{"init.svc.logd", "u:object_r:default_prop:s0"},
{"init.svc.logd-reinit", "u:object_r:default_prop:s0"},
{"init.svc.media", "u:object_r:default_prop:s0"},
{"init.svc.mediadrm", "u:object_r:default_prop:s0"},
{"init.svc.mediaextractor", "u:object_r:default_prop:s0"},
{"init.svc.mediametrics", "u:object_r:default_prop:s0"},
{"init.svc.msm_irqbalance", "u:object_r:default_prop:s0"},
{"init.svc.netd", "u:object_r:default_prop:s0"},
{"init.svc.netmgrd", "u:object_r:default_prop:s0"},
{"init.svc.per_mgr", "u:object_r:default_prop:s0"},
{"init.svc.per_proxy", "u:object_r:default_prop:s0"},
{"init.svc.perfd", "u:object_r:default_prop:s0"},
{"init.svc.qcamerasvr", "u:object_r:default_prop:s0"},
{"init.svc.qmuxd", "u:object_r:default_prop:s0"},
{"init.svc.qseecomd", "u:object_r:default_prop:s0"},
{"init.svc.qti", "u:object_r:default_prop:s0"},
{"init.svc.ril-daemon", "u:object_r:default_prop:s0"},
{"init.svc.rmt_storage", "u:object_r:default_prop:s0"},
{"init.svc.servicemanager", "u:object_r:default_prop:s0"},
{"init.svc.ss_ramdump", "u:object_r:default_prop:s0"},
{"init.svc.start_hci_filter", "u:object_r:default_prop:s0"},
{"init.svc.storaged", "u:object_r:default_prop:s0"},
{"init.svc.surfaceflinger", "u:object_r:default_prop:s0"},
{"init.svc.thermal-engine", "u:object_r:default_prop:s0"},
{"init.svc.time_daemon", "u:object_r:default_prop:s0"},
{"init.svc.tombstoned", "u:object_r:default_prop:s0"},
{"init.svc.ueventd", "u:object_r:default_prop:s0"},
{"init.svc.update_engine", "u:object_r:default_prop:s0"},
{"init.svc.usb-hal-1-0", "u:object_r:default_prop:s0"},
{"init.svc.vndservicemanager", "u:object_r:default_prop:s0"},
{"init.svc.vold", "u:object_r:default_prop:s0"},
{"init.svc.webview_zygote32", "u:object_r:default_prop:s0"},
{"init.svc.wifi_hal_legacy", "u:object_r:default_prop:s0"},
{"init.svc.wificond", "u:object_r:default_prop:s0"},
{"init.svc.wpa_supplicant", "u:object_r:default_prop:s0"},
{"init.svc.zygote", "u:object_r:default_prop:s0"},
{"init.svc.zygote_secondary", "u:object_r:default_prop:s0"},
{"keyguard.no_require_sim", "u:object_r:default_prop:s0"},
{"log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0"},
{"logd.logpersistd.enable", "u:object_r:logpersistd_logging_prop:s0"},
{"media.aac_51_output_enabled", "u:object_r:default_prop:s0"},
{"media.recorder.show_manufacturer_and_model", "u:object_r:default_prop:s0"},
{"net.bt.name", "u:object_r:system_prop:s0"},
{"net.lte.ims.data.enabled", "u:object_r:net_radio_prop:s0"},
{"net.qtaguid_enabled", "u:object_r:system_prop:s0"},
{"net.tcp.default_init_rwnd", "u:object_r:system_prop:s0"},
{"nfc.initialized", "u:object_r:nfc_prop:s0"},
{"persist.audio.fluence.speaker", "u:object_r:audio_prop:s0"},
{"persist.audio.fluence.voicecall", "u:object_r:audio_prop:s0"},
{"persist.audio.fluence.voicecomm", "u:object_r:audio_prop:s0"},
{"persist.audio.fluence.voicerec", "u:object_r:audio_prop:s0"},
{"persist.camera.tnr.preview", "u:object_r:default_prop:s0"},
{"persist.camera.tnr.video", "u:object_r:default_prop:s0"},
{"persist.data.iwlan.enable", "u:object_r:default_prop:s0"},
{"persist.hwc.mdpcomp.enable", "u:object_r:default_prop:s0"},
{"persist.logd.logpersistd", "u:object_r:logpersistd_logging_prop:s0"},
{"persist.media.treble_omx", "u:object_r:default_prop:s0"},
{"persist.qcril.disable_retry", "u:object_r:default_prop:s0"},
{"persist.radio.adb_log_on", "u:object_r:radio_prop:s0"},
{"persist.radio.always_send_plmn", "u:object_r:radio_prop:s0"},
{"persist.radio.apm_sim_not_pwdn", "u:object_r:radio_prop:s0"},
{"persist.radio.custom_ecc", "u:object_r:radio_prop:s0"},
{"persist.radio.data_con_rprt", "u:object_r:radio_prop:s0"},
{"persist.radio.data_no_toggle", "u:object_r:radio_prop:s0"},
{"persist.radio.eons.enabled", "u:object_r:radio_prop:s0"},
{"persist.radio.eri64_as_home", "u:object_r:radio_prop:s0"},
{"persist.radio.mode_pref_nv10", "u:object_r:radio_prop:s0"},
{"persist.radio.process_sups_ind", "u:object_r:radio_prop:s0"},
{"persist.radio.redir_party_num", "u:object_r:radio_prop:s0"},
{"persist.radio.ril_payload_on", "u:object_r:radio_prop:s0"},
{"persist.radio.snapshot_enabled", "u:object_r:radio_prop:s0"},
{"persist.radio.snapshot_timer", "u:object_r:radio_prop:s0"},
{"persist.radio.use_cc_names", "u:object_r:radio_prop:s0"},
{"persist.speaker.prot.enable", "u:object_r:default_prop:s0"},
{"persist.sys.boot.reason", "u:object_r:last_boot_reason_prop:s0"},
{"persist.sys.dalvik.vm.lib.2", "u:object_r:system_prop:s0"},
{"persist.sys.debug.color_temp", "u:object_r:system_prop:s0"},
{"persist.sys.preloads.file_cache_expired", "u:object_r:system_prop:s0"},
{"persist.sys.timezone", "u:object_r:system_prop:s0"},
{"persist.sys.usb.config", "u:object_r:system_prop:s0"},
{"persist.sys.webview.vmsize", "u:object_r:system_prop:s0"},
{"persist.tom", "u:object_r:default_prop:s0"},
{"persist.tom2", "u:object_r:default_prop:s0"},
{"pm.dexopt.ab-ota", "u:object_r:default_prop:s0"},
{"pm.dexopt.bg-dexopt", "u:object_r:default_prop:s0"},
{"pm.dexopt.boot", "u:object_r:default_prop:s0"},
{"pm.dexopt.first-boot", "u:object_r:default_prop:s0"},
{"pm.dexopt.install", "u:object_r:default_prop:s0"},
{"qcom.bluetooth.soc", "u:object_r:default_prop:s0"},
{"radio.atfwd.start", "u:object_r:radio_atfwd_prop:s0"},
{"ril.ecclist", "u:object_r:radio_prop:s0"},
{"ril.nosim.ecc_list_1", "u:object_r:radio_prop:s0"},
{"ril.nosim.ecc_list_count", "u:object_r:radio_prop:s0"},
{"ril.qcril_pre_init_lock_held", "u:object_r:radio_prop:s0"},
{"rild.libpath", "u:object_r:default_prop:s0"},
{"ro.allow.mock.location", "u:object_r:default_prop:s0"},
{"ro.audio.flinger_standbytime_ms", "u:object_r:default_prop:s0"},
{"ro.baseband", "u:object_r:default_prop:s0"},
{"ro.bionic.ld.warning", "u:object_r:default_prop:s0"},
{"ro.board.platform", "u:object_r:default_prop:s0"},
{"ro.boot.baseband", "u:object_r:default_prop:s0"},
{"ro.boot.bootloader", "u:object_r:default_prop:s0"},
{"ro.boot.bootreason", "u:object_r:bootloader_boot_reason_prop:s0"},
{"ro.boot.dlcomplete", "u:object_r:default_prop:s0"},
{"ro.boot.emmc", "u:object_r:default_prop:s0"},
{"ro.boot.flash.locked", "u:object_r:default_prop:s0"},
{"ro.boot.hardware", "u:object_r:default_prop:s0"},
{"ro.boot.hardware.sku", "u:object_r:default_prop:s0"},
{"ro.boot.revision", "u:object_r:default_prop:s0"},
{"ro.boot.serialno", "u:object_r:serialno_prop:s0"},
{"ro.boot.verifiedbootstate", "u:object_r:default_prop:s0"},
{"ro.boot.veritymode", "u:object_r:default_prop:s0"},
{"ro.boot.wificountrycode", "u:object_r:default_prop:s0"},
{"ro.bootimage.build.date", "u:object_r:default_prop:s0"},
{"ro.bootimage.build.date.utc", "u:object_r:default_prop:s0"},
{"ro.bootimage.build.fingerprint", "u:object_r:default_prop:s0"},
{"ro.bootloader", "u:object_r:default_prop:s0"},
{"ro.bootmode", "u:object_r:default_prop:s0"},
{"ro.boottime.adbd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.atfwd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.audioserver", "u:object_r:boottime_prop:s0"},
{"ro.boottime.bootanim", "u:object_r:boottime_prop:s0"},
{"ro.boottime.bullhead-sh", "u:object_r:boottime_prop:s0"},
{"ro.boottime.cameraserver", "u:object_r:boottime_prop:s0"},
{"ro.boottime.cnd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.cnss-daemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.cnss_diag", "u:object_r:boottime_prop:s0"},
{"ro.boottime.configstore-hal-1-0", "u:object_r:boottime_prop:s0"},
{"ro.boottime.console", "u:object_r:boottime_prop:s0"},
{"ro.boottime.devstart_sh", "u:object_r:boottime_prop:s0"},
{"ro.boottime.drm", "u:object_r:boottime_prop:s0"},
{"ro.boottime.dumpstate-1-0", "u:object_r:boottime_prop:s0"},
{"ro.boottime.flash-nanohub-fw", "u:object_r:boottime_prop:s0"},
{"ro.boottime.fps_hal", "u:object_r:boottime_prop:s0"},
{"ro.boottime.gatekeeperd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.gralloc-2-0", "u:object_r:boottime_prop:s0"},
{"ro.boottime.healthd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.hidl_memory", "u:object_r:boottime_prop:s0"},
{"ro.boottime.hwservicemanager", "u:object_r:boottime_prop:s0"},
{"ro.boottime.imsdatadaemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.imsqmidaemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.cold_boot_wait", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.mount_all.default", "u:object_r:boottime_prop:s0"},
{"ro.boottime.init.selinux", "u:object_r:boottime_prop:s0"},
{"ro.boottime.installd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.irsc_util", "u:object_r:boottime_prop:s0"},
{"ro.boottime.keystore", "u:object_r:boottime_prop:s0"},
{"ro.boottime.lmkd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.loc_launcher", "u:object_r:boottime_prop:s0"},
{"ro.boottime.logd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.logd-reinit", "u:object_r:boottime_prop:s0"},
{"ro.boottime.media", "u:object_r:boottime_prop:s0"},
{"ro.boottime.mediadrm", "u:object_r:boottime_prop:s0"},
{"ro.boottime.mediaextractor", "u:object_r:boottime_prop:s0"},
{"ro.boottime.mediametrics", "u:object_r:boottime_prop:s0"},
{"ro.boottime.msm_irqbalance", "u:object_r:boottime_prop:s0"},
{"ro.boottime.netd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.netmgrd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.per_mgr", "u:object_r:boottime_prop:s0"},
{"ro.boottime.per_proxy", "u:object_r:boottime_prop:s0"},
{"ro.boottime.perfd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.qcamerasvr", "u:object_r:boottime_prop:s0"},
{"ro.boottime.qmuxd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.qseecomd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.qti", "u:object_r:boottime_prop:s0"},
{"ro.boottime.ril-daemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.rmt_storage", "u:object_r:boottime_prop:s0"},
{"ro.boottime.servicemanager", "u:object_r:boottime_prop:s0"},
{"ro.boottime.ss_ramdump", "u:object_r:boottime_prop:s0"},
{"ro.boottime.start_hci_filter", "u:object_r:boottime_prop:s0"},
{"ro.boottime.storaged", "u:object_r:boottime_prop:s0"},
{"ro.boottime.surfaceflinger", "u:object_r:boottime_prop:s0"},
{"ro.boottime.thermal-engine", "u:object_r:boottime_prop:s0"},
{"ro.boottime.time_daemon", "u:object_r:boottime_prop:s0"},
{"ro.boottime.tombstoned", "u:object_r:boottime_prop:s0"},
{"ro.boottime.ueventd", "u:object_r:boottime_prop:s0"},
{"ro.boottime.update_engine", "u:object_r:boottime_prop:s0"},
{"ro.boottime.usb-hal-1-0", "u:object_r:boottime_prop:s0"},
{"ro.boottime.vndservicemanager", "u:object_r:boottime_prop:s0"},
{"ro.boottime.vold", "u:object_r:boottime_prop:s0"},
{"ro.boottime.webview_zygote32", "u:object_r:boottime_prop:s0"},
{"ro.boottime.wifi_hal_legacy", "u:object_r:boottime_prop:s0"},
{"ro.boottime.wificond", "u:object_r:boottime_prop:s0"},
{"ro.boottime.zygote", "u:object_r:boottime_prop:s0"},
{"ro.boottime.zygote_secondary", "u:object_r:boottime_prop:s0"},
{"ro.bt.bdaddr_path", "u:object_r:bluetooth_prop:s0"},
{"ro.build.characteristics", "u:object_r:default_prop:s0"},
{"ro.build.date", "u:object_r:default_prop:s0"},
{"ro.build.date.utc", "u:object_r:default_prop:s0"},
{"ro.build.description", "u:object_r:default_prop:s0"},
{"ro.build.display.id", "u:object_r:default_prop:s0"},
{"ro.build.expect.baseband", "u:object_r:default_prop:s0"},
{"ro.build.expect.bootloader", "u:object_r:default_prop:s0"},
{"ro.build.fingerprint", "u:object_r:fingerprint_prop:s0"},
{"ro.build.flavor", "u:object_r:default_prop:s0"},
{"ro.build.host", "u:object_r:default_prop:s0"},
{"ro.build.id", "u:object_r:default_prop:s0"},
{"ro.build.product", "u:object_r:default_prop:s0"},
{"ro.build.tags", "u:object_r:default_prop:s0"},
{"ro.build.type", "u:object_r:default_prop:s0"},
{"ro.build.user", "u:object_r:default_prop:s0"},
{"ro.build.version.all_codenames", "u:object_r:default_prop:s0"},
{"ro.build.version.base_os", "u:object_r:default_prop:s0"},
{"ro.build.version.codename", "u:object_r:default_prop:s0"},
{"ro.build.version.incremental", "u:object_r:default_prop:s0"},
{"ro.build.version.preview_sdk", "u:object_r:default_prop:s0"},
{"ro.build.version.release", "u:object_r:default_prop:s0"},
{"ro.build.version.sdk", "u:object_r:default_prop:s0"},
{"ro.build.version.security_patch", "u:object_r:default_prop:s0"},
{"ro.camera.notify_nfc", "u:object_r:default_prop:s0"},
{"ro.carrier", "u:object_r:default_prop:s0"},
{"ro.com.android.dataroaming", "u:object_r:default_prop:s0"},
{"ro.config.alarm_alert", "u:object_r:config_prop:s0"},
{"ro.config.notification_sound", "u:object_r:config_prop:s0"},
{"ro.config.ringtone", "u:object_r:config_prop:s0"},
{"ro.config.vc_call_vol_steps", "u:object_r:config_prop:s0"},
{"ro.crypto.fs_crypto_blkdev", "u:object_r:vold_prop:s0"},
{"ro.crypto.state", "u:object_r:vold_prop:s0"},
{"ro.crypto.type", "u:object_r:vold_prop:s0"},
{"ro.dalvik.vm.native.bridge", "u:object_r:dalvik_prop:s0"},
{"ro.debuggable", "u:object_r:default_prop:s0"},
{"ro.device_owner", "u:object_r:device_logging_prop:s0"},
{"ro.expect.recovery_id", "u:object_r:default_prop:s0"},
{"ro.frp.pst", "u:object_r:default_prop:s0"},
{"ro.hardware", "u:object_r:default_prop:s0"},
{"ro.hwui.drop_shadow_cache_size", "u:object_r:default_prop:s0"},
{"ro.hwui.gradient_cache_size", "u:object_r:default_prop:s0"},
{"ro.hwui.layer_cache_size", "u:object_r:default_prop:s0"},
{"ro.hwui.path_cache_size", "u:object_r:default_prop:s0"},
{"ro.hwui.r_buffer_cache_size", "u:object_r:default_prop:s0"},
{"ro.hwui.text_large_cache_height", "u:object_r:default_prop:s0"},
{"ro.hwui.text_large_cache_width", "u:object_r:default_prop:s0"},
{"ro.hwui.text_small_cache_height", "u:object_r:default_prop:s0"},
{"ro.hwui.text_small_cache_width", "u:object_r:default_prop:s0"},
{"ro.hwui.texture_cache_flushrate", "u:object_r:default_prop:s0"},
{"ro.hwui.texture_cache_size", "u:object_r:default_prop:s0"},
{"ro.min_freq_0", "u:object_r:default_prop:s0"},
{"ro.min_freq_4", "u:object_r:default_prop:s0"},
{"ro.oem_unlock_supported", "u:object_r:default_prop:s0"},
{"ro.opengles.version", "u:object_r:default_prop:s0"},
{"ro.persistent_properties.ready", "u:object_r:persistent_properties_ready_prop:s0"},
{"ro.product.board", "u:object_r:default_prop:s0"},
{"ro.product.brand", "u:object_r:default_prop:s0"},
{"ro.product.cpu.abi", "u:object_r:default_prop:s0"},
{"ro.product.cpu.abilist", "u:object_r:default_prop:s0"},
{"ro.product.cpu.abilist32", "u:object_r:default_prop:s0"},
{"ro.product.cpu.abilist64", "u:object_r:default_prop:s0"},
{"ro.product.device", "u:object_r:default_prop:s0"},
{"ro.product.first_api_level", "u:object_r:default_prop:s0"},
{"ro.product.locale", "u:object_r:default_prop:s0"},
{"ro.product.manufacturer", "u:object_r:default_prop:s0"},
{"ro.product.model", "u:object_r:default_prop:s0"},
{"ro.product.name", "u:object_r:default_prop:s0"},
{"ro.property_service.version", "u:object_r:default_prop:s0"},
{"ro.qc.sdk.audio.fluencetype", "u:object_r:default_prop:s0"},
{"ro.recovery_id", "u:object_r:default_prop:s0"},
{"ro.revision", "u:object_r:default_prop:s0"},
{"ro.ril.svdo", "u:object_r:radio_prop:s0"},
{"ro.ril.svlte1x", "u:object_r:radio_prop:s0"},
{"ro.runtime.firstboot", "u:object_r:firstboot_prop:s0"},
{"ro.secure", "u:object_r:default_prop:s0"},
{"ro.serialno", "u:object_r:serialno_prop:s0"},
{"ro.sf.lcd_density", "u:object_r:default_prop:s0"},
{"ro.telephony.call_ring.multiple", "u:object_r:default_prop:s0"},
{"ro.telephony.default_cdma_sub", "u:object_r:default_prop:s0"},
{"ro.telephony.default_network", "u:object_r:default_prop:s0"},
{"ro.treble.enabled", "u:object_r:default_prop:s0"},
{"ro.vendor.build.date", "u:object_r:default_prop:s0"},
{"ro.vendor.build.date.utc", "u:object_r:default_prop:s0"},
{"ro.vendor.build.fingerprint", "u:object_r:default_prop:s0"},
{"ro.vendor.extension_library", "u:object_r:default_prop:s0"},
{"ro.wifi.channels", "u:object_r:default_prop:s0"},
{"ro.zygote", "u:object_r:default_prop:s0"},
{"security.perf_harden", "u:object_r:shell_prop:s0"},
{"sensors.contexthub.lid_state", "u:object_r:contexthub_prop:s0"},
{"service.adb.root", "u:object_r:shell_prop:s0"},
{"service.bootanim.exit", "u:object_r:system_prop:s0"},
{"service.sf.present_timestamp", "u:object_r:system_prop:s0"},
{"sys.boot.reason", "u:object_r:system_boot_reason_prop:s0"},
{"sys.boot_completed", "u:object_r:system_prop:s0"},
{"sys.ims.QMI_DAEMON_STATUS", "u:object_r:qcom_ims_prop:s0"},
{"sys.listeners.registered", "u:object_r:qseecomtee_prop:s0"},
{"sys.logbootcomplete", "u:object_r:system_prop:s0"},
{"sys.oem_unlock_allowed", "u:object_r:system_prop:s0"},
{"sys.qcom.devup", "u:object_r:system_prop:s0"},
{"sys.sysctl.extra_free_kbytes", "u:object_r:system_prop:s0"},
{"sys.usb.config", "u:object_r:system_radio_prop:s0"},
{"sys.usb.configfs", "u:object_r:system_radio_prop:s0"},
{"sys.usb.controller", "u:object_r:system_prop:s0"},
{"sys.usb.ffs.aio_compat", "u:object_r:ffs_prop:s0"},
{"sys.usb.ffs.max_read", "u:object_r:ffs_prop:s0"},
{"sys.usb.ffs.max_write", "u:object_r:ffs_prop:s0"},
{"sys.usb.ffs.ready", "u:object_r:ffs_prop:s0"},
{"sys.usb.mtp.device_type", "u:object_r:system_prop:s0"},
{"sys.usb.state", "u:object_r:system_prop:s0"},
{"telephony.lteOnCdmaDevice", "u:object_r:default_prop:s0"},
{"tombstoned.max_tombstone_count", "u:object_r:default_prop:s0"},
{"vidc.debug.perf.mode", "u:object_r:default_prop:s0"},
{"vidc.enc.dcvs.extra-buff-count", "u:object_r:default_prop:s0"},
{"vold.decrypt", "u:object_r:vold_prop:s0"},
{"vold.has_adoptable", "u:object_r:vold_prop:s0"},
{"vold.post_fs_data_done", "u:object_r:vold_prop:s0"},
{"wc_transport.clean_up", "u:object_r:wc_transport_prop:s0"},
{"wc_transport.hci_filter_status", "u:object_r:wc_transport_prop:s0"},
{"wc_transport.ref_count", "u:object_r:wc_transport_prop:s0"},
{"wc_transport.soc_initialized", "u:object_r:wc_transport_prop:s0"},
{"wc_transport.start_hci", "u:object_r:wc_transport_prop:s0"},
{"wc_transport.vnd_power", "u:object_r:wc_transport_prop:s0"},
{"wifi.interface", "u:object_r:default_prop:s0"},
{"wifi.supplicant_scan_interval", "u:object_r:default_prop:s0"},
};
for (const auto& [property, context] : properties_and_contexts) {
const char* returned_context;
property_info_area->GetPropertyInfo(property.c_str(), &returned_context, nullptr);
EXPECT_EQ(context, returned_context) << property;
}
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,105 @@
//
// Copyright (C) 2017 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 "trie_builder.h"
#include <android-base/strings.h>
using android::base::Split;
namespace android {
namespace properties {
TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_schema)
: builder_root_("root") {
auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
builder_root_.set_context(context_pointer);
auto* schema_pointer = StringPointerFromContainer(default_schema, &schemas_);
builder_root_.set_schema(schema_pointer);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
const std::string& schema, bool exact, std::string* error) {
auto* context_pointer = StringPointerFromContainer(context, &contexts_);
auto* schema_pointer = StringPointerFromContainer(schema, &schemas_);
return AddToTrie(name, context_pointer, schema_pointer, exact, error);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
const std::string* schema, bool exact, std::string* error) {
TrieBuilderNode* current_node = &builder_root_;
auto name_pieces = Split(name, ".");
bool ends_with_dot = false;
if (name_pieces.back().empty()) {
ends_with_dot = true;
name_pieces.pop_back();
}
// Move us to the final node that we care about, adding incremental nodes if necessary.
while (name_pieces.size() > 1) {
auto child = current_node->FindChild(name_pieces.front());
if (child == nullptr) {
child = current_node->AddChild(name_pieces.front());
}
if (child == nullptr) {
*error = "Unable to allocate Trie node";
return false;
}
current_node = child;
name_pieces.erase(name_pieces.begin());
}
// Store our context based on what type of match it is.
if (exact) {
if (!current_node->AddExactMatchContext(name_pieces.front(), context, schema)) {
*error = "Duplicate exact match detected for '" + name + "'";
return false;
}
} else if (!ends_with_dot) {
if (!current_node->AddPrefixContext(name_pieces.front(), context, schema)) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
} else {
auto child = current_node->FindChild(name_pieces.front());
if (child == nullptr) {
child = current_node->AddChild(name_pieces.front());
}
if (child == nullptr) {
*error = "Unable to allocate Trie node";
return false;
}
if (child->context() != nullptr || child->schema() != nullptr) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
child->set_context(context);
child->set_schema(schema);
}
return true;
}
const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,
std::set<std::string>* container) {
// Get a pointer to the string in a given set, such that we only ever serialize each string once.
auto [iterator, _] = container->emplace(string);
return &(*iterator);
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,124 @@
//
// Copyright (C) 2017 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.
//
#ifndef PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
#include <memory>
#include <set>
#include <string>
#include <vector>
namespace android {
namespace properties {
struct PropertyEntryBuilder {
PropertyEntryBuilder() : context(nullptr), schema(nullptr) {}
PropertyEntryBuilder(const std::string& name, const std::string* context,
const std::string* schema)
: name(name), context(context), schema(schema) {}
std::string name;
const std::string* context;
const std::string* schema;
};
class TrieBuilderNode {
public:
TrieBuilderNode(const std::string& name) : property_entry_(name, nullptr, nullptr) {}
TrieBuilderNode* FindChild(const std::string& name) {
for (auto& child : children_) {
if (child.name() == name) return &child;
}
return nullptr;
}
const TrieBuilderNode* FindChild(const std::string& name) const {
for (const auto& child : children_) {
if (child.name() == name) return &child;
}
return nullptr;
}
TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
bool AddPrefixContext(const std::string& prefix, const std::string* context,
const std::string* schema) {
if (std::find_if(prefixes_.begin(), prefixes_.end(),
[&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {
return false;
}
prefixes_.emplace_back(prefix, context, schema);
return true;
}
bool AddExactMatchContext(const std::string& exact_match, const std::string* context,
const std::string* schema) {
if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {
return t.name == exact_match;
}) != exact_matches_.end()) {
return false;
}
exact_matches_.emplace_back(exact_match, context, schema);
return true;
}
const std::string& name() const { return property_entry_.name; }
const std::string* context() const { return property_entry_.context; }
void set_context(const std::string* context) { property_entry_.context = context; }
const std::string* schema() const { return property_entry_.schema; }
void set_schema(const std::string* schema) { property_entry_.schema = schema; }
const PropertyEntryBuilder property_entry() const { return property_entry_; }
const std::vector<TrieBuilderNode>& children() const { return children_; }
const std::vector<PropertyEntryBuilder>& prefixes() const { return prefixes_; }
const std::vector<PropertyEntryBuilder>& exact_matches() const { return exact_matches_; }
private:
PropertyEntryBuilder property_entry_;
std::vector<TrieBuilderNode> children_;
std::vector<PropertyEntryBuilder> prefixes_;
std::vector<PropertyEntryBuilder> exact_matches_;
};
class TrieBuilder {
public:
TrieBuilder(const std::string& default_context, const std::string& default_schema);
bool AddToTrie(const std::string& name, const std::string& context, const std::string& schema,
bool exact, std::string* error);
const TrieBuilderNode builder_root() const { return builder_root_; }
const std::set<std::string>& contexts() const { return contexts_; }
const std::set<std::string>& schemas() const { return schemas_; }
private:
bool AddToTrie(const std::string& name, const std::string* context, const std::string* schema,
bool exact, std::string* error);
const std::string* StringPointerFromContainer(const std::string& string,
std::set<std::string>* container);
TrieBuilderNode builder_root_;
std::set<std::string> contexts_;
std::set<std::string> schemas_;
};
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,129 @@
//
// Copyright (C) 2017 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 "trie_builder.h"
#include <gtest/gtest.h>
namespace android {
namespace properties {
TEST(propertyinfoserializer, BuildTrie_Simple) {
auto trie_builder = TrieBuilder("default", "default_schema");
// Add test data to tree
auto error = std::string();
EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_schema", false, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_schema", false, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_schema", true, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_schema", true, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_schema", true, &error));
EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_schema", true, &error));
ASSERT_EQ(5U, trie_builder.contexts().size());
ASSERT_EQ(5U, trie_builder.schemas().size());
auto& builder_root = trie_builder.builder_root();
// Check the root node
EXPECT_EQ("root", builder_root.name());
ASSERT_NE(nullptr, builder_root.context());
EXPECT_EQ("default", *builder_root.context());
ASSERT_NE(nullptr, builder_root.schema());
EXPECT_EQ("default_schema", *builder_root.schema());
EXPECT_EQ(0U, builder_root.prefixes().size());
EXPECT_EQ(0U, builder_root.exact_matches().size());
ASSERT_EQ(2U, builder_root.children().size());
// Check the 'test.' node
auto* test_node = builder_root.FindChild("test");
EXPECT_EQ("test", test_node->name());
ASSERT_NE(nullptr, test_node->context());
EXPECT_EQ("1st", *test_node->context());
ASSERT_NE(nullptr, test_node->schema());
EXPECT_EQ("1st_schema", *test_node->schema());
EXPECT_EQ(0U, test_node->children().size());
EXPECT_EQ(1U, test_node->prefixes().size());
{
auto& property_entry = test_node->prefixes()[0];
EXPECT_EQ("test", property_entry.name);
ASSERT_NE(nullptr, property_entry.context);
EXPECT_EQ("2nd", *property_entry.context);
ASSERT_NE(nullptr, property_entry.schema);
EXPECT_EQ("2nd_schema", *property_entry.schema);
}
EXPECT_EQ(3U, test_node->exact_matches().size());
EXPECT_EQ("test1", test_node->exact_matches()[0].name);
EXPECT_EQ("test2", test_node->exact_matches()[1].name);
EXPECT_EQ("test3", test_node->exact_matches()[2].name);
ASSERT_NE(nullptr, test_node->exact_matches()[0].context);
ASSERT_NE(nullptr, test_node->exact_matches()[1].context);
ASSERT_NE(nullptr, test_node->exact_matches()[2].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[0].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[1].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[2].context);
ASSERT_NE(nullptr, test_node->exact_matches()[0].schema);
ASSERT_NE(nullptr, test_node->exact_matches()[1].schema);
ASSERT_NE(nullptr, test_node->exact_matches()[2].schema);
EXPECT_EQ("3rd_schema", *test_node->exact_matches()[0].schema);
EXPECT_EQ("3rd_schema", *test_node->exact_matches()[1].schema);
EXPECT_EQ("3rd_schema", *test_node->exact_matches()[2].schema);
// Check the long string node
auto expect_empty_one_child = [](auto* node) {
ASSERT_NE(nullptr, node);
EXPECT_EQ(nullptr, node->context());
EXPECT_EQ(nullptr, node->schema());
EXPECT_EQ(0U, node->prefixes().size());
EXPECT_EQ(0U, node->exact_matches().size());
EXPECT_EQ(1U, node->children().size());
};
// Start with 'this'
auto* long_string_node = builder_root.FindChild("this");
expect_empty_one_child(long_string_node);
// Move to 'is'
long_string_node = long_string_node->FindChild("is");
expect_empty_one_child(long_string_node);
// Move to 'a'
long_string_node = long_string_node->FindChild("a");
expect_empty_one_child(long_string_node);
// Move to 'long'
long_string_node = long_string_node->FindChild("long");
EXPECT_EQ(0U, long_string_node->prefixes().size());
EXPECT_EQ(1U, long_string_node->exact_matches().size());
EXPECT_EQ(0U, long_string_node->children().size());
{
auto& property_entry = long_string_node->exact_matches()[0];
EXPECT_EQ("string", property_entry.name);
ASSERT_NE(nullptr, property_entry.context);
EXPECT_EQ("4th", *property_entry.context);
ASSERT_NE(nullptr, property_entry.schema);
EXPECT_EQ("4th_schema", *property_entry.schema);
}
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,108 @@
//
// Copyright (C) 2017 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.
//
#ifndef PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
#include <string>
#include <vector>
namespace android {
namespace properties {
template <typename T>
class ArenaObjectPointer {
public:
ArenaObjectPointer(std::string& arena_data, uint32_t offset)
: arena_data_(arena_data), offset_(offset) {}
T* operator->() { return reinterpret_cast<T*>(arena_data_.data() + offset_); }
private:
std::string& arena_data_;
uint32_t offset_;
};
class TrieNodeArena {
public:
TrieNodeArena() : current_data_pointer_(0) {}
// We can't return pointers to objects since data_ may move when reallocated, thus invalidating
// any pointers. Therefore we return an ArenaObjectPointer, which always accesses elements via
// data_ + offset.
template <typename T>
ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {
uint32_t offset;
AllocateData(sizeof(T), &offset);
if (return_offset) *return_offset = offset;
return ArenaObjectPointer<T>(data_, offset);
}
uint32_t AllocateUint32Array(int length) {
uint32_t offset;
AllocateData(sizeof(uint32_t) * length, &offset);
return offset;
}
uint32_t* uint32_array(uint32_t offset) {
return reinterpret_cast<uint32_t*>(data_.data() + offset);
}
uint32_t AllocateAndWriteString(const std::string& string) {
uint32_t offset;
char* data = static_cast<char*>(AllocateData(string.size() + 1, &offset));
strcpy(data, string.c_str());
return offset;
}
void AllocateAndWriteUint32(uint32_t value) {
auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
*location = value;
}
void* AllocateData(size_t size, uint32_t* offset) {
size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
if (current_data_pointer_ + aligned_size > data_.size()) {
auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;
data_.resize(new_size, '\0');
}
if (offset) *offset = current_data_pointer_;
uint32_t return_offset = current_data_pointer_;
current_data_pointer_ += aligned_size;
return &data_[0] + return_offset;
}
uint32_t size() const { return current_data_pointer_; }
const std::string& data() const { return data_; }
std::string truncated_data() const {
auto result = data_;
result.resize(current_data_pointer_);
return result;
}
private:
std::string data_;
uint32_t current_data_pointer_;
};
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,142 @@
//
// Copyright (C) 2017 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 "trie_serializer.h"
namespace android {
namespace properties {
// Serialized strings contains:
// 1) A uint32_t count of elements in the below array
// 2) A sorted array of uint32_t offsets pointing to null terminated strings
// 3) Each of the null terminated strings themselves packed back to back
// This returns the offset into arena where the serialized strings start.
void TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {
arena_->AllocateAndWriteUint32(strings.size());
// Allocate space for the array.
uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());
// Write offset pointers and strings; these are already alphabetically sorted by virtue of being
// in an std::set.
auto it = strings.begin();
for (unsigned int i = 0; i < strings.size(); ++i, ++it) {
uint32_t string_offset = arena_->AllocateAndWriteString(*it);
arena_->uint32_array(offset_array_offset)[i] = string_offset;
}
}
uint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {
uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
? serialized_info()->FindContextIndex(property_entry.context->c_str())
: ~0u;
uint32_t schema_index = property_entry.schema != nullptr && !property_entry.schema->empty()
? serialized_info()->FindSchemaIndex(property_entry.schema->c_str())
: ~0u;
uint32_t offset;
auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
serialized_property_entry->namelen = property_entry.name.size();
serialized_property_entry->context_index = context_index;
serialized_property_entry->schema_index = schema_index;
return offset;
}
uint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {
uint32_t trie_offset;
auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);
trie->property_entry = WritePropertyEntry(builder_node.property_entry());
// Write prefix matches
auto sorted_prefix_matches = builder_node.prefixes();
// Prefixes are sorted by descending length
std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });
trie->num_prefixes = sorted_prefix_matches.size();
uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());
trie->prefix_entries = prefix_entries_array_offset;
for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {
uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);
arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;
}
// Write exact matches
auto sorted_exact_matches = builder_node.exact_matches();
// Exact matches are sorted alphabetically
std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });
trie->num_exact_matches = sorted_exact_matches.size();
uint32_t exact_match_entries_array_offset =
arena_->AllocateUint32Array(sorted_exact_matches.size());
trie->exact_match_entries = exact_match_entries_array_offset;
for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {
uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);
arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;
}
// Write children
auto sorted_children = builder_node.children();
std::sort(sorted_children.begin(), sorted_children.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });
trie->num_child_nodes = sorted_children.size();
uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());
trie->child_nodes = children_offset_array_offset;
for (unsigned int i = 0; i < sorted_children.size(); ++i) {
arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);
}
return trie_offset;
}
TrieSerializer::TrieSerializer() {}
std::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {
arena_.reset(new TrieNodeArena());
auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);
header->current_version = 1;
header->minimum_supported_version = 1;
// Store where we're about to write the contexts.
header->contexts_offset = arena_->size();
SerializeStrings(trie_builder.contexts());
// Store where we're about to write the schemas.
header->schemas_offset = arena_->size();
SerializeStrings(trie_builder.schemas());
// We need to store size() up to this point now for Find*Offset() to work.
header->size = arena_->size();
uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());
header->root_offset = root_trie_offset;
// Record the real size now that we've written everything
header->size = arena_->size();
return arena_->truncated_data();
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,55 @@
//
// Copyright (C) 2017 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.
//
#ifndef PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
#include <string>
#include <vector>
#include "property_info_parser/property_info_parser.h"
#include "trie_builder.h"
#include "trie_node_arena.h"
namespace android {
namespace properties {
class TrieSerializer {
public:
TrieSerializer();
std::string SerializeTrie(const TrieBuilder& trie_builder);
private:
void SerializeStrings(const std::set<std::string>& strings);
uint32_t WritePropertyEntry(const PropertyEntryBuilder& property_entry);
// Writes a new TrieNode to arena, and recursively writes its children.
// Returns the offset within arena.
uint32_t WriteTrieNode(const TrieBuilderNode& builder_node);
const PropertyInfoArea* serialized_info() const {
return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
}
std::unique_ptr<TrieNodeArena> arena_;
};
} // namespace properties
} // namespace android
#endif