Merge changes from topic "property-trie"

* changes:
  Have property_service create a serialized property_contexts file
  Parse property contexts via a serialized trie
This commit is contained in:
Tom Cherry 2017-12-13 01:28:36 +00:00 committed by Gerrit Code Review
commit c47dd6b5c0
19 changed files with 2157 additions and 15 deletions

View file

@ -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",
],
}

View file

@ -84,6 +84,8 @@ LOCAL_STATIC_LIBRARIES := \
libavb \
libkeyutils \
libprotobuf-cpp-lite \
libpropertyinfoserializer \
libpropertyinfoparser \
LOCAL_REQUIRED_MODULES := \
e2fsdroid \

View file

@ -50,17 +50,27 @@
#include <android-base/strings.h>
#include <bootimg.h>
#include <fs_mgr.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
#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<PropertyInfoEntry> 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<PropertyInfoEntry>* 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<PropertyInfoEntry>();
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);

50
init/space_tokenizer.h Normal file
View file

@ -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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,221 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef PROPERTY_INFO_PARSER_H
#define PROPERTY_INFO_PARSER_H
#include <stdint.h>
namespace android {
namespace properties {
// The below structs intentionally do not end with char name[0] or other tricks to allocate
// with a dynamic size, such that they can be added onto in the future without breaking
// backwards compatibility.
struct PropertyEntry {
uint32_t name_offset;
uint32_t namelen;
// This is the context match for this node_; ~0u if it doesn't correspond to any.
uint32_t context_index;
// This is the schema for this node_; ~0u if it doesn't correspond to any.
uint32_t schema_index;
};
struct TrieNodeInternal {
// This points to a property entry struct, which includes the name for this node
uint32_t property_entry;
// Children are a sorted list of child nodes_; binary search them.
uint32_t num_child_nodes;
uint32_t child_nodes;
// Prefixes are terminating prefix matches at this node, sorted longest to smallest
// Take the first match sequentially found with StartsWith().
uint32_t num_prefixes;
uint32_t prefix_entries;
// Exact matches are a sorted list of exact matches at this node_; binary search them.
uint32_t num_exact_matches;
uint32_t exact_match_entries;
};
struct PropertyInfoAreaHeader {
// The current version of this data as created by property service.
uint32_t current_version;
// The lowest version of libc that can properly parse this data.
uint32_t minimum_supported_version;
uint32_t size;
uint32_t contexts_offset;
uint32_t schemas_offset;
uint32_t root_offset;
};
class SerializedData {
public:
uint32_t size() const {
return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base_)->size;
}
const char* c_string(uint32_t offset) const {
if (offset != 0 && offset > size()) return nullptr;
return static_cast<const char*>(data_base_ + offset);
}
const uint32_t* uint32_array(uint32_t offset) const {
if (offset != 0 && offset > size()) return nullptr;
return reinterpret_cast<const uint32_t*>(data_base_ + offset);
}
uint32_t uint32(uint32_t offset) const {
if (offset != 0 && offset > size()) return ~0u;
return *reinterpret_cast<const uint32_t*>(data_base_ + offset);
}
const char* data_base() const { return data_base_; }
private:
const char data_base_[0];
};
class TrieNode {
public:
TrieNode() : serialized_data_(nullptr), trie_node_base_(nullptr) {}
TrieNode(const SerializedData* data_base, const TrieNodeInternal* trie_node_base)
: serialized_data_(data_base), trie_node_base_(trie_node_base) {}
const char* name() const {
return serialized_data_->c_string(node_property_entry()->name_offset);
}
uint32_t context_index() const { return node_property_entry()->context_index; }
uint32_t schema_index() const { return node_property_entry()->schema_index; }
uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }
TrieNode child_node(int n) const {
uint32_t child_node_offset = serialized_data_->uint32_array(trie_node_base_->child_nodes)[n];
const TrieNodeInternal* trie_node_base =
reinterpret_cast<const TrieNodeInternal*>(serialized_data_->data_base() + child_node_offset);
return TrieNode(serialized_data_, trie_node_base);
}
bool FindChildForString(const char* input, uint32_t namelen, TrieNode* child) const;
uint32_t num_prefixes() const { return trie_node_base_->num_prefixes; }
const PropertyEntry* prefix(int n) const {
uint32_t prefix_entry_offset =
serialized_data_->uint32_array(trie_node_base_->prefix_entries)[n];
return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
prefix_entry_offset);
}
uint32_t num_exact_matches() const { return trie_node_base_->num_exact_matches; }
const PropertyEntry* exact_match(int n) const {
uint32_t exact_match_entry_offset =
serialized_data_->uint32_array(trie_node_base_->exact_match_entries)[n];
return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
exact_match_entry_offset);
}
private:
const PropertyEntry* node_property_entry() const {
return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +
trie_node_base_->property_entry);
}
const SerializedData* serialized_data_;
const TrieNodeInternal* trie_node_base_;
};
class PropertyInfoArea : private SerializedData {
public:
void GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
uint32_t* schema_index) const;
void GetPropertyInfo(const char* property, const char** context, const char** schema) const;
int FindContextIndex(const char* context) const;
int FindSchemaIndex(const char* schema) const;
const char* context(uint32_t index) const {
uint32_t context_array_size_offset = contexts_offset();
const uint32_t* context_array = uint32_array(context_array_size_offset + sizeof(uint32_t));
return data_base() + context_array[index];
}
const char* schema(uint32_t index) const {
uint32_t schema_array_size_offset = schemas_offset();
const uint32_t* schema_array = uint32_array(schema_array_size_offset + sizeof(uint32_t));
return data_base() + schema_array[index];
}
uint32_t current_version() const { return header()->current_version; }
uint32_t minimum_supported_version() const { return header()->minimum_supported_version; }
uint32_t size() const { return SerializedData::size(); }
uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }
uint32_t num_schemas() const { return uint32_array(schemas_offset())[0]; }
TrieNode root_node() const { return trie(header()->root_offset); }
private:
const PropertyInfoAreaHeader* header() const {
return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());
}
uint32_t contexts_offset() const { return header()->contexts_offset; }
uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }
uint32_t schemas_offset() const { return header()->schemas_offset; }
uint32_t schemas_array_offset() const { return schemas_offset() + sizeof(uint32_t); }
TrieNode trie(uint32_t offset) const {
if (offset != 0 && offset > size()) return TrieNode();
const TrieNodeInternal* trie_node_base =
reinterpret_cast<const TrieNodeInternal*>(data_base() + offset);
return TrieNode(this, trie_node_base);
}
};
// This is essentially a smart pointer for read only mmap region for property contexts.
class PropertyInfoAreaFile {
public:
PropertyInfoAreaFile() : mmap_base_(nullptr), mmap_size_(0) {}
~PropertyInfoAreaFile() { Reset(); }
PropertyInfoAreaFile(const PropertyInfoAreaFile&) = delete;
void operator=(const PropertyInfoAreaFile&) = delete;
PropertyInfoAreaFile(PropertyInfoAreaFile&&) = default;
PropertyInfoAreaFile& operator=(PropertyInfoAreaFile&&) = default;
bool LoadDefaultPath();
bool LoadPath(const char* filename);
const PropertyInfoArea* operator->() const {
return reinterpret_cast<const PropertyInfoArea*>(mmap_base_);
}
explicit operator bool() const { return mmap_base_ != nullptr; }
void Reset();
private:
void* mmap_base_;
size_t mmap_size_;
};
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,220 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "property_info_parser/property_info_parser.h"
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace android {
namespace properties {
namespace {
// Binary search to find index of element in an array compared via f(search).
template <typename F>
int Find(uint32_t array_length, F&& f) {
int bottom = 0;
int top = array_length - 1;
while (top >= bottom) {
int search = (top + bottom) / 2;
auto cmp = f(search);
if (cmp == 0) return search;
if (cmp < 0) bottom = search + 1;
if (cmp > 0) top = search - 1;
}
return -1;
}
} // namespace
// Binary search the list of contexts to find the index of a given context string.
// Only should be used for TrieSerializer to construct the Trie.
int PropertyInfoArea::FindContextIndex(const char* context) const {
return Find(num_contexts(), [this, context](auto array_offset) {
auto string_offset = uint32_array(contexts_array_offset())[array_offset];
return strcmp(c_string(string_offset), context);
});
}
// Binary search the list of schemas to find the index of a given schema string.
// Only should be used for TrieSerializer to construct the Trie.
int PropertyInfoArea::FindSchemaIndex(const char* schema) const {
return Find(num_schemas(), [this, schema](auto array_offset) {
auto string_offset = uint32_array(schemas_array_offset())[array_offset];
return strcmp(c_string(string_offset), schema);
});
}
// Binary search the list of children nodes to find a TrieNode for a given property piece.
// Used to traverse the Trie in GetPropertyInfoIndexes().
bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {
auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {
const char* child_name = child_node(array_offset).name();
int cmp = strncmp(child_name, name, namelen);
if (cmp == 0 && child_name[namelen] != '\0') {
// We use strncmp() since name isn't null terminated, but we don't want to match only a
// prefix of a child node's name, so we check here if we did only match a prefix and
// return 1, to indicate to the binary search to search earlier in the array for the real
// match.
return 1;
}
return cmp;
});
if (node_index == -1) {
return false;
}
*child = child_node(node_index);
return true;
}
void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
uint32_t* schema_index) const {
uint32_t return_context_index = ~0u;
uint32_t return_schema_index = ~0u;
const char* remaining_name = name;
auto trie_node = root_node();
while (true) {
const char* sep = strchr(remaining_name, '.');
// Apply prefix match for prefix deliminated with '.'
if (trie_node.context_index() != ~0u) {
return_context_index = trie_node.context_index();
}
if (trie_node.schema_index() != ~0u) {
return_schema_index = trie_node.schema_index();
}
if (sep == nullptr) {
break;
}
const uint32_t substr_size = sep - remaining_name;
TrieNode child_node;
if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {
break;
}
trie_node = child_node;
remaining_name = sep + 1;
}
// We've made it to a leaf node, so check contents and return appropriately.
// Check exact matches
for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {
if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {
if (context_index != nullptr) *context_index = trie_node.exact_match(i)->context_index;
if (schema_index != nullptr) *schema_index = trie_node.exact_match(i)->schema_index;
return;
}
}
// Check prefix matches for prefixes not deliminated with '.'
const uint32_t remaining_name_size = strlen(remaining_name);
for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
auto prefix_len = trie_node.prefix(i)->namelen;
if (prefix_len > remaining_name_size) continue;
if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {
if (context_index != nullptr) *context_index = trie_node.prefix(i)->context_index;
if (schema_index != nullptr) *schema_index = trie_node.prefix(i)->schema_index;
return;
}
}
// Return previously found '.' deliminated prefix match.
if (context_index != nullptr) *context_index = return_context_index;
if (schema_index != nullptr) *schema_index = return_schema_index;
return;
}
void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,
const char** schema) const {
uint32_t context_index;
uint32_t schema_index;
GetPropertyInfoIndexes(property, &context_index, &schema_index);
if (context != nullptr) {
if (context_index == ~0u) {
*context = nullptr;
} else {
*context = this->context(context_index);
}
}
if (schema != nullptr) {
if (schema_index == ~0u) {
*schema = nullptr;
} else {
*schema = this->schema(schema_index);
}
}
}
bool PropertyInfoAreaFile::LoadDefaultPath() {
return LoadPath("/dev/__properties__/property_info");
}
bool PropertyInfoAreaFile::LoadPath(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) {
close(fd);
return false;
}
if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
(fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
close(fd);
return false;
}
auto mmap_size = fd_stat.st_size;
void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
if (map_result == MAP_FAILED) {
close(fd);
return false;
}
auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
if (property_info_area->minimum_supported_version() > 1 ||
property_info_area->size() != mmap_size) {
munmap(map_result, mmap_size);
close(fd);
return false;
}
close(fd);
mmap_base_ = map_result;
mmap_size_ = mmap_size;
return true;
}
void PropertyInfoAreaFile::Reset() {
if (mmap_size_ > 0) {
munmap(mmap_base_, mmap_size_);
}
mmap_base_ = nullptr;
mmap_size_ = 0;
}
} // namespace properties
} // namespace android

