Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4343d918 authored by Tom Cherry's avatar Tom Cherry Committed by android-build-merger
Browse files

Parse property contexts via a serialized trie am: d853f77e

am: f075dea9

Change-Id: I6a8f2f3d5fce24adc317356f225b670a2fc98b80
parents ab4913be f075dea9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
../.clang-format-2
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
subdirs = ["*"]
+16 −0
Original line number Diff line number Diff line
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"],
}
+221 −0
Original line number Diff line number Diff line
//
// 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
+220 −0
Original line number Diff line number Diff line
//
// 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
Loading