Merge "storaged: align buffer address and size for direct_io"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e2e9577..dc45959 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -71,3 +71,6 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index ed165ed..f23150d 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -25,6 +25,9 @@
         "-Werror",
         "-Wno-unused-variable",
     ],
+    cppflags: [
+        "-std=gnu++1z",
+    ],
 }
 
 cc_library_static {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2c18a6d..34afed1 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -23,6 +23,10 @@
 #include <sys/mount.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <utility>
+#include <vector>
+
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -397,16 +401,17 @@
 }
 
 static std::string read_fstab_from_dt() {
-    std::string fstab;
     if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
-        return fstab;
+        return {};
     }
 
     std::string fstabdir_name = get_android_dt_dir() + "/fstab";
     std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
-    if (!fstabdir) return fstab;
+    if (!fstabdir) return {};
 
     dirent* dp;
+    // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
+    std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
     while ((dp = readdir(fstabdir.get())) != NULL) {
         // skip over name, compatible and .
         if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
@@ -427,41 +432,54 @@
         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
-        fstab_entry.push_back(android::base::StringPrintf("/%s", dp->d_name));
+
+        std::string mount_point;
+        file_name =
+            android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+        if (read_dt_file(file_name, &value)) {
+            LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
+            mount_point = value;
+        } else {
+            mount_point = android::base::StringPrintf("/%s", dp->d_name);
+        }
+        fstab_entry.push_back(mount_point);
 
         file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
 
         file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
 
         file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
         if (!read_dt_file(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
+            return {};
         }
         fstab_entry.push_back(value);
-
-        fstab += android::base::Join(fstab_entry, " ");
-        fstab += '\n';
+        // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
+        fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
     }
 
-    return fstab;
+    // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
+    std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
+              [](const auto& a, const auto& b) { return a.first < b.first; });
+
+    std::string fstab_result;
+    for (const auto& [_, dt_entry] : fstab_dt_entries) {
+        fstab_result += dt_entry + "\n";
+    }
+    return fstab_result;
 }
 
 bool is_dt_compatible() {
diff --git a/healthd/Android.bp b/healthd/Android.bp
index ea59469..3a71d03 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -20,38 +20,6 @@
     export_header_lib_headers: ["libhealthd_headers"],
 }
 