View file

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

View file

@ -0,0 +1,47 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef PROPERTY_INFO_SERIALIZER_H
#define PROPERTY_INFO_SERIALIZER_H
#include <string>
#include <vector>
namespace android {
namespace properties {
struct PropertyInfoEntry {
PropertyInfoEntry() {}
template <typename T, typename U, typename V>
PropertyInfoEntry(T&& name, U&& context, V&& schema, bool exact_match)
: name(std::forward<T>(name)),
context(std::forward<U>(context)),
schema(std::forward<V>(schema)),
exact_match(exact_match) {}
std::string name;
std::string context;
std::string schema;
bool exact_match;
};
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
const std::string& default_context, const std::string& default_schema,
std::string* serialized_trie, std::string* error);
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,47 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "property_info_serializer/property_info_serializer.h"
#include "property_info_parser/property_info_parser.h"
#include <set>
#include "trie_builder.h"
#include "trie_serializer.h"
namespace android {
namespace properties {
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
const std::string& default_context, const std::string& default_schema,
std::string* serialized_trie, std::string* error) {
// Check that names are legal first
auto trie_builder = TrieBuilder(default_context, default_schema);
for (const auto& [name, context, schema, is_exact] : property_info) {
if (!trie_builder.AddToTrie(name, context, schema, is_exact, error)) {
return false;
}
}
auto trie_serializer = TrieSerializer();
*serialized_trie = trie_serializer.SerializeTrie(trie_builder);
return true;
}
} // namespace properties
} // namespace android

