diff --git a/init/Android.bp b/init/Android.bp index 0ec348cf0..2fea359a6 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -95,6 +95,8 @@ cc_library_static { "libprocessgroup", "libfs_mgr", "libprotobuf-cpp-lite", + "libpropertyinfoserializer", + "libpropertyinfoparser", ], include_dirs: [ "system/core/mkbootimg", @@ -193,6 +195,7 @@ cc_test { "libselinux", "libcrypto", "libprotobuf-cpp-lite", + "libpropertyinfoparser", ], } diff --git a/init/Android.mk b/init/Android.mk index 516f1b350..5239366f6 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -84,6 +84,8 @@ LOCAL_STATIC_LIBRARIES := \ libavb \ libkeyutils \ libprotobuf-cpp-lite \ + libpropertyinfoserializer \ + libpropertyinfoparser \ LOCAL_REQUIRED_MODULES := \ e2fsdroid \ diff --git a/init/property_service.cpp b/init/property_service.cpp index 3cf3ab9d0..4b6c502c0 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -50,17 +50,27 @@ #include #include #include +#include +#include #include #include #include #include "init.h" #include "persistent_properties.h" +#include "space_tokenizer.h" #include "util.h" +using android::base::ReadFileToString; +using android::base::Split; using android::base::StartsWith; using android::base::StringPrintf; using android::base::Timer; +using android::base::Trim; +using android::base::WriteStringToFile; +using android::properties::BuildTrie; +using android::properties::PropertyInfoAreaFile; +using android::properties::PropertyInfoEntry; #define RECOVERY_MOUNT_POINT "/recovery" @@ -71,27 +81,29 @@ static bool persistent_properties_loaded = false; static int property_set_fd = -1; -static struct selabel_handle* sehandle_prop; +static PropertyInfoAreaFile property_info_area; + +void CreateSerializedPropertyInfo(); void property_init() { + mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); + CreateSerializedPropertyInfo(); if (__system_property_area_init()) { LOG(FATAL) << "Failed to initialize property area"; } + if (!property_info_area.LoadDefaultPath()) { + LOG(FATAL) << "Failed to load serialized property info file"; + } } - static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) { - if (!sctx) { return false; } - if (!sehandle_prop) { - return false; - } - - char* tctx = nullptr; - if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) { - return false; + const char* target_context = nullptr; + property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr); + if (target_context == nullptr) { + return false; } property_audit_data audit_data; @@ -99,9 +111,9 @@ static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* c audit_data.name = name.c_str(); audit_data.cr = cr; - bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0); + bool has_access = + (selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0); - freecon(tctx); return has_access; } @@ -433,7 +445,7 @@ static void handle_property_set(SocketConnection& socket, std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid); std::string process_cmdline; std::string process_log_string; - if (android::base::ReadFileToString(cmdline_path, &process_cmdline)) { + if (ReadFileToString(cmdline_path, &process_cmdline)) { // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path. process_log_string = StringPrintf(" (%s)", process_cmdline.c_str()); } @@ -714,9 +726,80 @@ static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, return 0; } -void start_property_service() { - sehandle_prop = selinux_android_prop_context_handle(); +Result ParsePropertyInfoLine(const std::string& line) { + auto tokenizer = SpaceTokenizer(line); + auto property = tokenizer.GetNext(); + if (property.empty()) return Error() << "Did not find a property entry in '" << line << "'"; + + auto context = tokenizer.GetNext(); + if (context.empty()) return Error() << "Did not find a context entry in '" << line << "'"; + + // It is not an error to not find these, as older files will not contain them. + auto exact_match = tokenizer.GetNext(); + auto schema = tokenizer.GetRemaining(); + + return {property, context, schema, exact_match == "exact"}; +} + +bool LoadPropertyInfoFromFile(const std::string& filename, + std::vector* property_infos) { + auto file_contents = std::string(); + if (!ReadFileToString(filename, &file_contents)) { + PLOG(ERROR) << "Could not read properties from '" << filename << "'"; + return false; + } + + for (const auto& line : Split(file_contents, "\n")) { + auto trimmed_line = Trim(line); + if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) { + continue; + } + + auto property_info = ParsePropertyInfoLine(line); + if (!property_info) { + LOG(ERROR) << "Could not read line from '" << filename << "': " << property_info.error(); + continue; + } + + property_infos->emplace_back(*property_info); + } + return true; +} + +void CreateSerializedPropertyInfo() { + auto property_infos = std::vector(); + if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) { + if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts", + &property_infos)) { + return; + } + // Don't check for failure here, so we always have a sane list of properties. + // E.g. In case of recovery, the vendor partition will not have mounted and we + // still need the system / platform properties to function. + LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts", &property_infos); + } else { + if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) { + return; + } + LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos); + } + auto serialized_contexts = std::string(); + auto error = std::string(); + if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts, + &error)) { + LOG(ERROR) << "Unable to serialize property contexts: " << error; + return; + } + + constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info"; + if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) { + PLOG(ERROR) << "Unable to write serialized property infos to file"; + } + selinux_android_restorecon(kPropertyInfosPath, 0); +} + +void start_property_service() { selinux_callback cb; cb.func_audit = SelinuxAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb); diff --git a/init/space_tokenizer.h b/init/space_tokenizer.h new file mode 100644 index 000000000..e7e22c551 --- /dev/null +++ b/init/space_tokenizer.h @@ -0,0 +1,50 @@ +/* + * 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 _INIT_SPACE_TOKENIZER_H +#define _INIT_SPACE_TOKENIZER_H + +namespace android { +namespace init { + +class SpaceTokenizer { + public: + SpaceTokenizer(const std::string& string) + : string_(string), it_(string_.begin()), end_(string_.end()) {} + + std::string GetNext() { + auto next = std::string(); + while (it_ != end_ && !isspace(*it_)) { + next.push_back(*it_++); + } + while (it_ != end_ && isspace(*it_)) { + it_++; + } + return next; + } + + std::string GetRemaining() { return std::string(it_, end_); } + + private: + std::string string_; + std::string::const_iterator it_; + std::string::const_iterator end_; +}; + +} // namespace init +} // namespace android + +#endif diff --git a/property_service/.clang-format b/property_service/.clang-format new file mode 120000 index 000000000..fd0645fdf --- /dev/null +++ b/property_service/.clang-format @@ -0,0 +1 @@ +../.clang-format-2 \ No newline at end of file diff --git a/property_service/Android.bp b/property_service/Android.bp new file mode 100644 index 000000000..b44c29601 --- /dev/null +++ b/property_service/Android.bp @@ -0,0 +1 @@ +subdirs = ["*"] diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp new file mode 100644 index 000000000..3e732b51c --- /dev/null +++ b/property_service/libpropertyinfoparser/Android.bp @@ -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"], +} diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h new file mode 100644 index 000000000..c2114ccff --- /dev/null +++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h @@ -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 + +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(data_base_)->size; + } + + const char* c_string(uint32_t offset) const { + if (offset != 0 && offset > size()) return nullptr; + return static_cast(data_base_ + offset); + } + + const uint32_t* uint32_array(uint32_t offset) const { + if (offset != 0 && offset > size()) return nullptr; + return reinterpret_cast(data_base_ + offset); + } + + uint32_t uint32(uint32_t offset) const { + if (offset != 0 && offset > size()) return ~0u; + return *reinterpret_cast(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(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(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(serialized_data_->data_base() + + exact_match_entry_offset); + } + + private: + const PropertyEntry* node_property_entry() const { + return reinterpret_cast(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(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(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(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 diff --git a/property_service/libpropertyinfoparser/property_info_parser.cpp b/property_service/libpropertyinfoparser/property_info_parser.cpp new file mode 100644 index 000000000..84f8c29f8 --- /dev/null +++ b/property_service/libpropertyinfoparser/property_info_parser.cpp @@ -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 +#include +#include +#include +#include + +namespace android { +namespace properties { + +namespace { + +// Binary search to find index of element in an array compared via f(search). +template +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(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(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 diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp new file mode 100644 index 000000000..20e5e1366 --- /dev/null +++ b/property_service/libpropertyinfoserializer/Android.bp @@ -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"], +} diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h new file mode 100644 index 000000000..f7e708ede --- /dev/null +++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h @@ -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 +#include + +namespace android { +namespace properties { + +struct PropertyInfoEntry { + PropertyInfoEntry() {} + template + PropertyInfoEntry(T&& name, U&& context, V&& schema, bool exact_match) + : name(std::forward(name)), + context(std::forward(context)), + schema(std::forward(schema)), + exact_match(exact_match) {} + std::string name; + std::string context; + std::string schema; + bool exact_match; +}; + +bool BuildTrie(const std::vector& property_info, + const std::string& default_context, const std::string& default_schema, + std::string* serialized_trie, std::string* error); + +} // namespace properties +} // namespace android + +#endif diff --git a/property_service/libpropertyinfoserializer/property_info_serializer.cpp b/property_service/libpropertyinfoserializer/property_info_serializer.cpp new file mode 100644 index 000000000..656c96e33 --- /dev/null +++ b/property_service/libpropertyinfoserializer/property_info_serializer.cpp @@ -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 + +#include "trie_builder.h" +#include "trie_serializer.h" + +namespace android { +namespace properties { + +bool BuildTrie(const std::vector& 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 diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp new file mode 100644 index 000000000..b7a103e14 --- /dev/null +++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp @@ -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 + +namespace android { +namespace properties { + +TEST(propertyinfoserializer, TrieNodeCheck) { + auto property_info = std::vector{ + {"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(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{ + {"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(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{ + // 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(serialized_trie.data()); + + auto properties_and_contexts = std::vector>{ + // 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 diff --git a/property_service/libpropertyinfoserializer/trie_builder.cpp b/property_service/libpropertyinfoserializer/trie_builder.cpp new file mode 100644 index 000000000..feb753be7 --- /dev/null +++ b/property_service/libpropertyinfoserializer/trie_builder.cpp @@ -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 + +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* 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 diff --git a/property_service/libpropertyinfoserializer/trie_builder.h b/property_service/libpropertyinfoserializer/trie_builder.h new file mode 100644 index 000000000..f928e764f --- /dev/null +++ b/property_service/libpropertyinfoserializer/trie_builder.h @@ -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 +#include +#include +#include + +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& children() const { return children_; } + const std::vector& prefixes() const { return prefixes_; } + const std::vector& exact_matches() const { return exact_matches_; } + + private: + PropertyEntryBuilder property_entry_; + std::vector children_; + std::vector prefixes_; + std::vector 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& contexts() const { return contexts_; } + const std::set& 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* container); + + TrieBuilderNode builder_root_; + std::set contexts_; + std::set schemas_; +}; + +} // namespace properties +} // namespace android + +#endif diff --git a/property_service/libpropertyinfoserializer/trie_builder_test.cpp b/property_service/libpropertyinfoserializer/trie_builder_test.cpp new file mode 100644 index 000000000..2b948f3b0 --- /dev/null +++ b/property_service/libpropertyinfoserializer/trie_builder_test.cpp @@ -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 + +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 diff --git a/property_service/libpropertyinfoserializer/trie_node_arena.h b/property_service/libpropertyinfoserializer/trie_node_arena.h new file mode 100644 index 000000000..5e0ef8273 --- /dev/null +++ b/property_service/libpropertyinfoserializer/trie_node_arena.h @@ -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 +#include + +namespace android { +namespace properties { + +template +class ArenaObjectPointer { + public: + ArenaObjectPointer(std::string& arena_data, uint32_t offset) + : arena_data_(arena_data), offset_(offset) {} + + T* operator->() { return reinterpret_cast(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 + ArenaObjectPointer AllocateObject(uint32_t* return_offset) { + uint32_t offset; + AllocateData(sizeof(T), &offset); + if (return_offset) *return_offset = offset; + return ArenaObjectPointer(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(data_.data() + offset); + } + + uint32_t AllocateAndWriteString(const std::string& string) { + uint32_t offset; + char* data = static_cast(AllocateData(string.size() + 1, &offset)); + strcpy(data, string.c_str()); + return offset; + } + + void AllocateAndWriteUint32(uint32_t value) { + auto location = static_cast(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 diff --git a/property_service/libpropertyinfoserializer/trie_serializer.cpp b/property_service/libpropertyinfoserializer/trie_serializer.cpp new file mode 100644 index 000000000..5326537f4 --- /dev/null +++ b/property_service/libpropertyinfoserializer/trie_serializer.cpp @@ -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& 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(&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(&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(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 diff --git a/property_service/libpropertyinfoserializer/trie_serializer.h b/property_service/libpropertyinfoserializer/trie_serializer.h new file mode 100644 index 000000000..e4d3343a0 --- /dev/null +++ b/property_service/libpropertyinfoserializer/trie_serializer.h @@ -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 +#include + +#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& 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(arena_->data().data()); + } + + std::unique_ptr arena_; +}; + +} // namespace properties +} // namespace android + +#endif