-cc_library_static {
-    name: "android.hardware.health@2.0-impl",
-    vendor_available: true,
-    srcs: [
-        "Health.cpp",
-        "healthd_common.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    export_include_dirs: ["include"],
-
-    shared_libs: [
-        "libbase",
-        "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
-        "liblog",
-        "libutils",
-        "libcutils",
-        "android.hardware.health@2.0",
-    ],
-
-    static_libs: [
-        "libbatterymonitor",
-        "android.hardware.health@1.0-convert",
-    ],
-}
-
 cc_binary {
     name: "android.hardware.health@2.0-service",
     init_rc: ["android.hardware.health@2.0-service.rc"],
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 63c9d27..7e58785 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -48,6 +48,7 @@
 
 LOCAL_STATIC_LIBRARIES := \
     android.hardware.health@2.0 \
+    android.hardware.health@2.0-impl \
     android.hardware.health@1.0 \
     libminui \
     libpng \
diff --git a/healthd/Health.cpp b/healthd/Health.cpp
deleted file mode 100644
index 822f664..0000000
--- a/healthd/Health.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-#define LOG_TAG "Health"
-#include <android-base/logging.h>
-
-#include <health2/Health.h>
-
-#include <hidl/HidlTransportSupport.h>
-
-extern void healthd_battery_update_internal(bool);
-
-namespace android {
-namespace hardware {
-namespace health {
-namespace V2_0 {
-namespace implementation {
-
-sp<Health> Health::instance_;
-
-Health::Health(struct healthd_config* c) {
-    // TODO(b/69268160): remove when libhealthd is removed.
-    healthd_board_init(c);
-    battery_monitor_ = std::make_unique<BatteryMonitor>();
-    battery_monitor_->init(c);
-}
-
-// Methods from IHealth follow.
-Return<Result> Health::registerCallback(const sp<IHealthInfoCallback>& callback) {
-    if (callback == nullptr) {
-        return Result::SUCCESS;
-    }
-
-    {
-        std::lock_guard<std::mutex> _lock(callbacks_lock_);
-        callbacks_.push_back(callback);
-        // unlock
-    }
-
-    auto linkRet = callback->linkToDeath(this, 0u /* cookie */);
-    if (!linkRet.withDefault(false)) {
-        LOG(WARNING) << __func__ << "Cannot link to death: "
-                     << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description());
-        // ignore the error
-    }
-
-    return update();
-}
-
-bool Health::unregisterCallbackInternal(const sp<IBase>& callback) {
-    if (callback == nullptr) return false;
-
-    bool removed = false;
-    std::lock_guard<std::mutex> _lock(callbacks_lock_);
-    for (auto it = callbacks_.begin(); it != callbacks_.end();) {
-        if (interfacesEqual(*it, callback)) {
-            it = callbacks_.erase(it);
-            removed = true;
-        } else {
-            ++it;
-        }
-    }
-    (void)callback->unlinkToDeath(this).isOk();  // ignore errors
-    return removed;
-}
-
-Return<Result> Health::unregisterCallback(const sp<IHealthInfoCallback>& callback) {
-    return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND;
-}
-
-template<typename T>
-void getProperty(const std::unique_ptr<BatteryMonitor>& monitor, int id, T defaultValue,
-                 const std::function<void(Result, T)>& callback) {
-    struct BatteryProperty prop;
-    T ret = defaultValue;
-    Result result = Result::SUCCESS;
-    status_t err = monitor->getProperty(static_cast<int>(id), &prop);
-    if (err != OK) {
-        LOG(DEBUG) << "getProperty(" << id << ")" << " fails: (" << err << ") " << strerror(-err);
-    } else {
-        ret = static_cast<T>(prop.valueInt64);
-    }
-    switch (err) {
-        case OK:             result = Result::SUCCESS; break;
-        case NAME_NOT_FOUND: result = Result::NOT_SUPPORTED; break;
-        default:             result = Result::UNKNOWN; break;
-    }
-    callback(result, static_cast<T>(ret));
-}
-
-Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {
-    getProperty(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, INT32_MIN, _hidl_cb);
-    return Void();
-}
-
-Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {
-    getProperty(battery_monitor_, BATTERY_PROP_CURRENT_NOW, INT32_MIN, _hidl_cb);
-    return Void();
-}
-
-Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {
-    getProperty(battery_monitor_, BATTERY_PROP_CURRENT_AVG, INT32_MIN, _hidl_cb);
-    return Void();
-}
-
-Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {
-    getProperty(battery_monitor_, BATTERY_PROP_CAPACITY, INT32_MIN, _hidl_cb);
-    return Void();
-}
-
-Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
-    getProperty(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, INT64_MIN, _hidl_cb);
-    return Void();
-}
-
-Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {
-    getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb);
-    return Void();
-}
-
-
-Return<Result> Health::update() {
-    if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {
-        LOG(WARNING) << "health@2.0: update: not initialized. "
-                     << "update() should not be called in charger / recovery.";
-        return Result::UNKNOWN;
-    }
-
-    // Retrieve all information and call healthd_mode_ops->battery_update, which calls
-    // notifyListeners.
-    bool chargerOnline = battery_monitor_->update();
-
-    // adjust uevent / wakealarm periods
-    healthd_battery_update_internal(chargerOnline);
-
-    return Result::SUCCESS;
-}
-
-void Health::notifyListeners(const HealthInfo& info) {
-    std::lock_guard<std::mutex> _lock(callbacks_lock_);
-    for (auto it = callbacks_.begin(); it != callbacks_.end();) {
-        auto ret = (*it)->healthInfoChanged(info);
-        if (!ret.isOk() && ret.isDeadObject()) {
-            it = callbacks_.erase(it);
-        } else {
-            ++it;
-        }
-    }
-}
-
-Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
-    if (handle != nullptr && handle->numFds >= 1) {
-        int fd = handle->data[0];
-        battery_monitor_->dumpState(fd);
-        fsync(fd);
-    }
-    return Void();
-}
-
-void Health::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) {
-    (void)unregisterCallbackInternal(who.promote());
-}
-
-sp<IHealth> Health::initInstance(struct healthd_config* c) {
-    if (instance_ == nullptr) {
-        instance_ = new Health(c);
-    }
-    return instance_;
-}
-
-sp<Health> Health::getImplementation() {
-    CHECK(instance_ != nullptr);
-    return instance_;
-}
-
-}  // namespace implementation
-}  // namespace V2_0
-}  // namespace health
-}  // namespace hardware
-}  // namespace android
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
deleted file mode 100644
index 3006644..0000000
--- a/healthd/healthd_common.cpp
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#define LOG_TAG "healthd-common"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-#include <healthd/BatteryMonitor.h>
-
-#include <errno.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <batteryservice/BatteryService.h>
-#include <cutils/klog.h>
-#include <cutils/uevent.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <utils/Errors.h>
-
-#include <health2/Health.h>
-
-using namespace android;
-
-// Periodic chores fast interval in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-// Periodic chores fast interval in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
-
-static struct healthd_config healthd_config = {
-    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
-    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
-    .batteryStatusPath = String8(String8::kEmptyString),
-    .batteryHealthPath = String8(String8::kEmptyString),
-    .batteryPresentPath = String8(String8::kEmptyString),
-    .batteryCapacityPath = String8(String8::kEmptyString),
-    .batteryVoltagePath = String8(String8::kEmptyString),
-    .batteryTemperaturePath = String8(String8::kEmptyString),
-    .batteryTechnologyPath = String8(String8::kEmptyString),
-    .batteryCurrentNowPath = String8(String8::kEmptyString),
-    .batteryCurrentAvgPath = String8(String8::kEmptyString),
-    .batteryChargeCounterPath = String8(String8::kEmptyString),
-    .batteryFullChargePath = String8(String8::kEmptyString),
-    .batteryCycleCountPath = String8(String8::kEmptyString),
-    .energyCounter = NULL,
-    .boot_min_cap = 0,
-    .screen_on = NULL,
-};
-
-static int eventct;
-static int epollfd;
-
-#define POWER_SUPPLY_SUBSYSTEM "power_supply"
-
-// epoll_create() parameter is actually unused
-#define MAX_EPOLL_EVENTS 40
-static int uevent_fd;
-static int wakealarm_fd;
-
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
-
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-
-using ::android::hardware::health::V2_0::implementation::Health;
-
-struct healthd_mode_ops *healthd_mode_ops = nullptr;
-
-int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
-    struct epoll_event ev;
-
-    ev.events = EPOLLIN;
-
-    if (wakeup == EVENT_WAKEUP_FD)
-        ev.events |= EPOLLWAKEUP;
-
-    ev.data.ptr = (void *)handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-        KLOG_ERROR(LOG_TAG,
-                   "epoll_ctl failed; errno=%d\n", errno);
-        return -1;
-    }
-
-    eventct++;
-    return 0;
-}
-
-static void wakealarm_set_interval(int interval) {
-    struct itimerspec itval;
-
-    if (wakealarm_fd == -1)
-            return;
-
-    wakealarm_wake_interval = interval;
-
-    if (interval == -1)
-        interval = 0;
-
-    itval.it_interval.tv_sec = interval;
-    itval.it_interval.tv_nsec = 0;
-    itval.it_value.tv_sec = interval;
-    itval.it_value.tv_nsec = 0;
-
-    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
-        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
-}
-
-void healthd_battery_update_internal(bool charger_online) {
-    // Fast wake interval when on charger (watch for overheat);
-    // slow wake interval when on battery (watch for drained battery).
-
-    int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast
-                                           : healthd_config.periodic_chores_interval_slow;
-
-    if (new_wake_interval != wakealarm_wake_interval)
-            wakealarm_set_interval(new_wake_interval);
-
-    // During awake periods poll at fast rate.  If wake alarm is set at fast
-    // rate then just use the alarm; if wake alarm is set at slow rate then
-    // poll at fast rate while awake and let alarm wake up at slow rate when
-    // asleep.
-
-    if (healthd_config.periodic_chores_interval_fast == -1)
-        awake_poll_interval = -1;
-    else
-        awake_poll_interval =
-            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
-                -1 : healthd_config.periodic_chores_interval_fast * 1000;
-}
-
-static void healthd_battery_update(void) {
-    Health::getImplementation()->update();
-}
-
-static void periodic_chores() {
-    healthd_battery_update();
-}
-
-#define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
-    char msg[UEVENT_MSG_LEN+2];
-    char *cp;
-    int n;
-
-    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
-    if (n <= 0)
-        return;
-    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-        return;
-
-    msg[n] = '\0';
-    msg[n+1] = '\0';
-    cp = msg;
-
-    while (*cp) {
-        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
-            healthd_battery_update();
-            break;
-        }
-
-        /* advance to after the next \0 */
-        while (*cp++)
-            ;
-    }
-}
-
-static void uevent_init(void) {
-    uevent_fd = uevent_open_socket(64*1024, true);
-
-    if (uevent_fd < 0) {
-        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
-        return;
-    }
-
-    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
-    if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "register for uevent events failed\n");
-}
-
-static void wakealarm_event(uint32_t /*epevents*/) {
-    unsigned long long wakeups;
-
-    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
-        return;
-    }
-
-    periodic_chores();
-}
-
-static void wakealarm_init(void) {
-    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
-    if (wakealarm_fd == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
-        return;
-    }
-
-    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "Registration of wakealarm event failed\n");
-
-    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
-}
-
-static void healthd_mainloop(void) {
-    int nevents = 0;
-    while (1) {
-        struct epoll_event events[eventct];
-        int timeout = awake_poll_interval;
-        int mode_timeout;
-
-        /* Don't wait for first timer timeout to run periodic chores */
-        if (!nevents)
-            periodic_chores();
-
-        healthd_mode_ops->heartbeat();
-
-        mode_timeout = healthd_mode_ops->preparetowait();
-        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
-            timeout = mode_timeout;
-        nevents = epoll_wait(epollfd, events, eventct, timeout);
-        if (nevents == -1) {
-            if (errno == EINTR)
-                continue;
-            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
-            break;
-        }
-
-        for (int n = 0; n < nevents; ++n) {
-            if (events[n].data.ptr)
-                (*(void (*)(int))events[n].data.ptr)(events[n].events);
-        }
-    }
-
-    return;
-}
-
-static int healthd_init() {
-    epollfd = epoll_create(MAX_EPOLL_EVENTS);
-    if (epollfd == -1) {
-        KLOG_ERROR(LOG_TAG,
-                   "epoll_create failed; errno=%d\n",
-                   errno);
-        return -1;
-    }
-
-    healthd_mode_ops->init(&healthd_config);
-    wakealarm_init();
-    uevent_init();
-
-    return 0;
-}
-
-int healthd_main() {
-    int ret;
-
-    klog_set_level(KLOG_LEVEL);
-
-    if (!healthd_mode_ops) {
-        KLOG_ERROR("healthd ops not set, exiting\n");
-        exit(1);
-    }
-
-    ret = healthd_init();
-    if (ret) {
-        KLOG_ERROR("Initialization failed, exiting\n");
-        exit(2);
-    }
-
-    healthd_mainloop();
-    KLOG_ERROR("Main loop terminated, exiting\n");
-    return 3;
-}
diff --git a/healthd/include/health2/Health.h b/healthd/include/health2/Health.h
deleted file mode 100644
index 012b95b..0000000
--- a/healthd/include/health2/Health.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
-#define ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
-
-#include <memory>
-#include <vector>
-
-#include <android/hardware/health/1.0/types.h>
-#include <android/hardware/health/2.0/IHealth.h>
-#include <healthd/BatteryMonitor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace health {
-namespace V2_0 {
-namespace implementation {
-
-using V1_0::BatteryStatus;
-using V1_0::HealthInfo;
-
-using ::android::hidl::base::V1_0::IBase;
-
-struct Health : public IHealth, hidl_death_recipient {
-  public:
-    static sp<IHealth> initInstance(struct healthd_config* c);
-    // Should only be called by implementation itself (-impl, -service).
-    // Clients should not call this function. Instead, initInstance() initializes and returns the
-    // global instance that has fewer functions.
-    // TODO(b/62229583): clean up and hide these functions after update() logic is simplified.
-    static sp<Health> getImplementation();
-
-    Health(struct healthd_config* c);
-
-    // TODO(b/62229583): clean up and hide these functions after update() logic is simplified.
-    void notifyListeners(const HealthInfo& info);
-
-    // Methods from IHealth follow.
-    Return<Result> registerCallback(const sp<IHealthInfoCallback>& callback) override;
-    Return<Result> unregisterCallback(const sp<IHealthInfoCallback>& callback) override;
-    Return<Result> update() override;
-    Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override;
-    Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override;
-    Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override;
-    Return<void> getCapacity(getCapacity_cb _hidl_cb) override;
-    Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override;
-    Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override;
-
-    // Methods from ::android::hidl::base::V1_0::IBase follow.
-    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
-
-    void serviceDied(uint64_t cookie, const wp<IBase>& /* who */) override;
-
-  private:
-    static sp<Health> instance_;
-
-    std::mutex callbacks_lock_;
-    std::vector<sp<IHealthInfoCallback>> callbacks_;
-    std::unique_ptr<BatteryMonitor> battery_monitor_;
-
-    bool unregisterCallbackInternal(const sp<IBase>& cb);
-
-};
-
-}  // namespace implementation
-}  // namespace V2_0
-}  // namespace health
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 5d160ee..f3c70de 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -35,6 +35,10 @@
 #include <android-base/macros.h>
 #include <android-base/strings.h>
 