View file

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

View file

@ -0,0 +1,105 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "trie_builder.h"
#include <android-base/strings.h>
using android::base::Split;
namespace android {
namespace properties {
TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_schema)
: builder_root_("root") {
auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
builder_root_.set_context(context_pointer);
auto* schema_pointer = StringPointerFromContainer(default_schema, &schemas_);
builder_root_.set_schema(schema_pointer);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
const std::string& schema, bool exact, std::string* error) {
auto* context_pointer = StringPointerFromContainer(context, &contexts_);
auto* schema_pointer = StringPointerFromContainer(schema, &schemas_);
return AddToTrie(name, context_pointer, schema_pointer, exact, error);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
const std::string* schema, bool exact, std::string* error) {
TrieBuilderNode* current_node = &builder_root_;
auto name_pieces = Split(name, ".");
bool ends_with_dot = false;
if (name_pieces.back().empty()) {
ends_with_dot = true;
name_pieces.pop_back();
}
// Move us to the final node that we care about, adding incremental nodes if necessary.
while (name_pieces.size() > 1) {
auto child = current_node->FindChild(name_pieces.front());
if (child == nullptr) {
child = current_node->AddChild(name_pieces.front());
}
if (child == nullptr) {
*error = "Unable to allocate Trie node";
return false;
}
current_node = child;
name_pieces.erase(name_pieces.begin());
}
// Store our context based on what type of match it is.
if (exact) {
if (!current_node->AddExactMatchContext(name_pieces.front(), context, schema)) {
*error = "Duplicate exact match detected for '" + name + "'";
return false;
}
} else if (!ends_with_dot) {
if (!current_node->AddPrefixContext(name_pieces.front(), context, schema)) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
} else {
auto child = current_node->FindChild(name_pieces.front());
if (child == nullptr) {
child = current_node->AddChild(name_pieces.front());
}
if (child == nullptr) {
*error = "Unable to allocate Trie node";
return false;
}
if (child->context() != nullptr || child->schema() != nullptr) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
child->set_context(context);
child->set_schema(schema);
}
return true;
}
const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,
std::set<std::string>* container) {
// Get a pointer to the string in a given set, such that we only ever serialize each string once.
auto [iterator, _] = container->emplace(string);
return &(*iterator);
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,124 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
#include <memory>
#include <set>
#include <string>
#include <vector>
namespace android {
namespace properties {
struct PropertyEntryBuilder {
PropertyEntryBuilder() : context(nullptr), schema(nullptr) {}
PropertyEntryBuilder(const std::string& name, const std::string* context,
const std::string* schema)
: name(name), context(context), schema(schema) {}
std::string name;
const std::string* context;
const std::string* schema;
};
class TrieBuilderNode {
public:
TrieBuilderNode(const std::string& name) : property_entry_(name, nullptr, nullptr) {}
TrieBuilderNode* FindChild(const std::string& name) {
for (auto& child : children_) {
if (child.name() == name) return &child;
}
return nullptr;
}
const TrieBuilderNode* FindChild(const std::string& name) const {
for (const auto& child : children_) {
if (child.name() == name) return &child;
}
return nullptr;
}
TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
bool AddPrefixContext(const std::string& prefix, const std::string* context,
const std::string* schema) {
if (std::find_if(prefixes_.begin(), prefixes_.end(),
[&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {
return false;
}
prefixes_.emplace_back(prefix, context, schema);
return true;
}
bool AddExactMatchContext(const std::string& exact_match, const std::string* context,
const std::string* schema) {
if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {
return t.name == exact_match;
}) != exact_matches_.end()) {
return false;
}
exact_matches_.emplace_back(exact_match, context, schema);
return true;
}
const std::string& name() const { return property_entry_.name; }
const std::string* context() const { return property_entry_.context; }
void set_context(const std::string* context) { property_entry_.context = context; }
const std::string* schema() const { return property_entry_.schema; }
void set_schema(const std::string* schema) { property_entry_.schema = schema; }
const PropertyEntryBuilder property_entry() const { return property_entry_; }
const std::vector<TrieBuilderNode>& children() const { return children_; }
const std::vector<PropertyEntryBuilder>& prefixes() const { return prefixes_; }
const std::vector<PropertyEntryBuilder>& exact_matches() const { return exact_matches_; }
private:
PropertyEntryBuilder property_entry_;
std::vector<TrieBuilderNode> children_;
std::vector<PropertyEntryBuilder> prefixes_;
std::vector<PropertyEntryBuilder> exact_matches_;
};
class TrieBuilder {
public:
TrieBuilder(const std::string& default_context, const std::string& default_schema);
bool AddToTrie(const std::string& name, const std::string& context, const std::string& schema,
bool exact, std::string* error);
const TrieBuilderNode builder_root() const { return builder_root_; }
const std::set<std::string>& contexts() const { return contexts_; }
const std::set<std::string>& schemas() const { return schemas_; }
private:
bool AddToTrie(const std::string& name, const std::string* context, const std::string* schema,
bool exact, std::string* error);
const std::string* StringPointerFromContainer(const std::string& string,
std::set<std::string>* container);
TrieBuilderNode builder_root_;
std::set<std::string> contexts_;
std::set<std::string> schemas_;
};
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,129 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "trie_builder.h"
#include <gtest/gtest.h>
namespace android {
namespace properties {
TEST(propertyinfoserializer, BuildTrie_Simple) {
auto trie_builder = TrieBuilder("default", "default_schema");
// Add test data to tree
auto error = std::string();
EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_schema", false, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_schema", false, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_schema", true, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_schema", true, &error));
EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_schema", true, &error));
EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_schema", true, &error));
ASSERT_EQ(5U, trie_builder.contexts().size());
ASSERT_EQ(5U, trie_builder.schemas().size());
auto& builder_root = trie_builder.builder_root();
// Check the root node
EXPECT_EQ("root", builder_root.name());
ASSERT_NE(nullptr, builder_root.context());
EXPECT_EQ("default", *builder_root.context());
ASSERT_NE(nullptr, builder_root.schema());
EXPECT_EQ("default_schema", *builder_root.schema());
EXPECT_EQ(0U, builder_root.prefixes().size());
EXPECT_EQ(0U, builder_root.exact_matches().size());
ASSERT_EQ(2U, builder_root.children().size());
// Check the 'test.' node
auto* test_node = builder_root.FindChild("test");
EXPECT_EQ("test", test_node->name());
ASSERT_NE(nullptr, test_node->context());
EXPECT_EQ("1st", *test_node->context());
ASSERT_NE(nullptr, test_node->schema());
EXPECT_EQ("1st_schema", *test_node->schema());
EXPECT_EQ(0U, test_node->children().size());
EXPECT_EQ(1U, test_node->prefixes().size());
{
auto& property_entry = test_node->prefixes()[0];
EXPECT_EQ("test", property_entry.name);
ASSERT_NE(nullptr, property_entry.context);
EXPECT_EQ("2nd", *property_entry.context);
ASSERT_NE(nullptr, property_entry.schema);
EXPECT_EQ("2nd_schema", *property_entry.schema);
}
EXPECT_EQ(3U, test_node->exact_matches().size());
EXPECT_EQ("test1", test_node->exact_matches()[0].name);
EXPECT_EQ("test2", test_node->exact_matches()[1].name);
EXPECT_EQ("test3", test_node->exact_matches()[2].name);
ASSERT_NE(nullptr, test_node->exact_matches()[0].context);
ASSERT_NE(nullptr, test_node->exact_matches()[1].context);
ASSERT_NE(nullptr, test_node->exact_matches()[2].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[0].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[1].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[2].context);
ASSERT_NE(nullptr, test_node->exact_matches()[0].schema);
ASSERT_NE(nullptr, test_node->exact_matches()[1].schema);
ASSERT_NE(nullptr, test_node->exact_matches()[2].schema);
EXPECT_EQ("3rd_schema", *test_node->exact_matches()[0].schema);
EXPECT_EQ("3rd_schema", *test_node->exact_matches()[1].schema);
EXPECT_EQ("3rd_schema", *test_node->exact_matches()[2].schema);
// Check the long string node
auto expect_empty_one_child = [](auto* node) {
ASSERT_NE(nullptr, node);
EXPECT_EQ(nullptr, node->context());
EXPECT_EQ(nullptr, node->schema());
EXPECT_EQ(0U, node->prefixes().size());
EXPECT_EQ(0U, node->exact_matches().size());
EXPECT_EQ(1U, node->children().size());
};
// Start with 'this'
auto* long_string_node = builder_root.FindChild("this");
expect_empty_one_child(long_string_node);
// Move to 'is'
long_string_node = long_string_node->FindChild("is");
expect_empty_one_child(long_string_node);
// Move to 'a'
long_string_node = long_string_node->FindChild("a");
expect_empty_one_child(long_string_node);
// Move to 'long'
long_string_node = long_string_node->FindChild("long");
EXPECT_EQ(0U, long_string_node->prefixes().size());
EXPECT_EQ(1U, long_string_node->exact_matches().size());
EXPECT_EQ(0U, long_string_node->children().size());
{
auto& property_entry = long_string_node->exact_matches()[0];
EXPECT_EQ("string", property_entry.name);
ASSERT_NE(nullptr, property_entry.context);
EXPECT_EQ("4th", *property_entry.context);
ASSERT_NE(nullptr, property_entry.schema);
EXPECT_EQ("4th_schema", *property_entry.schema);
}
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,108 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
#include <string>
#include <vector>
namespace android {
namespace properties {
template <typename T>
class ArenaObjectPointer {
public:
ArenaObjectPointer(std::string& arena_data, uint32_t offset)
: arena_data_(arena_data), offset_(offset) {}
T* operator->() { return reinterpret_cast<T*>(arena_data_.data() + offset_); }
private:
std::string& arena_data_;
uint32_t offset_;
};
class TrieNodeArena {
public:
TrieNodeArena() : current_data_pointer_(0) {}
// We can't return pointers to objects since data_ may move when reallocated, thus invalidating
// any pointers. Therefore we return an ArenaObjectPointer, which always accesses elements via
// data_ + offset.
template <typename T>
ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {
uint32_t offset;
AllocateData(sizeof(T), &offset);
if (return_offset) *return_offset = offset;
return ArenaObjectPointer<T>(data_, offset);
}
uint32_t AllocateUint32Array(int length) {
uint32_t offset;
AllocateData(sizeof(uint32_t) * length, &offset);
return offset;
}
uint32_t* uint32_array(uint32_t offset) {
return reinterpret_cast<uint32_t*>(data_.data() + offset);
}
uint32_t AllocateAndWriteString(const std::string& string) {
uint32_t offset;
char* data = static_cast<char*>(AllocateData(string.size() + 1, &offset));
strcpy(data, string.c_str());
return offset;
}
void AllocateAndWriteUint32(uint32_t value) {
auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
*location = value;
}
void* AllocateData(size_t size, uint32_t* offset) {
size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
if (current_data_pointer_ + aligned_size > data_.size()) {
auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;
data_.resize(new_size, '\0');
}
if (offset) *offset = current_data_pointer_;
uint32_t return_offset = current_data_pointer_;
current_data_pointer_ += aligned_size;
return &data_[0] + return_offset;
}
uint32_t size() const { return current_data_pointer_; }
const std::string& data() const { return data_; }
std::string truncated_data() const {
auto result = data_;
result.resize(current_data_pointer_);
return result;
}
private:
std::string data_;
uint32_t current_data_pointer_;
};
} // namespace properties
} // namespace android
#endif

View file

@ -0,0 +1,142 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "trie_serializer.h"
namespace android {
namespace properties {
// Serialized strings contains:
// 1) A uint32_t count of elements in the below array
// 2) A sorted array of uint32_t offsets pointing to null terminated strings
// 3) Each of the null terminated strings themselves packed back to back
// This returns the offset into arena where the serialized strings start.
void TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {
arena_->AllocateAndWriteUint32(strings.size());
// Allocate space for the array.
uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());
// Write offset pointers and strings; these are already alphabetically sorted by virtue of being
// in an std::set.
auto it = strings.begin();
for (unsigned int i = 0; i < strings.size(); ++i, ++it) {
uint32_t string_offset = arena_->AllocateAndWriteString(*it);
arena_->uint32_array(offset_array_offset)[i] = string_offset;
}
}
uint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {
uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
? serialized_info()->FindContextIndex(property_entry.context->c_str())
: ~0u;
uint32_t schema_index = property_entry.schema != nullptr && !property_entry.schema->empty()
? serialized_info()->FindSchemaIndex(property_entry.schema->c_str())
: ~0u;
uint32_t offset;
auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
serialized_property_entry->namelen = property_entry.name.size();
serialized_property_entry->context_index = context_index;
serialized_property_entry->schema_index = schema_index;
return offset;
}
uint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {
uint32_t trie_offset;
auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);
trie->property_entry = WritePropertyEntry(builder_node.property_entry());
// Write prefix matches
auto sorted_prefix_matches = builder_node.prefixes();
// Prefixes are sorted by descending length
std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });
trie->num_prefixes = sorted_prefix_matches.size();
uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());
trie->prefix_entries = prefix_entries_array_offset;
for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {
uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);
arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;
}
// Write exact matches
auto sorted_exact_matches = builder_node.exact_matches();
// Exact matches are sorted alphabetically
std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });
trie->num_exact_matches = sorted_exact_matches.size();
uint32_t exact_match_entries_array_offset =
arena_->AllocateUint32Array(sorted_exact_matches.size());
trie->exact_match_entries = exact_match_entries_array_offset;
for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {
uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);
arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;
}
// Write children
auto sorted_children = builder_node.children();
std::sort(sorted_children.begin(), sorted_children.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });
trie->num_child_nodes = sorted_children.size();
uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());
trie->child_nodes = children_offset_array_offset;
for (unsigned int i = 0; i < sorted_children.size(); ++i) {
arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);
}
return trie_offset;
}
TrieSerializer::TrieSerializer() {}
std::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {
arena_.reset(new TrieNodeArena());
auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);
header->current_version = 1;
header->minimum_supported_version = 1;
// Store where we're about to write the contexts.
header->contexts_offset = arena_->size();
SerializeStrings(trie_builder.contexts());
// Store where we're about to write the schemas.
header->schemas_offset = arena_->size();
SerializeStrings(trie_builder.schemas());
// We need to store size() up to this point now for Find*Offset() to work.
header->size = arena_->size();
uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());
header->root_offset = root_trie_offset;
// Record the real size now that we've written everything
header->size = arena_->size();
return arena_->truncated_data();
}
} // namespace properties
} // namespace android

View file

@ -0,0 +1,55 @@
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
#include <string>
#include <vector>
#include "property_info_parser/property_info_parser.h"
#include "trie_builder.h"
#include "trie_node_arena.h"
namespace android {
namespace properties {
class TrieSerializer {
public:
TrieSerializer();
std::string SerializeTrie(const TrieBuilder& trie_builder);
private:
void SerializeStrings(const std::set<std::string>& strings);
uint32_t WritePropertyEntry(const PropertyEntryBuilder& property_entry);
// Writes a new TrieNode to arena, and recursively writes its children.
// Returns the offset within arena.
uint32_t WriteTrieNode(const TrieBuilderNode& builder_node);
const PropertyInfoArea* serialized_info() const {
return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
}
std::unique_ptr<TrieNodeArena> arena_;
};
} // namespace properties
} // namespace android
#endif