+#ifdef __BIONIC__
+#include <android-base/properties.h>
+#endif
+
 #define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
                                              "%s:%d: %s CHECK '" #predicate "' failed.",\
                                              __FILE__, __LINE__, __FUNCTION__)
@@ -110,6 +114,25 @@
   return std::string(debuggable) == "1";
 }
 
+static std::string vndk_version_str() {
+#ifdef __BIONIC__
+  std::string version = android::base::GetProperty("ro.vndk.version", "");
+  if (version != "" && version != "current") {
+    return "." + version;
+  }
+#endif
+  return "";
+}
+
+static void insert_vndk_version_str(std::string* file_name) {
+  CHECK(file_name != nullptr);
+  size_t insert_pos = file_name->find_last_of(".");
+  if (insert_pos == std::string::npos) {
+    insert_pos = file_name->length();
+  }
+  file_name->insert(insert_pos, vndk_version_str());
+}
+
 class LibraryNamespaces {
  public:
   LibraryNamespaces() : initialized_(false) { }
@@ -318,6 +341,10 @@
                         "Error reading public native library list from \"%s\": %s",
                         public_native_libraries_system_config.c_str(), error_msg.c_str());
 
+    // Insert VNDK version to llndk and vndksp config file names.
+    insert_vndk_version_str(&llndk_native_libraries_system_config);
+    insert_vndk_version_str(&vndksp_native_libraries_system_config);
+
     // For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
     // variable to add libraries to the list. This is intended for platform tests only.
     if (is_debuggable()) {
@@ -347,11 +374,11 @@
     system_public_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
-    ReadConfig(kLlndkNativeLibrariesSystemConfigPathFromRoot, &sonames);
+    ReadConfig(llndk_native_libraries_system_config, &sonames);
     system_llndk_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
-    ReadConfig(kVndkspNativeLibrariesSystemConfigPathFromRoot, &sonames);
+    ReadConfig(vndksp_native_libraries_system_config, &sonames);
     system_vndksp_libraries_ = base::Join(sonames, ':');
 
     sonames.clear();
diff --git a/property_service/.clang-format b/property_service/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/property_service/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/property_service/Android.bp b/property_service/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/property_service/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
new file mode 100644
index 0000000..3e732b5
--- /dev/null
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -0,0 +1,16 @@
+cc_library_static {
+    name: "libpropertyinfoparser",
+    srcs: ["property_info_parser.cpp"],
+
+    cpp_std: "experimental",
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+    },
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    stl: "none",
+    export_include_dirs: ["include"],
+}
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
new file mode 100644
index 0000000..8c3507e
--- /dev/null
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -0,0 +1,224 @@
+//
+// 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:
+  void CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+                        uint32_t* context_index, uint32_t* schema_index) const;
+
+  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
diff --git a/property_service/libpropertyinfoparser/property_info_parser.cpp b/property_service/libpropertyinfoparser/property_info_parser.cpp
new file mode 100644
index 0000000..89864dd
--- /dev/null
+++ b/property_service/libpropertyinfoparser/property_info_parser.cpp
@@ -0,0 +1,229 @@
+//
+// 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::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
+                                        uint32_t* context_index, uint32_t* schema_index) const {
+  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)) {
+      *context_index = trie_node.prefix(i)->context_index;
+      *schema_index = trie_node.prefix(i)->schema_index;
+      return;
+    }
+  }
+}
+
+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();
+    }
+
+    // Check prefixes at this node.  This comes after the node check since these prefixes are by
+    // definition longer than the node itself.
+    CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_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 '.'
+  CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+  // Return previously found 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
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
new file mode 100644
index 0000000..20e5e13
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -0,0 +1,38 @@
+cc_defaults {
+    name: "propertyinfoserializer_defaults",
+    cpp_std: "experimental",
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+    },
+    cppflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    static_libs: [
+        "libpropertyinfoparser",
+        "libbase",
+    ],
+}
+
+cc_library_static {
+    name: "libpropertyinfoserializer",
+    defaults: ["propertyinfoserializer_defaults"],
+    srcs: [
+        "property_info_serializer.cpp",
+        "trie_builder.cpp",
+        "trie_serializer.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "propertyinfoserializer_tests",
+    defaults: ["propertyinfoserializer_defaults"],
+    srcs: [
+        "trie_builder_test.cpp",
+        "property_info_serializer_test.cpp",
+    ],
+    static_libs: ["libpropertyinfoserializer"],
+}
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
new file mode 100644
index 0000000..f7e708e
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef PROPERTY_INFO_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_H
+
+#include <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
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer.cpp b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
new file mode 100644
index 0000000..656c96e
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "property_info_serializer/property_info_serializer.h"
+
+#include "property_info_parser/property_info_parser.h"
+
+#include <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
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
new file mode 100644
index 0000000..b3fae26
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -0,0 +1,848 @@
+//
+// 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},
+      {"testoneword", "8th", "8th", true},  {"testwordprefix", "9th", "9th", false},
+  };
+
+  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);
+
+  property_info_area->GetPropertyInfo("testoneword", &context, &schema);
+  EXPECT_STREQ("8th", context);
+  EXPECT_STREQ("8th", schema);
+
+  property_info_area->GetPropertyInfo("testwordprefix", &context, &schema);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", schema);
+
+  property_info_area->GetPropertyInfo("testwordprefixblah", &context, &schema);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", schema);
+
+  property_info_area->GetPropertyInfo("testwordprefix.blah", &context, &schema);
+  EXPECT_STREQ("9th", context);
+  EXPECT_STREQ("9th", 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;
+  }
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_without_dot) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.radio", "1st", "1st", false},
+      {"persist.radio.something.else.here", "2nd", "2nd", false},
+  };
+
+  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());
+
+  const char* context;
+  const char* schema;
+  property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.long.long.long.sub.property", &context,
+                                      &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here2", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.here.after", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else.nothere", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio.something.else", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+}
+
+TEST(propertyinfoserializer, GetPropertyInfo_prefix_with_dot_vs_without) {
+  auto property_info = std::vector<PropertyInfoEntry>{
+      {"persist.", "1st", "1st", false},
+      {"persist.radio", "2nd", "2nd", false},
+      {"persist.radio.long.property.exact.match", "3rd", "3rd", 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());
+
+  const char* context;
+  const char* schema;
+  property_info_area->GetPropertyInfo("persist.notradio", &context, &schema);
+  EXPECT_STREQ("1st", context);
+  EXPECT_STREQ("1st", schema);
+  property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.long.property.prefix.match", &context, &schema);
+  EXPECT_STREQ("2nd", context);
+  EXPECT_STREQ("2nd", schema);
+  property_info_area->GetPropertyInfo("persist.radio.long.property.exact.match", &context, &schema);
+  EXPECT_STREQ("3rd", context);
+  EXPECT_STREQ("3rd", schema);
+}
+
+}  // namespace properties
+}  // namespace android
diff --git a/property_service/libpropertyinfoserializer/trie_builder.cpp b/property_service/libpropertyinfoserializer/trie_builder.cpp
new file mode 100644
index 0000000..feb753b
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.cpp
@@ -0,0 +1,105 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "trie_builder.h"
+
+#include <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
diff --git a/property_service/libpropertyinfoserializer/trie_builder.h b/property_service/libpropertyinfoserializer/trie_builder.h
new file mode 100644
index 0000000..f928e76
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder.h
@@ -0,0 +1,124 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H
+
+#include <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
diff --git a/property_service/libpropertyinfoserializer/trie_builder_test.cpp b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
new file mode 100644
index 0000000..2b948f3
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
@@ -0,0 +1,129 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "trie_builder.h"
+
+#include <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
diff --git a/property_service/libpropertyinfoserializer/trie_node_arena.h b/property_service/libpropertyinfoserializer/trie_node_arena.h
new file mode 100644
index 0000000..5e0ef82
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_node_arena.h
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H
+
+#include <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
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.cpp b/property_service/libpropertyinfoserializer/trie_serializer.cpp
new file mode 100644
index 0000000..5326537
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.cpp
@@ -0,0 +1,142 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "trie_serializer.h"
+
+namespace android {
+namespace properties {
+
+// Serialized strings contains:
+// 1) A uint32_t count of elements in the below array
+// 2) A sorted array of uint32_t offsets pointing to null terminated strings
+// 3) Each of the null terminated strings themselves packed back to back
+// This returns the offset into arena where the serialized strings start.
+void TrieSerializer::SerializeStrings(const std::set<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
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.h b/property_service/libpropertyinfoserializer/trie_serializer.h
new file mode 100644
index 0000000..e4d3343
--- /dev/null
+++ b/property_service/libpropertyinfoserializer/trie_serializer.h
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H
+
+#include <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
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index e9bd8d4..492d63a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -125,6 +125,18 @@
 bcp_md5 :=
 bcp_dep :=
 
+# If PLATFORM_VNDK_VERSION is defined and not "current", generate versioned
+# module names for ld.config.txt, llndk.libraries.txt and vndksp.libraries.txt
+# files.
+define versioned_module_name
+$(strip \
+  $(if $(filter-out current,$(PLATFORM_VNDK_VERSION)), \
+    $(basename $(LOCAL_MODULE)).$(PLATFORM_VNDK_VERSION)$(suffix $(LOCAL_MODULE)), \
+    $(LOCAL_MODULE) \
+  ) \
+)
+endef
+
 #######################################
 # ld.config.txt
 include $(CLEAR_VARS)
@@ -141,7 +153,7 @@
 LOCAL_MODULE := ld.config.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
 include $(BUILD_SYSTEM)/base_rules.mk
 
 llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
@@ -187,13 +199,14 @@
 
 LOCAL_MODULE := ld.config.txt
 ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
-LOCAL_SRC_FILES := etc/ld.config.txt
+  LOCAL_SRC_FILES := etc/ld.config.txt
+  LOCAL_MODULE_STEM := $(call versioned_module_name)
 else
-LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+  LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+  LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 endif
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
 endif
 
@@ -203,7 +216,7 @@
 LOCAL_MODULE := llndk.libraries.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
 include $(BUILD_SYSTEM)/base_rules.mk
 $(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
 $(LOCAL_BUILT_MODULE):
@@ -219,7 +232,7 @@
 LOCAL_MODULE := vndksp.libraries.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call versioned_module_name)
 include $(BUILD_SYSTEM)/base_rules.mk
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
 $(LOCAL_BUILT_MODULE):
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 3183b80..2cba1e3 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -76,7 +76,7 @@
 namespace.sphal.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libvndksupport.so:libz.so
 
 # WARNING: only VNDK-SP libs can be listed here. DO NOT EDIT this line.
-namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+namespace.sphal.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -99,7 +99,7 @@
 
 namespace.rs.links = default,vndk
 namespace.rs.link.default.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so:liblog.so:libnativewindow.so:libEGL.so:libsync.so:libGLESv1_CM.so:libGLESv2.so:libmediandk.so:libvndksupport.so:libz.so:libft2.so
-namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.allocator@2.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
+namespace.rs.link.vndk.shared_libs = android.hardware.renderscript@1.0.so:android.hardware.graphics.mapper@2.0.so:android.hardware.graphics.common@1.0.so:android.hidl.memory@1.0.so:libhwbinder.so:libbase.so:libcutils.so:libhardware.so:libhidlbase.so:libhidlmemory.so:libhidltransport.so:libion.so:libutils.so:libc++.so
 
 ###############################################################################
 # "vndk" namespace
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index 2d05fca..7055393 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -30,10 +30,10 @@
 namespace.default.search.paths = /system/${LIB}
 # /vendor/app, /vendor/framework were added since libart should be able to dlopen
 # the odex files from the directory.
-namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
+namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/priv-app:/vendor/framework:/oem/app:/data:/mnt/expand
 
 namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
+namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/priv-app:/vendor/framework:/oem/app:/mnt/expand
 
 ###############################################################################
 # "sphal" namespace
@@ -136,7 +136,8 @@
 namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}
 namespace.default.permitted.paths = /vendor
 
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor
+namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor
 
 namespace.default.links = system
 namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%:%VNDK_SAMEPROCESS_LIBRARIES%:%VNDK_CORE_LIBRARIES%