Merge "Add global GCOV_PREFIX option."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ece143c..577e9b9 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1129,6 +1129,18 @@
         return 0;
     }
 
+#if ADB_HOST
+    if (!strcmp(service, "host-features")) {
+        FeatureSet features = supported_features();
+        // Abuse features to report libusb status.
+        if (should_use_libusb()) {
+            features.insert(kFeatureLibusb);
+        }
+        SendOkay(reply_fd, FeatureSetToString(features));
+        return 0;
+    }
+#endif
+
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
         const std::string address(service + 11);
diff --git a/adb/adb.h b/adb/adb.h
index a30e297..aea5fb8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -54,7 +54,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 38
+#define ADB_SERVER_VERSION 39
 
 class atransport;
 
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7afa616..7058acb 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -19,16 +19,15 @@
 #include "adb_utils.h"
 #include "adb_unique_fd.h"
 
-#include <libgen.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <algorithm>
-#include <mutex>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
@@ -100,52 +99,6 @@
   return result;
 }
 
-std::string adb_basename(const std::string& path) {
-  static std::mutex& basename_lock = *new std::mutex();
-
-  // Copy path because basename may modify the string passed in.
-  std::string result(path);
-
-  // Use lock because basename() may write to a process global and return a
-  // pointer to that. Note that this locking strategy only works if all other
-  // callers to basename in the process also grab this same lock.
-  std::lock_guard<std::mutex> lock(basename_lock);
-
-  // Note that if std::string uses copy-on-write strings, &str[0] will cause
-  // the copy to be made, so there is no chance of us accidentally writing to
-  // the storage for 'path'.
-  char* name = basename(&result[0]);
-
-  // In case dirname returned a pointer to a process global, copy that string
-  // before leaving the lock.
-  result.assign(name);
-
-  return result;
-}
-
-std::string adb_dirname(const std::string& path) {
-  static std::mutex& dirname_lock = *new std::mutex();
-
-  // Copy path because dirname may modify the string passed in.
-  std::string result(path);
-
-  // Use lock because dirname() may write to a process global and return a
-  // pointer to that. Note that this locking strategy only works if all other
-  // callers to dirname in the process also grab this same lock.
-  std::lock_guard<std::mutex> lock(dirname_lock);
-
-  // Note that if std::string uses copy-on-write strings, &str[0] will cause
-  // the copy to be made, so there is no chance of us accidentally writing to
-  // the storage for 'path'.
-  char* parent = dirname(&result[0]);
-
-  // In case dirname returned a pointer to a process global, copy that string
-  // before leaving the lock.
-  result.assign(parent);
-
-  return result;
-}
-
 // Given a relative or absolute filepath, create the directory hierarchy
 // as needed. Returns true if the hierarchy is/was setup.
 bool mkdirs(const std::string& path) {
@@ -171,7 +124,7 @@
     return true;
   }
 
-  const std::string parent(adb_dirname(path));
+  const std::string parent(android::base::Dirname(path));
 
   // If dirname returned the same path as what we passed in, don't go recursive.
   // This can happen on Windows when walking up the directory hierarchy and not
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 2b59034..e0ad103 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -28,11 +28,6 @@
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
 
-// Like the regular basename and dirname, but thread-safe on all
-// platforms and capable of correctly handling exotic Windows paths.
-std::string adb_basename(const std::string& path);
-std::string adb_dirname(const std::string& path);
-
 // Return the user's home directory.
 std::string adb_get_homedir_path();
 
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 4cac485..a3bc445 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -109,18 +109,6 @@
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
 
-TEST(adb_utils, adb_basename) {
-  EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
-  EXPECT_EQ("sh", adb_basename("sh"));
-  EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
-}
-
-TEST(adb_utils, adb_dirname) {
-  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
-  EXPECT_EQ(".", adb_dirname("sh"));
-  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
-}
-
 void test_mkdirs(const std::string& basepath) {
   // Test creating a directory hierarchy.
   ASSERT_TRUE(mkdirs(basepath));
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index b7e76a6..a5c312b 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/strings.h>
 
 #include "sysdeps.h"
@@ -113,14 +114,14 @@
 
   private:
     void SetLineMessage(const std::string& action) {
-        line_message_ = action + " " + adb_basename(dest_file_);
+        line_message_ = action + " " + android::base::Basename(dest_file_);
     }
 
     void SetSrcFile(const std::string path) {
         src_file_ = path;
         if (!dest_dir_.empty()) {
             // Only uses device-provided name when user passed a directory.
-            dest_file_ = adb_basename(path);
+            dest_file_ = android::base::Basename(path);
             SetLineMessage("generating");
         }
     }
diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp
index f02dccf..bfc8e16 100644
--- a/adb/client/usb_dispatch.cpp
+++ b/adb/client/usb_dispatch.cpp
@@ -17,11 +17,6 @@
 #include <android-base/logging.h>
 #include "usb.h"
 
-static bool should_use_libusb() {
-    static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
-    return enable;
-}
-
 void usb_init() {
     if (should_use_libusb()) {
         LOG(DEBUG) << "using libusb backend";
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 3b2df2e..3de7be6 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1929,8 +1929,7 @@
     else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
         fprintf(stdout, "%s", adb_version().c_str());
         return 0;
-    }
-    else if (!strcmp(argv[0], "features")) {
+    } else if (!strcmp(argv[0], "features")) {
         // Only list the features common to both the adb client and the device.
         FeatureSet features;
         std::string error;
@@ -1945,6 +1944,8 @@
             }
         }
         return 0;
+    } else if (!strcmp(argv[0], "host-features")) {
+        return adb_query_command("host:host-features");
     } else if (!strcmp(argv[0], "reconnect")) {
         if (argc == 1) {
             return adb_query_command("host:reconnect");
@@ -2114,7 +2115,8 @@
 
         std::string cmd = android::base::StringPrintf(
                 "%s install-write -S %" PRIu64 " %d %d_%s -",
-                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
+                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
+                android::base::Basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
@@ -2232,7 +2234,7 @@
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
     std::string apk_dest = android::base::StringPrintf(
-        where, adb_basename(argv[last_apk]).c_str());
+        where, android::base::Basename(argv[last_apk]).c_str());
     if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
     result = pm_command(transport, serial, argc, argv);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 271943d..22bd2f2 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -844,7 +844,8 @@
         // TODO(b/25566053): Make pushing empty directories work.
         // TODO(b/25457350): We don't preserve permissions on directories.
         sc.Warning("skipping empty directory '%s'", lpath.c_str());
-        copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+        copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+                    android::base::Basename(lpath), S_IFDIR);
         ci.skip = true;
         file_list->push_back(ci);
         return true;
@@ -977,7 +978,7 @@
                 if (dst_dir.back() != '/') {
                     dst_dir.push_back('/');
                 }
-                dst_dir.append(adb_basename(src_path));
+                dst_dir.append(android::base::Basename(src_path));
             }
 
             success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
@@ -995,7 +996,7 @@
             if (path_holder.back() != '/') {
                 path_holder.push_back('/');
             }
-            path_holder += adb_basename(src_path);
+            path_holder += android::base::Basename(src_path);
             dst_path = path_holder.c_str();
         }
 
@@ -1015,7 +1016,8 @@
     std::vector<copyinfo> linklist;
 
     // Add an entry for the current directory to ensure it gets created before pulling its contents.
-    copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+    copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+                android::base::Basename(lpath), S_IFDIR);
     file_list->push_back(ci);
 
     // Put the files/dirs in rpath on the lists.
@@ -1149,7 +1151,7 @@
         if (srcs.size() == 1 && errno == ENOENT) {
             // However, its parent must exist.
             struct stat parent_st;
-            if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+            if (stat(android::base::Dirname(dst).c_str(), &parent_st) == -1) {
                 sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
                 return false;
             }
@@ -1204,7 +1206,7 @@
                 if (!adb_is_separator(dst_dir.back())) {
                     dst_dir.push_back(OS_PATH_SEPARATOR);
                 }
-                dst_dir.append(adb_basename(src_path));
+                dst_dir.append(android::base::Basename(src_path));
             }
 
             success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
@@ -1220,7 +1222,7 @@
             // really want to copy to local_dir + OS_PATH_SEPARATOR +
             // basename(remote).
             path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
-                                                      adb_basename(src_path).c_str());
+                                                      android::base::Basename(src_path).c_str());
             dst_path = path_holder.c_str();
         }
 
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index e667bf8..2acf661 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <utime.h>
 
+#include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <private/android_filesystem_config.h>
@@ -206,7 +207,7 @@
 
     int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
     if (fd < 0 && errno == ENOENT) {
-        if (!secure_mkdirs(adb_dirname(path))) {
+        if (!secure_mkdirs(android::base::Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             goto fail;
         }
@@ -333,7 +334,7 @@
 
     ret = symlink(&buffer[0], path.c_str());
     if (ret && errno == ENOENT) {
-        if (!secure_mkdirs(adb_dirname(path))) {
+        if (!secure_mkdirs(android::base::Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             return false;
         }
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index f9e028b..76b156d 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -108,11 +108,10 @@
         return;
     }
 
-    std::string fstab_filename = "/fstab." + android::base::GetProperty("ro.hardware", "");
-
-    fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+    // read all fstab entries at once from all sources
+    fstab = fs_mgr_read_fstab_default();
     if (!fstab) {
-        WriteFdFmt(fd, "Failed to open %s\nMaybe run adb root?\n", fstab_filename.c_str());
+        WriteFdFmt(fd, "Failed to read fstab\nMaybe run adb root?\n");
         return;
     }
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 60f3b5c..c951f5b 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -51,6 +51,7 @@
 const char* const kFeatureShell2 = "shell_v2";
 const char* const kFeatureCmd = "cmd";
 const char* const kFeatureStat2 = "stat_v2";
+const char* const kFeatureLibusb = "libusb";
 
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned command = p->msg.command;
diff --git a/adb/transport.h b/adb/transport.h
index 3306388..490e513 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -47,6 +47,8 @@
 // The 'cmd' command is available
 extern const char* const kFeatureCmd;
 extern const char* const kFeatureStat2;
+// The server is running with libusb enabled.
+extern const char* const kFeatureLibusb;
 
 class atransport {
 public:
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 3d6cc99..e16cf12 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -97,3 +97,12 @@
 {
     return (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && usb_protocol == ADB_PROTOCOL);
 }
+
+bool should_use_libusb() {
+#if defined(_WIN32) || !ADB_HOST
+    return false;
+#else
+    static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
+    return enable;
+#endif
+}
diff --git a/adb/usb.h b/adb/usb.h
index 879bacc..ba70de4 100644
--- a/adb/usb.h
+++ b/adb/usb.h
@@ -55,3 +55,5 @@
 
 // USB device detection.
 int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol);
+
+bool should_use_libusb();
diff --git a/adf/libadf/Android.bp b/adf/libadf/Android.bp
index 1a81a49..c276c53 100644
--- a/adf/libadf/Android.bp
+++ b/adf/libadf/Android.bp
@@ -14,7 +14,7 @@
 
 cc_library_static {
     name: "libadf",
-    srcs: ["adf.c"],
+    srcs: ["adf.cpp"],
     cflags: ["-Werror"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
diff --git a/adf/libadf/adf.c b/adf/libadf/adf.cpp
similarity index 73%
rename from adf/libadf/adf.c
rename to adf/libadf/adf.cpp
index 10f88b0..60d8ef0 100644
--- a/adf/libadf/adf.c
+++ b/adf/libadf/adf.cpp
@@ -23,6 +23,10 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <memory>
+#include <vector>
+
 #include <linux/limits.h>
 
 #include <sys/ioctl.h>
@@ -31,52 +35,44 @@
 
 #define ADF_BASE_PATH "/dev/"
 
-static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids)
+static ssize_t adf_id_vector_to_array(const std::vector<adf_id_t> &in,
+        adf_id_t **out)
 {
-    DIR *dir;
-    struct dirent *dirent;
-    size_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
+    auto size = sizeof(in[0]) * in.size();
+    // We can't use new[] since the existing API says the caller should free()
+    // the returned array
+    auto ret = static_cast<adf_id_t *>(malloc(size));
+    if (!ret)
+        return -ENOMEM;
 
-    dir = opendir(ADF_BASE_PATH);
+    std::copy(in.begin(), in.end(), ret);
+    *out = ret;
+    return in.size();
+}
+
+static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids_out)
+{
+    struct dirent *dirent;
+    std::unique_ptr<DIR, decltype(&closedir)>
+        dir{opendir(ADF_BASE_PATH), closedir};
     if (!dir)
         return -errno;
 
+    std::vector<adf_id_t> ids;
     errno = 0;
-    while ((dirent = readdir(dir))) {
+    while ((dirent = readdir(dir.get()))) {
         adf_id_t id;
         int matched = sscanf(dirent->d_name, pattern, &id);
 
-        if (matched < 0) {
-            ret = -errno;
-            goto done;
-        } else if (matched != 1) {
-            continue;
-        }
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = id;
-        n++;
+        if (matched < 0)
+            return -errno;
+        else if (matched == 1)
+            ids.push_back(id);
     }
     if (errno)
-        ret = -errno;
-    else
-        ret = n;
+        return -errno;
 
-done:
-    closedir(dir);
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *ids = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, ids_out);
 }
 
 ssize_t adf_devices(adf_id_t **ids)
@@ -115,46 +111,29 @@
     if (err < 0)
         return -ENOMEM;
 
-    if (data->n_attachments) {
-        data->attachments = malloc(sizeof(data->attachments[0]) *
-                data->n_attachments);
-        if (!data->attachments)
-            return -ENOMEM;
-    }
+    if (data->n_attachments)
+        data->attachments = new adf_attachment_config[data->n_attachments];
 
-    if (data->n_allowed_attachments) {
+    if (data->n_allowed_attachments)
         data->allowed_attachments =
-                malloc(sizeof(data->allowed_attachments[0]) *
-                        data->n_allowed_attachments);
-        if (!data->allowed_attachments) {
-            ret = -ENOMEM;
-            goto done;
-        }
-    }
+                new adf_attachment_config[data->n_allowed_attachments];
 
-    if (data->custom_data_size) {
-        data->custom_data = malloc(data->custom_data_size);
-        if (!data->custom_data) {
-            ret = -ENOMEM;
-            goto done;
-        }
-    }
+    if (data->custom_data_size)
+        data->custom_data = new char[data->custom_data_size];
 
     err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data);
-    if (err < 0)
+    if (err < 0) {
         ret = -errno;
-
-done:
-    if (ret < 0)
         adf_free_device_data(data);
+    }
     return ret;
 }
 
 void adf_free_device_data(struct adf_device_data *data)
 {
-    free(data->attachments);
-    free(data->allowed_attachments);
-    free(data->custom_data);
+    delete [] data->attachments;
+    delete [] data->allowed_attachments;
+    delete [] static_cast<char *>(data->custom_data);
 }
 
 int adf_device_post(struct adf_device *dev,
@@ -252,39 +231,17 @@
         adf_id_t overlay_engine, adf_id_t **interfaces)
 {
     struct adf_device_data data;
-    ssize_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
+    auto err = adf_get_device_data(dev, &data);
+    if (err < 0)
+        return err;
 
-    ret = adf_get_device_data(dev, &data);
-    if (ret < 0)
-        return ret;
+    std::vector<adf_id_t> ids;
+    for (size_t i = 0; i < data.n_allowed_attachments; i++)
+        if (data.allowed_attachments[i].overlay_engine == overlay_engine)
+            ids.push_back(data.allowed_attachments[i].interface);
 
-    size_t i;
-    for (i = 0; i < data.n_allowed_attachments; i++) {
-        if (data.allowed_attachments[i].overlay_engine != overlay_engine)
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = data.allowed_attachments[i].interface;
-        n++;
-    }
-
-    ret = n;
-
-done:
     adf_free_device_data(&data);
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *interfaces = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, interfaces);
 }
 
 static ssize_t adf_interfaces_filter(struct adf_device *dev,
@@ -292,46 +249,23 @@
         bool (*filter)(struct adf_interface_data *data, __u32 match),
         __u32 match)
 {
-    size_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
-
-    size_t i;
-    for (i = 0; i < n_in; i++) {
+    std::vector<adf_id_t> ids;
+    for (size_t i = 0; i < n_in; i++) {
         int fd = adf_interface_open(dev, in[i], O_RDONLY);
-        if (fd < 0) {
-            ret = fd;
-            goto done;
-        }
+        if (fd < 0)
+            return fd;
 
         struct adf_interface_data data;
-        ret = adf_get_interface_data(fd, &data);
+        auto ret = adf_get_interface_data(fd, &data);
         close(fd);
         if (ret < 0)
-            goto done;
+            return ret;
 
-        if (!filter(&data, match))
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = in[i];
-        n++;
+        if (filter(&data, match))
+            ids.push_back(in[i]);
     }
 
-    ret = n;
-
-done:
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *out = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, out);
 }
 
 static bool adf_interface_type_filter(struct adf_interface_data *data,
@@ -385,35 +319,24 @@
     if (err < 0)
         return -errno;
 
-    if (data->n_available_modes) {
-        data->available_modes = malloc(sizeof(data->available_modes[0]) *
-                data->n_available_modes);
-        if (!data->available_modes)
-            return -ENOMEM;
-    }
+    if (data->n_available_modes)
+        data->available_modes = new drm_mode_modeinfo[data->n_available_modes];
 
-    if (data->custom_data_size) {
-        data->custom_data = malloc(data->custom_data_size);
-        if (!data->custom_data) {
-            ret = -ENOMEM;
-            goto done;
-        }
-    }
+    if (data->custom_data_size)
+        data->custom_data = new char[data->custom_data_size];
 
     err = ioctl(fd, ADF_GET_INTERFACE_DATA, data);
-    if (err < 0)
+    if (err < 0) {
         ret = -errno;
-
-done:
-    if (ret < 0)
         adf_free_interface_data(data);
+    }
     return ret;
 }
 
 void adf_free_interface_data(struct adf_interface_data *data)
 {
-    free(data->available_modes);
-    free(data->custom_data);
+    delete [] data->available_modes;
+    delete [] static_cast<char *>(data->custom_data);
 }
 
 int adf_interface_blank(int fd, __u8 mode)
@@ -522,39 +445,16 @@
         adf_id_t interface, adf_id_t **overlay_engines)
 {
     struct adf_device_data data;
-    ssize_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
+    auto err = adf_get_device_data(dev, &data);
+    if (err < 0)
+        return err;
 
-    ret = adf_get_device_data(dev, &data);
-    if (ret < 0)
-        return ret;
+    std::vector<adf_id_t> ids;
+    for (size_t i = 0; i < data.n_allowed_attachments; i++)
+        if (data.allowed_attachments[i].interface == interface)
+            ids.push_back(data.allowed_attachments[i].overlay_engine);
 
-    size_t i;
-    for (i = 0; i < data.n_allowed_attachments; i++) {
-        if (data.allowed_attachments[i].interface != interface)
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = data.allowed_attachments[i].overlay_engine;
-        n++;
-    }
-
-    ret = n;
-
-done:
-    adf_free_device_data(&data);
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *overlay_engines = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, overlay_engines);
 }
 
 static ssize_t adf_overlay_engines_filter(struct adf_device *dev,
@@ -562,46 +462,24 @@
         bool (*filter)(struct adf_overlay_engine_data *data, void *cookie),
         void *cookie)
 {
-    size_t n = 0;
-    ssize_t ret;
-    adf_id_t *ids_ret = NULL;
-
+    std::vector<adf_id_t> ids;
     size_t i;
     for (i = 0; i < n_in; i++) {
         int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY);
-        if (fd < 0) {
-            ret = fd;
-            goto done;
-        }
+        if (fd < 0)
+            return fd;
 
         struct adf_overlay_engine_data data;
-        ret = adf_get_overlay_engine_data(fd, &data);
+        auto ret = adf_get_overlay_engine_data(fd, &data);
         close(fd);
         if (ret < 0)
-            goto done;
+            return ret;
 
-        if (!filter(&data, cookie))
-            continue;
-
-        adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0]));
-        if (!new_ids) {
-            ret = -ENOMEM;
-            goto done;
-        }
-
-        ids_ret = new_ids;
-        ids_ret[n] = in[i];
-        n++;
+        if (filter(&data, cookie))
+            ids.push_back(in[i]);
     }
 
-    ret = n;
-
-done:
-    if (ret < 0)
-        free(ids_ret);
-    else
-        *out = ids_ret;
-    return ret;
+    return adf_id_vector_to_array(ids, out);
 }
 
 struct format_filter_cookie {
@@ -612,7 +490,7 @@
 static bool adf_overlay_engine_format_filter(
         struct adf_overlay_engine_data *data, void *cookie)
 {
-    struct format_filter_cookie *c = cookie;
+    auto c = static_cast<format_filter_cookie *>(cookie);
     size_t i;
     for (i = 0; i < data->n_supported_formats; i++) {
         size_t j;
@@ -656,35 +534,24 @@
     if (err < 0)
         return -errno;
 
-    if (data->n_supported_formats) {
-        data->supported_formats = malloc(sizeof(data->supported_formats[0]) *
-              data->n_supported_formats);
-        if (!data->supported_formats)
-            return -ENOMEM;
-    }
+    if (data->n_supported_formats)
+        data->supported_formats = new __u32[data->n_supported_formats];
 
-    if (data->custom_data_size) {
-      data->custom_data = malloc(data->custom_data_size);
-      if (!data->custom_data) {
-          ret = -ENOMEM;
-          goto done;
-      }
-    }
+    if (data->custom_data_size)
+      data->custom_data = new char[data->custom_data_size];
 
     err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data);
-    if (err < 0)
+    if (err < 0) {
         ret = -errno;
-
-done:
-    if (ret < 0)
         adf_free_overlay_engine_data(data);
+    }
     return ret;
 }
 
 void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
 {
-    free(data->supported_formats);
-    free(data->custom_data);
+    delete [] data->supported_formats;
+    delete [] static_cast<char *>(data->custom_data);
 }
 
 bool adf_overlay_engine_supports_format(int fd, __u32 format)
@@ -724,12 +591,12 @@
 int adf_read_event(int fd, struct adf_event **event)
 {
     struct adf_event header;
-    struct {
+    struct event_with_data {
         struct adf_event base;
         uint8_t data[0];
-    } *event_ret;
+    };
+    using unique_event = std::unique_ptr<event_with_data, decltype(&free)>;
     size_t data_size;
-    int ret = 0;
 
     int err = read(fd, &header, sizeof(header));
     if (err < 0)
@@ -739,28 +606,23 @@
     if (header.length < sizeof(header))
         return -EIO;
 
-    event_ret = malloc(header.length);
+    // Again, we can't use new[] since the existing API says the caller should
+    // free() the returned event
+    auto event_ptr = static_cast<event_with_data *>(malloc(header.length));
+    unique_event event_ret{event_ptr, free};
     if (!event_ret)
         return -ENOMEM;
     data_size = header.length - sizeof(header);
 
-    memcpy(event_ret, &header, sizeof(header));
+    memcpy(event_ret.get(), &header, sizeof(header));
     ssize_t read_size = read(fd, &event_ret->data, data_size);
-    if (read_size < 0) {
-        ret = -errno;
-        goto done;
-    }
-    if ((size_t)read_size < data_size) {
-        ret = -EIO;
-        goto done;
-    }
+    if (read_size < 0)
+        return -errno;
+    if ((size_t)read_size < data_size)
+        return -EIO;
 
-    *event = &event_ret->base;
-
-done:
-    if (ret < 0)
-        free(event_ret);
-    return ret;
+    *event = &event_ret.release()->base;
+    return 0;
 }
 
 void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE])
diff --git a/adf/libadf/tests/adf_test.cpp b/adf/libadf/tests/adf_test.cpp
index 4727c2b..82a91f4 100644
--- a/adf/libadf/tests/adf_test.cpp
+++ b/adf/libadf/tests/adf_test.cpp
@@ -188,7 +188,7 @@
 const size_t AdfTest::n_fmt8888 = sizeof(fmt8888) / sizeof(fmt8888[0]);
 
 TEST(adf, devices) {
-    adf_id_t *devs;
+    adf_id_t *devs = nullptr;
     ssize_t n_devs = adf_devices(&devs);
     free(devs);
 
diff --git a/base/Android.bp b/base/Android.bp
index 694e029..7b1dc8e 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -78,6 +78,7 @@
     host_supported: true,
     clang: true,
     srcs: [
+        "endian_test.cpp",
         "errors_test.cpp",
         "file_test.cpp",
         "logging_test.cpp",
diff --git a/base/endian_test.cpp b/base/endian_test.cpp
new file mode 100644
index 0000000..963ab13
--- /dev/null
+++ b/base/endian_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "android-base/endian.h"
+
+#include <gtest/gtest.h>
+
+TEST(endian, constants) {
+  ASSERT_TRUE(__LITTLE_ENDIAN == LITTLE_ENDIAN);
+  ASSERT_TRUE(__BIG_ENDIAN == BIG_ENDIAN);
+  ASSERT_TRUE(__BYTE_ORDER == BYTE_ORDER);
+
+  ASSERT_EQ(__LITTLE_ENDIAN, __BYTE_ORDER);
+}
+
+TEST(endian, smoke) {
+  static constexpr uint16_t le16 = 0x1234;
+  static constexpr uint32_t le32 = 0x12345678;
+  static constexpr uint64_t le64 = 0x123456789abcdef0;
+
+  static constexpr uint16_t be16 = 0x3412;
+  static constexpr uint32_t be32 = 0x78563412;
+  static constexpr uint64_t be64 = 0xf0debc9a78563412;
+
+  ASSERT_EQ(be16, htons(le16));
+  ASSERT_EQ(be32, htonl(le32));
+  ASSERT_EQ(be64, htonq(le64));
+
+  ASSERT_EQ(le16, ntohs(be16));
+  ASSERT_EQ(le32, ntohl(be32));
+  ASSERT_EQ(le64, ntohq(be64));
+
+  ASSERT_EQ(be16, htobe16(le16));
+  ASSERT_EQ(be32, htobe32(le32));
+  ASSERT_EQ(be64, htobe64(le64));
+
+  ASSERT_EQ(le16, betoh16(be16));
+  ASSERT_EQ(le32, betoh32(be32));
+  ASSERT_EQ(le64, betoh64(be64));
+
+  ASSERT_EQ(le16, htole16(le16));
+  ASSERT_EQ(le32, htole32(le32));
+  ASSERT_EQ(le64, htole64(le64));
+
+  ASSERT_EQ(le16, letoh16(le16));
+  ASSERT_EQ(le32, letoh32(le32));
+  ASSERT_EQ(le64, letoh64(le64));
+
+  ASSERT_EQ(le16, be16toh(be16));
+  ASSERT_EQ(le32, be32toh(be32));
+  ASSERT_EQ(le64, be64toh(be64));
+
+  ASSERT_EQ(le16, le16toh(le16));
+  ASSERT_EQ(le32, le32toh(le32));
+  ASSERT_EQ(le64, le64toh(le64));
+}
diff --git a/base/file.cpp b/base/file.cpp
index 6284b04..81b04d7 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,11 +18,13 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <libgen.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <memory>
+#include <mutex>
 #include <string>
 #include <vector>
 
@@ -236,5 +238,59 @@
 #endif
 }
 
+std::string GetExecutableDirectory() {
+  return Dirname(GetExecutablePath());
+}
+
+std::string Basename(const std::string& path) {
+  // Copy path because basename may modify the string passed in.
+  std::string result(path);
+
+#if !defined(__BIONIC__)
+  // Use lock because basename() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to basename in the process also grab this same lock, but its
+  // better than nothing.  Bionic's basename returns a thread-local buffer.
+  static std::mutex& basename_lock = *new std::mutex();
+  std::lock_guard<std::mutex> lock(basename_lock);
+#endif
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* name = basename(&result[0]);
+
+  // In case basename returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(name);
+
+  return result;
+}
+
+std::string Dirname(const std::string& path) {
+  // Copy path because dirname may modify the string passed in.
+  std::string result(path);
+
+#if !defined(__BIONIC__)
+  // Use lock because dirname() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock, but its
+  // better than nothing.  Bionic's dirname returns a thread-local buffer.
+  static std::mutex& dirname_lock = *new std::mutex();
+  std::lock_guard<std::mutex> lock(dirname_lock);
+#endif
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* parent = dirname(&result[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(parent);
+
+  return result;
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index ed39ce9..1021326 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -159,6 +159,26 @@
 #endif
 }
 
+TEST(file, GetExecutableDirectory) {
+  std::string path = android::base::GetExecutableDirectory();
+  ASSERT_NE("", path);
+  ASSERT_NE(android::base::GetExecutablePath(), path);
+  ASSERT_EQ('/', path[0]);
+  ASSERT_NE('/', path[path.size() - 1]);
+}
+
 TEST(file, GetExecutablePath) {
   ASSERT_NE("", android::base::GetExecutablePath());
 }
+
+TEST(file, Basename) {
+  EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
+  EXPECT_EQ("sh", android::base::Basename("sh"));
+  EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
+}
+
+TEST(file, Dirname) {
+  EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
+  EXPECT_EQ(".", android::base::Dirname("sh"));
+  EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
+}
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
new file mode 100644
index 0000000..6eb677c
--- /dev/null
+++ b/base/include/android-base/endian.h
@@ -0,0 +1,90 @@
+/*
+ * 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 ANDROID_BASE_ENDIAN_H
+#define ANDROID_BASE_ENDIAN_H
+
+/* A cross-platform equivalent of bionic's <sys/endian.h>. */
+
+#if defined(__BIONIC__)
+
+#include <sys/endian.h>
+
+#elif defined(__GLIBC__)
+
+/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
+#include <endian.h>
+
+/* glibc keeps htons and htonl in <netinet/in.h>. */
+#include <netinet/in.h>
+
+/* glibc doesn't have the 64-bit variants. */
+#define htonq(x) htobe64(x)
+#define ntohq(x) be64toh(x)
+
+/* glibc has different names to BSD for these. */
+#define betoh16(x) be16toh(x)
+#define betoh32(x) be32toh(x)
+#define betoh64(x) be64toh(x)
+
+#else
+
+/* Mac OS and Windows have nothing. */
+
+#define __LITTLE_ENDIAN 1234
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
+
+#define __BIG_ENDIAN 4321
+#define BIG_ENDIAN __BIG_ENDIAN
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#define BYTE_ORDER __BYTE_ORDER
+
+#define htons(x) __builtin_bswap16(x)
+#define htonl(x) __builtin_bswap32(x)
+#define htonq(x) __builtin_bswap64(x)
+
+#define ntohs(x) __builtin_bswap16(x)
+#define ntohl(x) __builtin_bswap32(x)
+#define ntohq(x) __builtin_bswap64(x)
+
+#define htobe16(x) __builtin_bswap16(x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htobe64(x) __builtin_bswap64(x)
+
+#define betoh16(x) __builtin_bswap16(x)
+#define betoh32(x) __builtin_bswap32(x)
+#define betoh64(x) __builtin_bswap64(x)
+
+#define htole16(x) (x)
+#define htole32(x) (x)
+#define htole64(x) (x)
+
+#define letoh16(x) (x)
+#define letoh32(x) (x)
+#define letoh64(x) (x)
+
+#define be16toh(x) __builtin_bswap16(x)
+#define be32toh(x) __builtin_bswap32(x)
+#define be64toh(x) __builtin_bswap64(x)
+
+#define le16toh(x) (x)
+#define le32toh(x) (x)
+#define le64toh(x) (x)
+
+#endif
+
+#endif  // ANDROID_BASE_ENDIAN_H
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index cd64262..33d1ab3 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -51,6 +51,12 @@
 #endif
 
 std::string GetExecutablePath();
+std::string GetExecutableDirectory();
+
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string Basename(const std::string& path);
+std::string Dirname(const std::string& path);
 
 }  // namespace base
 }  // namespace android
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 50677a3..e78edbb 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -17,6 +17,37 @@
 #ifndef ANDROID_BASE_LOGGING_H
 #define ANDROID_BASE_LOGGING_H
 
+//
+// Google-style C++ logging.
+//
+
+// This header provides a C++ stream interface to logging.
+//
+// To log:
+//
+//   LOG(INFO) << "Some text; " << some_value;
+//
+// Replace `INFO` with any severity from `enum LogSeverity`.
+//
+// To log the result of a failed function and include the string
+// representation of `errno` at the end:
+//
+//   PLOG(ERROR) << "Write failed";
+//
+// The output will be something like `Write failed: I/O error`.
+// Remember this as 'P' as in perror(3).
+//
+// To output your own types, simply implement operator<< as normal.
+//
+// By default, output goes to logcat on Android and stderr on the host.
+// A process can use `SetLogger` to decide where all logging goes.
+// Implementations are provided for logcat, stderr, and dmesg.
+
+// This header also provides assertions:
+//
+//   CHECK(must_be_true);
+//   CHECK_EQ(a, b) << z_is_interesting_too;
+
 // NOTE: For Windows, you must include logging.h after windows.h to allow the
 // following code to suppress the evil ERROR macro:
 #ifdef _WIN32
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index e275fa2..4de5e57 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -23,6 +23,7 @@
 #error Only bionic supports system properties.
 #endif
 
+#include <chrono>
 #include <limits>
 #include <string>
 
@@ -58,8 +59,18 @@
 // tell you whether or not your call succeeded. A `false` return value definitely means failure.
 bool SetProperty(const std::string& key, const std::string& value);
 
-// Waits for the system property `key` to have the value `expected_value`, .
-void WaitForProperty(const std::string& key, const std::string& expected_value);
+// Waits for the system property `key` to have the value `expected_value`.
+// Times out after `relative_timeout`.
+// Returns true on success, false on timeout.
+bool WaitForProperty(const std::string& key,
+                     const std::string& expected_value,
+                     std::chrono::milliseconds relative_timeout);
+
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Returns true on success, false on timeout.
+bool WaitForPropertyCreation(const std::string& key,
+                             std::chrono::milliseconds relative_timeout);
 
 } // namespace base
 } // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 0f0f638..32c0128 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -21,10 +21,14 @@
 #include <sys/system_properties.h>
 #include <sys/_system_properties.h>
 
+#include <algorithm>
+#include <chrono>
 #include <string>
 
 #include <android-base/parseint.h>
 
+using namespace std::chrono_literals;
+
 namespace android {
 namespace base {
 
@@ -96,28 +100,79 @@
   }
 }
 
-void WaitForProperty(const std::string& key, const std::string& expected_value) {
+// TODO: chrono_utils?
+static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+  auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+  auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
+  ts.tv_sec = s.count();
+  ts.tv_nsec = ns.count();
+}
+
+using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
+
+static void UpdateTimeSpec(timespec& ts,
+                           const AbsTime& timeout) {
+  auto now = std::chrono::steady_clock::now();
+  auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
+  if (remaining_timeout < 0ns) {
+    ts = { 0, 0 };
+  } else {
+    DurationToTimeSpec(ts, remaining_timeout);
+  }
+}
+
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Sets absolute_timeout which represents absolute time for the timeout.
+// Returns nullptr on timeout.
+static const prop_info* WaitForPropertyCreation(const std::string& key,
+                                                const std::chrono::milliseconds& relative_timeout,
+                                                AbsTime& absolute_timeout) {
+  // TODO: boot_clock?
+  auto now = std::chrono::steady_clock::now();
+  absolute_timeout = now + relative_timeout;
+
   // Find the property's prop_info*.
   const prop_info* pi;
   unsigned global_serial = 0;
   while ((pi = __system_property_find(key.c_str())) == nullptr) {
     // The property doesn't even exist yet.
     // Wait for a global change and then look again.
-    global_serial = __system_property_wait_any(global_serial);
+    timespec ts;
+    UpdateTimeSpec(ts, absolute_timeout);
+    if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
   }
+  return pi;
+}
+
+bool WaitForProperty(const std::string& key,
+                     const std::string& expected_value,
+                     std::chrono::milliseconds relative_timeout) {
+  AbsTime absolute_timeout;
+  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+  if (pi == nullptr) return false;
 
   WaitForPropertyData data;
   data.expected_value = &expected_value;
   data.done = false;
   while (true) {
+    timespec ts;
     // Check whether the property has the value we're looking for?
     __system_property_read_callback(pi, WaitForPropertyCallback, &data);
-    if (data.done) return;
+    if (data.done) return true;
 
-    // It didn't so wait for it to change before checking again.
-    __system_property_wait(pi, data.last_read_serial);
+    // It didn't, so wait for the property to change before checking again.
+    UpdateTimeSpec(ts, absolute_timeout);
+    uint32_t unused;
+    if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
   }
 }
 
+bool WaitForPropertyCreation(const std::string& key,
+                             std::chrono::milliseconds relative_timeout) {
+  AbsTime absolute_timeout;
+  return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index d8186be..1bbe482 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -134,8 +134,41 @@
     android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
   });
 
-  android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a");
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
   flag = true;
-  android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b");
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
   thread.join();
 }
+
+TEST(properties, WaitForProperty_timeout) {
+  auto t0 = std::chrono::steady_clock::now();
+  ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
+                                              200ms));
+  auto t1 = std::chrono::steady_clock::now();
+
+  ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+  // Upper bounds on timing are inherently flaky, but let's try...
+  ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+}
+
+TEST(properties, WaitForPropertyCreation) {
+  std::thread thread([&]() {
+    std::this_thread::sleep_for(100ms);
+    android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
+  });
+
+  ASSERT_TRUE(android::base::WaitForPropertyCreation(
+          "debug.libbase.WaitForPropertyCreation_test", 1s));
+  thread.join();
+}
+
+TEST(properties, WaitForPropertyCreation_timeout) {
+  auto t0 = std::chrono::steady_clock::now();
+  ASSERT_FALSE(android::base::WaitForPropertyCreation(
+          "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
+  auto t1 = std::chrono::steady_clock::now();
+
+  ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+  // Upper bounds on timing are inherently flaky, but let's try...
+  ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 6939428..2863a26 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -25,7 +25,8 @@
     fs_mgr_slotselect.cpp \
     fs_mgr_verity.cpp \
     fs_mgr_avb.cpp \
-    fs_mgr_avb_ops.cpp
+    fs_mgr_avb_ops.cpp \
+    fs_mgr_boot_config.cpp
 LOCAL_C_INCLUDES := \
     $(LOCAL_PATH)/include \
     system/vold \
@@ -37,6 +38,9 @@
 ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
 LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
 endif
+ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_CFLAGS += -DALLOW_SKIP_SECURE_CHECK=1
+endif
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1768078..a50817e 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -48,7 +48,6 @@
 
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_avb.h"
-#include "fs_mgr_priv_verity.h"
 
 #define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
 #define KEY_IN_FOOTER  "footer"
@@ -436,16 +435,6 @@
     return ret;
 }
 
-static int device_is_secure() {
-    int ret = -1;
-    char value[PROP_VALUE_MAX];
-    ret = __system_property_get("ro.secure", value);
-    /* If error, we want to fail secure */
-    if (ret < 0)
-        return 1;
-    return strcmp(value, "0") ? 1 : 0;
-}
-
 static int device_is_force_encrypted() {
     int ret = -1;
     char value[PROP_VALUE_MAX];
@@ -661,6 +650,8 @@
     }
 }
 
+// TODO: add ueventd notifiers if they don't exist.
+// This is just doing a wait_for_device for maximum of 1s
 int fs_mgr_test_access(const char *device) {
     int tries = 25;
     while (tries--) {
@@ -672,6 +663,23 @@
     return -1;
 }
 
+bool is_device_secure() {
+    int ret = -1;
+    char value[PROP_VALUE_MAX];
+    ret = __system_property_get("ro.secure", value);
+    if (ret == 0) {
+#ifdef ALLOW_SKIP_SECURE_CHECK
+        // Allow eng builds to skip this check if the property
+        // is not readable (happens during early mount)
+        return false;
+#else
+        // If error and not an 'eng' build, we want to fail secure.
+        return true;
+#endif
+    }
+    return strcmp(value, "0") ? true : false;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
@@ -749,7 +757,7 @@
                 /* Skips mounting the device. */
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
             if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
                 LINFO << "Verity disabled";
@@ -880,6 +888,24 @@
     }
 }
 
+/* wrapper to __mount() and expects a fully prepared fstab_rec,
+ * unlike fs_mgr_do_mount which does more things with avb / verity
+ * etc.
+ */
+int fs_mgr_do_mount_one(struct fstab_rec *rec)
+{
+    if (!rec) {
+        return FS_MGR_DOMNT_FAILED;
+    }
+
+    int ret = __mount(rec->blk_device, rec->mount_point, rec);
+    if (ret) {
+      ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
+    }
+
+    return ret;
+}
+
 /* If tmp_mount_point is non-null, mount the filesystem there.  This is for the
  * tmp mount we do to check the user password
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
@@ -951,7 +977,7 @@
                 /* Skips mounting the device. */
                 continue;
             }
-        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
+        } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
             int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
             if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
                 LINFO << "Verity disabled";
@@ -1001,7 +1027,7 @@
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
  */
-int fs_mgr_do_tmpfs_mount(char *n_name)
+int fs_mgr_do_tmpfs_mount(const char *n_name)
 {
     int ret;
 
@@ -1171,22 +1197,3 @@
 
     return 0;
 }
-
-int fs_mgr_early_setup_verity(struct fstab_rec *fstab_rec)
-{
-    if ((fstab_rec->fs_mgr_flags & MF_VERIFY) && device_is_secure()) {
-        int rc = fs_mgr_setup_verity(fstab_rec, false);
-        if (__android_log_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
-            LINFO << "Verity disabled";
-            return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
-        } else if (rc == FS_MGR_SETUP_VERITY_SUCCESS) {
-            return FS_MGR_EARLY_SETUP_VERITY_SUCCESS;
-        } else {
-            return FS_MGR_EARLY_SETUP_VERITY_FAIL;
-        }
-    } else if (device_is_secure()) {
-        LERROR << "Verity must be enabled for early mounted partitions on secured devices";
-        return FS_MGR_EARLY_SETUP_VERITY_FAIL;
-    }
-    return FS_MGR_EARLY_SETUP_VERITY_NO_VERITY;
-}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 68efb00..2cb7e34 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -441,18 +441,23 @@
 
 static bool init_is_avb_used() {
     // When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
-    // size, digest} in kernel cmdline. They will then be imported by init
-    // process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
+    // size, digest} in kernel cmdline or in device tree. They will then be
+    // imported by init process to system properties: ro.boot.vbmeta.{hash_alg, size, digest}.
+    //
+    // In case of early mount, init properties are not initialized, so we also
+    // ensure we look into kernel command line and device tree if the property is
+    // not found
     //
     // Checks hash_alg as an indicator for whether AVB is used.
     // We don't have to parse and check all of them here. The check will
     // be done in fs_mgr_load_vbmeta_images() and FS_MGR_SETUP_AVB_FAIL will
     // be returned when there is an error.
 
-    std::string hash_alg = android::base::GetProperty("ro.boot.vbmeta.hash_alg", "");
-
-    if (hash_alg == "sha256" || hash_alg == "sha512") {
-        return true;
+    std::string hash_alg;
+    if (fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg) == 0) {
+        if (hash_alg == "sha256" || hash_alg == "sha512") {
+            return true;
+        }
     }
 
     return false;
@@ -482,10 +487,11 @@
     // Sets requested_partitions to nullptr as it's to copy the contents
     // of HASH partitions into fs_mgr_avb_verify_data, which is not required as
     // fs_mgr only deals with HASHTREE partitions.
-    const char* requested_partitions[] = {nullptr};
-    const char* ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "").c_str();
+    const char *requested_partitions[] = {nullptr};
+    std::string ab_suffix;
+    fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
     AvbSlotVerifyResult verify_result =
-        avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix,
+        avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix.c_str(),
                         fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
 
     // Only allow two verify results:
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
new file mode 100644
index 0000000..5b2f218
--- /dev/null
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/properties.h>
+
+#include "fs_mgr_priv.h"
+
+// Tries to get the boot config value in properties, kernel cmdline and
+// device tree (in that order).  returns 'true' if successfully found, 'false'
+// otherwise
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+    FS_MGR_CHECK(out_val != nullptr);
+
+    // first check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        return true;
+    }
+
+    // fallback to kernel cmdline, properties may not be ready yet
+    std::string cmdline;
+    std::string cmdline_key("androidboot." + key);
+    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+            std::vector<std::string> pieces = android::base::Split(entry, "=");
+            if (pieces.size() == 2) {
+                if (pieces[0] == cmdline_key) {
+                    *out_val = pieces[1];
+                    return true;
+                }
+            }
+        }
+    }
+
+    // lastly, check the device tree
+    if (is_dt_compatible()) {
+        std::string file_name = kAndroidDtDir + "/" + key;
+        // DT entries terminate with '\0' but so do the properties
+        if (android::base::ReadFileToString(file_name, out_val)) {
+            return true;
+        }
+
+        LERROR << "Error finding '" << key << "' in device tree";
+    }
+
+    return false;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 48ddf29..5b0243d 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -22,6 +23,10 @@
 #include <sys/mount.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
 #include "fs_mgr_priv.h"
 
 struct fs_mgr_flag_values {
@@ -130,6 +135,24 @@
     return size;
 }
 
+/* fills 'dt_value' with the underlying device tree value string without
+ * the trailing '\0'. Returns true if 'dt_value' has a valid string, 'false'
+ * otherwise.
+ */
+static bool read_dt_file(const std::string& file_name, std::string* dt_value)
+{
+    if (android::base::ReadFileToString(file_name, dt_value)) {
+        if (!dt_value->empty()) {
+            // trim the trailing '\0' out, otherwise the comparison
+            // will produce false-negatives.
+            dt_value->resize(dt_value->size() - 1);
+            return true;
+        }
+    }
+
+    return false;
+}
+
 static int parse_flags(char *flags, struct flag_list *fl,
                        struct fs_mgr_flag_values *flag_vals,
                        char *fs_options, int fs_options_len)
@@ -290,6 +313,97 @@
     return f;
 }
 
+static bool is_dt_fstab_compatible() {
+    std::string dt_value;
+    std::string file_name = kAndroidDtDir + "/fstab/compatible";
+    if (read_dt_file(file_name, &dt_value)) {
+        if (dt_value == "android,fstab") {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static std::string read_fstab_from_dt() {
+    std::string fstab;
+    if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
+        return fstab;
+    }
+
+    std::string fstabdir_name = kAndroidDtDir + "/fstab";
+    std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
+    if (!fstabdir) return fstab;
+
+    dirent* dp;
+    while ((dp = readdir(fstabdir.get())) != NULL) {
+        // skip over name and compatible
+        if (dp->d_type != DT_DIR) {
+            continue;
+        }
+
+        // skip if its not 'vendor', 'odm' or 'system'
+        if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
+            strcmp(dp->d_name, "vendor")) {
+            continue;
+        }
+
+        // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
+        std::vector<std::string> fstab_entry;
+        std::string file_name;
+        std::string value;
+        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;
+        }
+        fstab_entry.push_back(value);
+        fstab_entry.push_back(android::base::StringPrintf("/%s", dp->d_name));
+
+        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;
+        }
+        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;
+        }
+        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;
+        }
+        fstab_entry.push_back(value);
+
+        fstab += android::base::Join(fstab_entry, " ");
+        fstab += '\n';
+    }
+
+    return fstab;
+}
+
+bool is_dt_compatible() {
+    std::string file_name = kAndroidDtDir + "/compatible";
+    std::string dt_value;
+    if (read_dt_file(file_name, &dt_value)) {
+        if (dt_value == "android,firmware") {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file)
 {
     int cnt, entries;
@@ -444,6 +558,84 @@
     return fstab;
 }
 
+/* Returns fstab entries parsed from the device tree if they
+ * exist
+ */
+struct fstab *fs_mgr_read_fstab_dt()
+{
+    std::string fstab_buf = read_fstab_from_dt();
+    if (fstab_buf.empty()) {
+        return NULL;
+    }
+
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+        fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
+                 fstab_buf.length(), "r"), fclose);
+    if (!fstab_file) {
+        return NULL;
+    }
+
+    struct fstab *fstab = fs_mgr_read_fstab_file(fstab_file.get());
+    if (!fstab) {
+        LERROR << "failed to load fstab from kernel:" << std::endl << fstab_buf;
+    }
+
+    return fstab;
+}
+
+/* combines fstab entries passed in from device tree with
+ * the ones found in /fstab.<hardware>
+ */
+struct fstab *fs_mgr_read_fstab_default()
+{
+    struct fstab *fstab = fs_mgr_read_fstab_dt();
+    std::string hw;
+    if (!fs_mgr_get_boot_config("hardware", &hw)) {
+        // if we fail to find this, return whatever was found in device tree
+        LWARNING << "failed to find device hardware name";
+        return fstab;
+    }
+
+    std::string default_fstab = FSTAB_PREFIX + hw;
+    struct fstab *f = fs_mgr_read_fstab(default_fstab.c_str());
+    if (!f) {
+        // return what we have
+        LWARNING << "failed to read fstab entries from '" << default_fstab << "'";
+        return fstab;
+    }
+
+    // return the fstab read from file if device tree doesn't
+    // have one, other wise merge the two
+    if (!fstab) {
+        fstab = f;
+    } else {
+        int total_entries = fstab->num_entries + f->num_entries;
+        fstab->recs = static_cast<struct fstab_rec *>(realloc(
+                        fstab->recs, total_entries * (sizeof(struct fstab_rec))));
+        if (!fstab->recs) {
+            LERROR << "failed to allocate fstab recs";
+            fstab->num_entries = 0;
+            fs_mgr_free_fstab(fstab);
+            return NULL;
+        }
+
+        for (int i = fstab->num_entries, j = 0; i < total_entries; i++, j++) {
+            // copy everything and *not* strdup
+            fstab->recs[i] = f->recs[j];
+        }
+
+        // free up fstab entries read from file, but don't cleanup
+        // the strings within f->recs[X] to make sure they are accessible
+        // through fstab->recs[X].
+        free(f->fstab_filename);
+        free(f);
+
+        fstab->num_entries = total_entries;
+    }
+
+    return fstab;
+}
+
 void fs_mgr_free_fstab(struct fstab *fstab)
 {
     int i;
@@ -557,6 +749,11 @@
     return fstab->fs_mgr_flags & MF_VERIFY;
 }
 
+int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_VERIFYATBOOT;
+}
+
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
 {
     return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 79c27c4..4e2ac8b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -19,6 +19,7 @@
 
 #include <android-base/logging.h>
 #include <fs_mgr.h>
+#include "fs_mgr_priv_boot_config.h"
 
 /* The CHECK() in logging.h will use program invocation name as the tag.
  * Thus, the log will have prefix "init: " when libfs_mgr is statically
@@ -40,6 +41,8 @@
 #define PWARNING PLOG(WARNING) << FS_MGR_TAG
 #define PERROR   PLOG(ERROR) << FS_MGR_TAG
 
+const std::string FSTAB_PREFIX("/fstab.");
+
 __BEGIN_DECLS
 
 #define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
@@ -114,6 +117,8 @@
 int fs_mgr_set_blk_ro(const char *blockdev);
 int fs_mgr_test_access(const char *device);
 int fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool is_dt_compatible();
+bool is_device_secure();
 
 __END_DECLS
 
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_boot_config.h
similarity index 63%
rename from fs_mgr/fs_mgr_priv_verity.h
rename to fs_mgr/fs_mgr_priv_boot_config.h
index 1a6d215..8773d33 100644
--- a/fs_mgr/fs_mgr_priv_verity.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
+#ifndef __CORE_FS_MGR_PRIV_BOOTCONFIG_H
+#define __CORE_FS_MGR_PRIV_BOOTCONFIG_H
+
 #include <sys/cdefs.h>
+#include <string>
 
-#define FS_MGR_SETUP_VERITY_DISABLED (-2)
-#define FS_MGR_SETUP_VERITY_FAIL (-1)
-#define FS_MGR_SETUP_VERITY_SUCCESS 0
+const std::string kAndroidDtDir("/proc/device-tree/firmware/android");
 
-__BEGIN_DECLS
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
 
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool verify_dev);
-
-__END_DECLS
+#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index b30417f..f3bba7b 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -14,118 +14,31 @@
  * limitations under the License.
  */
 
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <ctype.h>
-#include <errno.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <cutils/properties.h>
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 
-// finds slot_suffix in androidboot.slot_suffix kernel command line argument
-// or in the device tree node at /firmware/android/slot_suffix property
-static int get_active_slot_suffix_from_kernel(char *out_suffix,
-                                              size_t suffix_len)
-{
-    std::string cmdline;
-    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-            std::vector<std::string> pieces = android::base::Split(entry, "=");
-            if (pieces.size() == 2) {
-                if (pieces[0] == "androidboot.slot_suffix") {
-                    strncpy(out_suffix, pieces[1].c_str(), suffix_len);
-                    return 0;
-                }
-            }
-        }
-    }
-
-    // if we can't find slot_suffix in cmdline, check the DT
-    static constexpr char android_dt_dir[] = "/proc/device-tree/firmware/android";
-    std::string file_name = android::base::StringPrintf("%s/compatible", android_dt_dir);
-    std::string dt_value;
-    if (android::base::ReadFileToString(file_name, &dt_value)) {
-        if (!dt_value.compare("android,firmware")) {
-            LERROR << "Error finding compatible android DT node";
-            return -1;
-        }
-
-        file_name = android::base::StringPrintf("%s/%s", android_dt_dir, "slot_suffix");
-        if (!android::base::ReadFileToString(file_name, &dt_value)) {
-            LERROR << "Error finding slot_suffix in device tree";
-            return -1;
-        }
-
-        // DT entries have a terminating '\0', so 'suffix_len' is safe.
-        strncpy(out_suffix, dt_value.c_str(), suffix_len);
-        return 0;
-    }
-
-    // slot_suffix missing in kernel cmdline or device tree
-    return -1;
-}
-
-// Gets slot_suffix from either the kernel cmdline / device tree.  Sets
-// |out_suffix| on success and returns 0. Returns -1 if slot_suffix could not
-// be determined.
-static int get_active_slot_suffix(char *out_suffix, size_t suffix_len)
-{
-    char propbuf[PROPERTY_VALUE_MAX];
-
-    // Get the suffix from the kernel commandline (note that we don't
-    // allow the empty suffix). On bootloaders natively supporting A/B
-    // we'll hit this path every time so don't bother logging it.
-    property_get("ro.boot.slot_suffix", propbuf, "");
-    if (propbuf[0] != '\0') {
-        strncpy(out_suffix, propbuf, suffix_len);
-        return 0;
-    }
-
-    // if the property is not set, we are probably being invoked early during
-    // boot.  Try to find the slotsuffix ourselves in the kernel command line
-    // or the device tree
-    if (get_active_slot_suffix_from_kernel(out_suffix, suffix_len) == 0) {
-        LINFO << "Using slot suffix '" << out_suffix << "' from kernel";
-        return 0;
-    }
-
-    LERROR << "Error determining slot_suffix";
-
-    return -1;
-}
-
 // Updates |fstab| for slot_suffix. Returns 0 on success, -1 on error.
 int fs_mgr_update_for_slotselect(struct fstab *fstab)
 {
     int n;
-    char suffix[PROPERTY_VALUE_MAX];
     int got_suffix = 0;
+    std::string suffix;
 
     for (n = 0; n < fstab->num_entries; n++) {
         if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
             char *tmp;
 
             if (!got_suffix) {
-                memset(suffix, '\0', sizeof(suffix));
-                if (get_active_slot_suffix(suffix, sizeof(suffix) - 1) != 0) {
+                if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
                   return -1;
                 }
                 got_suffix = 1;
             }
 
             if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
-                         suffix) > 0) {
+                         suffix.c_str()) > 0) {
                 free(fstab->recs[n].blk_device);
                 fstab->recs[n].blk_device = tmp;
             } else {
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 1ec4540..e8ed6a2 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -45,9 +45,6 @@
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_dm_ioctl.h"
-#include "fs_mgr_priv_verity.h"
-
-#define FSTAB_PREFIX "/fstab."
 
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
@@ -658,7 +655,6 @@
 
 static int load_verity_state(struct fstab_rec *fstab, int *mode)
 {
-    char propbuf[PROPERTY_VALUE_MAX];
     int match = 0;
     off64_t offset = 0;
 
@@ -666,10 +662,9 @@
     *mode = VERITY_MODE_EIO;
 
     /* use the kernel parameter if set */
-    property_get("ro.boot.veritymode", propbuf, "");
-
-    if (*propbuf != '\0') {
-        if (!strcmp(propbuf, "enforcing")) {
+    std::string veritymode;
+    if (fs_mgr_get_boot_config("veritymode", &veritymode)) {
+        if (veritymode.compare("enforcing")) {
             *mode = VERITY_MODE_DEFAULT;
         }
         return 0;
@@ -697,8 +692,6 @@
 
 int fs_mgr_load_verity_state(int *mode)
 {
-    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
-    char propbuf[PROPERTY_VALUE_MAX];
     int rc = -1;
     int i;
     int current;
@@ -708,13 +701,9 @@
      * logging mode, in which case return that */
     *mode = VERITY_MODE_DEFAULT;
 
-    property_get("ro.hardware", propbuf, "");
-    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
-
-    fstab = fs_mgr_read_fstab(fstab_filename);
-
+    fstab = fs_mgr_read_fstab_default();
     if (!fstab) {
-        LERROR << "Failed to read " << fstab_filename;
+        LERROR << "Failed to read default fstab";
         goto out;
     }
 
@@ -748,7 +737,6 @@
 {
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     bool system_root = false;
-    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
     std::string mount_point;
     char propbuf[PROPERTY_VALUE_MAX];
     const char *status;
@@ -768,22 +756,16 @@
     }
 
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
-
     if (fd == -1) {
         PERROR << "Error opening device mapper";
         goto out;
     }
 
-    property_get("ro.hardware", propbuf, "");
-    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
-
     property_get("ro.build.system_root_image", propbuf, "");
     system_root = !strcmp(propbuf, "true");
-
-    fstab = fs_mgr_read_fstab(fstab_filename);
-
+    fstab = fs_mgr_read_fstab_default();
     if (!fstab) {
-        LERROR << "Failed to read " << fstab_filename;
+        LERROR << "Failed to read default fstab";
         goto out;
     }
 
@@ -859,7 +841,10 @@
     *table = strdup(result.c_str());
 }
 
-int fs_mgr_setup_verity(struct fstab_rec *fstab, bool verify_dev)
+// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
+// mount. The 'wait_for_verity_dev' parameter makes this function wait for the
+// verity device to get created before return
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev)
 {
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
@@ -873,6 +858,13 @@
     const std::string mount_point(basename(fstab->mount_point));
     bool verified_at_boot = false;
 
+    // This is a public API and so deserves its own check to see if verity
+    // setup is needed at all.
+    if (!is_device_secure()) {
+        LINFO << "Verity setup skipped for " << mount_point;
+        return FS_MGR_SETUP_VERITY_SUCCESS;
+    }
+
     if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
             FEC_DEFAULT_ROOTS) < 0) {
         PERROR << "Failed to open '" << fstab->blk_device << "'";
@@ -1026,7 +1018,7 @@
     }
 
     // make sure we've set everything up properly
-    if (verify_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+    if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
         goto out;
     }
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index a9deed9..a5e5beb 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -85,6 +85,8 @@
 typedef void (*fs_mgr_verity_state_callback)(struct fstab_rec *fstab,
         const char *mount_point, int mode, int status);
 
+struct fstab *fs_mgr_read_fstab_default();
+struct fstab *fs_mgr_read_fstab_dt();
 struct fstab *fs_mgr_read_fstab_file(FILE *fstab_file);
 struct fstab *fs_mgr_read_fstab(const char *fstab_path);
 void fs_mgr_free_fstab(struct fstab *fstab);
@@ -103,7 +105,8 @@
 
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
-int fs_mgr_do_tmpfs_mount(char *n_name);
+int fs_mgr_do_mount_one(struct fstab_rec *rec);
+int fs_mgr_do_tmpfs_mount(const char *n_name);
 int fs_mgr_unmount_all(struct fstab *fstab);
 int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc,
                           char *real_blk_device, int size);
@@ -116,6 +119,7 @@
 int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab);
 int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
 int fs_mgr_is_verified(const struct fstab_rec *fstab);
+int fs_mgr_is_verifyatboot(const struct fstab_rec *fstab);
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
 int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
 const char* fs_mgr_get_file_encryption_mode(const struct fstab_rec *fstab);
@@ -131,10 +135,10 @@
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
-#define FS_MGR_EARLY_SETUP_VERITY_NO_VERITY -2
-#define FS_MGR_EARLY_SETUP_VERITY_FAIL -1
-#define FS_MGR_EARLY_SETUP_VERITY_SUCCESS 0
-int fs_mgr_early_setup_verity(struct fstab_rec *fstab);
+#define FS_MGR_SETUP_VERITY_DISABLED (-2)
+#define FS_MGR_SETUP_VERITY_FAIL (-1)
+#define FS_MGR_SETUP_VERITY_SUCCESS 0
+int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
 
 #ifdef __cplusplus
 }
diff --git a/init/Android.mk b/init/Android.mk
index a10a714..18cbedc 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -123,6 +123,7 @@
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
     init_parser_test.cpp \
+    property_service_test.cpp \
     util_test.cpp \
 
 LOCAL_SHARED_LIBRARIES += \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 2388edc..6dd1cbb 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -476,15 +476,18 @@
 static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
     Parser& parser = Parser::GetInstance();
     if (end_index <= start_index) {
-        // Use the default set if no path is given
-        static const std::vector<std::string> init_directories = {
-            "/system/etc/init",
-            "/vendor/etc/init",
-            "/odm/etc/init"
-        };
-
-        for (const auto& dir : init_directories) {
-            parser.ParseConfig(dir);
+        // Fallbacks for partitions on which early mount isn't enabled.
+        if (!parser.is_system_etc_init_loaded()) {
+            parser.ParseConfig("/system/etc/init");
+            parser.set_is_system_etc_init_loaded(true);
+        }
+        if (!parser.is_vendor_etc_init_loaded()) {
+            parser.ParseConfig("/vendor/etc/init");
+            parser.set_is_vendor_etc_init_loaded(true);
+        }
+        if (!parser.is_odm_etc_init_loaded()) {
+            parser.ParseConfig("/odm/etc/init");
+            parser.set_is_odm_etc_init_loaded(true);
         }
     } else {
         for (size_t i = start_index; i < end_index; ++i) {
diff --git a/init/devices.cpp b/init/devices.cpp
index b3b808b..bd11f5f 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -251,7 +251,10 @@
      * some device nodes, so the uid has to be set with chown() and is still
      * racy. Fixing the gid race at least fixed the issue with system_server
      * opening dynamic input devices under the AID_INPUT gid. */
-    setegid(gid);
+    if (setegid(gid)) {
+        PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
+        goto out;
+    }
     /* If the node already exists update its SELinux label to handle cases when
      * it was created with the wrong context during coldboot procedure. */
     if (mknod(path, mode, dev) && (errno == EEXIST) && secontext) {
@@ -273,7 +276,9 @@
 
 out:
     chown(path, uid, -1);
-    setegid(AID_ROOT);
+    if (setegid(AID_ROOT)) {
+        PLOG(FATAL) << "setegid(AID_ROOT) failed";
+    }
 
     if (secontext) {
         freecon(secontext);
@@ -999,15 +1004,20 @@
 }
 
 void device_init(const char* path, coldboot_callback fn) {
-    sehandle = selinux_android_file_context_handle();
-    selinux_status_open(true);
-
-    /* is 256K enough? udev uses 16MB! */
-    device_fd.reset(uevent_open_socket(256*1024, true));
-    if (device_fd == -1) {
-        return;
+    if (!sehandle) {
+        sehandle = selinux_android_file_context_handle();
     }
-    fcntl(device_fd, F_SETFL, O_NONBLOCK);
+    // open uevent socket and selinux status only if it hasn't been
+    // done before
+    if (device_fd == -1) {
+        /* is 256K enough? udev uses 16MB! */
+        device_fd.reset(uevent_open_socket(256 * 1024, true));
+        if (device_fd == -1) {
+            return;
+        }
+        fcntl(device_fd, F_SETFL, O_NONBLOCK);
+        selinux_status_open(true);
+    }
 
     if (access(COLDBOOT_DONE, F_OK) == 0) {
         LOG(VERBOSE) << "Skipping coldboot, already done!";
@@ -1040,6 +1050,7 @@
 void device_close() {
     destroy_platform_devices();
     device_fd.reset();
+    selinux_status_close();
 }
 
 int get_device_fd() {
diff --git a/init/init.cpp b/init/init.cpp
index 7f7eb2f..bddf005 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -52,6 +52,8 @@
 
 #include <fstream>
 #include <memory>
+#include <set>
+#include <vector>
 
 #include "action.h"
 #include "bootchart.h"
@@ -502,26 +504,30 @@
     std::string dt_value;
     std::string file_name = StringPrintf("%s/compatible", android_dt_dir);
 
-    android::base::ReadFileToString(file_name, &dt_value);
-    if (!dt_value.compare("android,firmware")) {
-        LOG(ERROR) << "firmware/android is not compatible with 'android,firmware'";
-        return false;
+    if (android::base::ReadFileToString(file_name, &dt_value)) {
+        // trim the trailing '\0' out, otherwise the comparison
+        // will produce false-negatives.
+        dt_value.resize(dt_value.size() - 1);
+        if (dt_value == "android,firmware") {
+            return true;
+        }
     }
 
-    return true;
+    return false;
 }
 
 static bool is_dt_fstab_compatible() {
     std::string dt_value;
     std::string file_name = StringPrintf("%s/%s/compatible", android_dt_dir, "fstab");
 
-    android::base::ReadFileToString(file_name, &dt_value);
-    if (!dt_value.compare("android,fstab")) {
-        LOG(ERROR) << "firmware/android/fstab is not compatible with 'android,fstab'";
-        return false;
+    if (android::base::ReadFileToString(file_name, &dt_value)) {
+        dt_value.resize(dt_value.size() - 1);
+        if (dt_value == "android,fstab") {
+            return true;
+        }
     }
 
-    return true;
+    return false;
 }
 
 static void process_kernel_dt() {
@@ -664,120 +670,47 @@
     }
 }
 
-static std::string import_dt_fstab() {
-    std::string fstab;
-    if (!is_dt_compatible() || !is_dt_fstab_compatible()) {
-        return fstab;
+static bool early_mount_one(struct fstab_rec* rec) {
+    if (rec && fs_mgr_is_verified(rec)) {
+        // setup verity and create the dm-XX block device
+        // needed to mount this partition
+        int ret = fs_mgr_setup_verity(rec, false);
+        if (ret == FS_MGR_SETUP_VERITY_FAIL) {
+            PLOG(ERROR) << "early_mount: Failed to setup verity for '" << rec->mount_point << "'";
+            return false;
+        }
+
+        // The exact block device name is added as a mount source by
+        // fs_mgr_setup_verity() in ->blk_device as "/dev/block/dm-XX"
+        // We create that device by running coldboot on /sys/block/dm-XX
+        std::string dm_device(basename(rec->blk_device));
+        std::string syspath = StringPrintf("/sys/block/%s", dm_device.c_str());
+        device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+            if (uevent->device_name && !strcmp(dm_device.c_str(), uevent->device_name)) {
+                LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
+                return COLDBOOT_STOP;
+            }
+            return COLDBOOT_CONTINUE;
+        });
     }
 
-    std::string fstabdir_name = StringPrintf("%s/fstab", android_dt_dir);
-    std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
-    if (!fstabdir) return fstab;
-
-    dirent* dp;
-    while ((dp = readdir(fstabdir.get())) != NULL) {
-        // skip over name and compatible
-        if (dp->d_type != DT_DIR) {
-            continue;
-        }
-
-        // skip if its not 'vendor', 'odm' or 'system'
-        if (strcmp(dp->d_name, "odm") && strcmp(dp->d_name, "system") &&
-            strcmp(dp->d_name, "vendor")) {
-            continue;
-        }
-
-        // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\n
-        std::vector<std::string> fstab_entry;
-        std::string file_name;
-        std::string value;
-        file_name = StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
-            LOG(ERROR) << "dt_fstab: Failed to find device for partition " << dp->d_name;
-            fstab.clear();
-            break;
-        }
-        // trim the terminating '\0' out
-        value.resize(value.size() - 1);
-        fstab_entry.push_back(value);
-        fstab_entry.push_back(StringPrintf("/%s", dp->d_name));
-
-        file_name = StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
-            LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
-        }
-        value.resize(value.size() - 1);
-        fstab_entry.push_back(value);
-
-        file_name = StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
-            LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
-        }
-        value.resize(value.size() - 1);
-        fstab_entry.push_back(value);
-
-        file_name = StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
-            LOG(ERROR) << "dt_fstab: Failed to find type for partition " << dp->d_name;
-            fstab.clear();
-            break;
-        }
-        value.resize(value.size() - 1);
-        fstab_entry.push_back(value);
-
-        fstab += android::base::Join(fstab_entry, " ");
-        fstab += '\n';
+    if (rec && fs_mgr_do_mount_one(rec)) {
+        PLOG(ERROR) << "early_mount: Failed to mount '" << rec->mount_point << "'";
+        return false;
     }
 
-    return fstab;
+    return true;
 }
 
-/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
-static bool early_mount() {
-    std::string fstab = import_dt_fstab();
-    if (fstab.empty()) {
-        LOG(INFO) << "Early mount skipped (missing fstab in device tree)";
-        return true;
+// Creates devices with uevent->partition_name matching one in the in/out
+// partition_names. Note that the partition_names MUST have A/B suffix
+// when A/B is used. Found partitions will then be removed from the
+// partition_names for caller to check which devices are NOT created.
+static void early_device_init(std::set<std::string>* partition_names) {
+    if (partition_names->empty()) {
+        return;
     }
-
-    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
-        fmemopen(static_cast<void*>(const_cast<char*>(fstab.c_str())), fstab.length(), "r"), fclose);
-    if (!fstab_file) {
-        PLOG(ERROR) << "Early mount failed to open fstab file in memory";
-        return false;
-    }
-
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> tab(
-        fs_mgr_read_fstab_file(fstab_file.get()), fs_mgr_free_fstab);
-    if (!tab) {
-        LOG(ERROR) << "Early mount fsmgr failed to load fstab from kernel:" << std::endl << fstab;
-        return false;
-    }
-
-    // find out fstab records for odm, system and vendor
-    fstab_rec* odm_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/odm");
-    fstab_rec* system_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/system");
-    fstab_rec* vendor_rec = fs_mgr_get_entry_for_mount_point(tab.get(), "/vendor");
-    if (!odm_rec && !system_rec && !vendor_rec) {
-        // nothing to early mount
-        return true;
-    }
-
-    // assume A/B device if we find 'slotselect' in any fstab entry
-    bool is_ab = ((odm_rec && fs_mgr_is_slotselect(odm_rec)) ||
-                  (system_rec && fs_mgr_is_slotselect(system_rec)) ||
-                  (vendor_rec && fs_mgr_is_slotselect(vendor_rec)));
-    bool found_odm = !odm_rec;
-    bool found_system = !system_rec;
-    bool found_vendor = !vendor_rec;
-    int count_odm = 0, count_vendor = 0, count_system = 0;
-
-    // create the devices we need..
-    device_init(nullptr, [&](uevent* uevent) -> coldboot_action_t {
+    device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
         if (!strncmp(uevent->subsystem, "firmware", 8)) {
             return COLDBOOT_CONTINUE;
         }
@@ -792,82 +725,132 @@
             return COLDBOOT_CONTINUE;
         }
 
-        coldboot_action_t ret;
-        bool create_this_node = false;
         if (uevent->partition_name) {
-            // prefix match partition names so we create device nodes for
-            // A/B-ed partitions
-            if (!found_odm && !strncmp(uevent->partition_name, "odm", 3)) {
-                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
-
-                // wait twice for A/B-ed partitions
-                count_odm++;
-                if (!is_ab) {
-                    found_odm = true;
-                } else if (count_odm == 2) {
-                    found_odm = true;
+            // match partition names to create device nodes for partitions
+            // both partition_names and uevent->partition_name have A/B suffix when A/B is used
+            auto iter = partition_names->find(uevent->partition_name);
+            if (iter != partition_names->end()) {
+                LOG(VERBOSE) << "early_mount: found partition: " << *iter;
+                partition_names->erase(iter);
+                if (partition_names->empty()) {
+                    return COLDBOOT_STOP;  // found all partitions, stop coldboot
+                } else {
+                    return COLDBOOT_CREATE;  // create this device and continue to find others
                 }
-
-                create_this_node = true;
-            } else if (!found_system && !strncmp(uevent->partition_name, "system", 6)) {
-                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
-
-                count_system++;
-                if (!is_ab) {
-                    found_system = true;
-                } else if (count_system == 2) {
-                    found_system = true;
-                }
-
-                create_this_node = true;
-            } else if (!found_vendor && !strncmp(uevent->partition_name, "vendor", 6)) {
-                LOG(VERBOSE) << "early_mount: found (" << uevent->partition_name << ") partition";
-                count_vendor++;
-                if (!is_ab) {
-                    found_vendor = true;
-                } else if (count_vendor == 2) {
-                    found_vendor = true;
-                }
-
-                create_this_node = true;
             }
         }
-
-        // if we found all other partitions already, create this
-        // node and stop coldboot. If this is a prefix matched
-        // partition, create device node and continue. For everything
-        // else skip the device node
-        if (found_odm && found_system && found_vendor) {
-            ret = COLDBOOT_STOP;
-        } else if (create_this_node) {
-            ret = COLDBOOT_CREATE;
-        } else {
-            ret = COLDBOOT_CONTINUE;
-        }
-
-        return ret;
+        // Not found a partition or find an unneeded partition, continue to find others
+        return COLDBOOT_CONTINUE;
     });
+}
 
-    // TODO: add support to mount partitions w/ verity
+static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
+                                 std::set<std::string>* out_partitions, bool* out_need_verity) {
+    std::string meta_partition;
+    out_partitions->clear();
+    *out_need_verity = false;
 
-    int ret = 0;
-    if (odm_rec &&
-        (ret = fs_mgr_do_mount(tab.get(), odm_rec->mount_point, odm_rec->blk_device, NULL))) {
-        PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting odm";
-        return false;
+    for (auto fstab_rec : early_fstab_recs) {
+        // don't allow verifyatboot for early mounted partitions
+        if (fs_mgr_is_verifyatboot(fstab_rec)) {
+            LOG(ERROR) << "early_mount: partitions can't be verified at boot";
+            return false;
+        }
+        // check for verified partitions
+        if (fs_mgr_is_verified(fstab_rec)) {
+            *out_need_verity = true;
+        }
+        // check if verity metadata is on a separate partition and get partition
+        // name from the end of the ->verity_loc path. verity state is not partition
+        // specific, so there must be only 1 additional partition that carries
+        // verity state.
+        if (fstab_rec->verity_loc) {
+            if (!meta_partition.empty()) {
+                LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
+                           << ", " << basename(fstab_rec->verity_loc);
+                return false;
+            } else {
+                meta_partition = basename(fstab_rec->verity_loc);
+            }
+        }
     }
 
-    if (vendor_rec &&
-        (ret = fs_mgr_do_mount(tab.get(), vendor_rec->mount_point, vendor_rec->blk_device, NULL))) {
-        PLOG(ERROR) << "early_mount: fs_mgr_do_mount returned error for mounting vendor";
-        return false;
+    // includes those early mount partitions and meta_partition (if any)
+    // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
+    for (auto fstab_rec : early_fstab_recs) {
+        out_partitions->emplace(basename(fstab_rec->blk_device));
     }
 
-    device_close();
+    if (!meta_partition.empty()) {
+        out_partitions->emplace(std::move(meta_partition));
+    }
 
     return true;
 }
 
+/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
+static bool early_mount() {
+    // first check if device tree fstab entries are compatible
+    if (!is_dt_fstab_compatible()) {
+        LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
+        return true;
+    }
+
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
+        fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
+    if (!tab) {
+        LOG(ERROR) << "Early mount failed to read fstab from device tree";
+        return false;
+    }
+
+    // find out fstab records for odm, system and vendor
+    std::vector<fstab_rec*> early_fstab_recs;
+    for (auto mount_point : {"/odm", "/system", "/vendor"}) {
+        fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
+        if (fstab_rec != nullptr) {
+            early_fstab_recs.push_back(fstab_rec);
+        }
+    }
+
+    // nothing to early mount
+    if (early_fstab_recs.empty()) return true;
+
+    bool need_verity;
+    std::set<std::string> partition_names;
+    // partition_names MUST have A/B suffix when A/B is used
+    if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity)) {
+        return false;
+    }
+
+    bool success = false;
+    // create the devices we need..
+    early_device_init(&partition_names);
+
+    // early_device_init will remove found partitions from partition_names
+    // So if the partition_names is not empty here, means some partitions
+    // are not found
+    if (!partition_names.empty()) {
+        LOG(ERROR) << "early_mount: partition(s) not found: "
+                   << android::base::Join(partition_names, ", ");
+        goto done;
+    }
+
+    if (need_verity) {
+        // create /dev/device mapper
+        device_init("/sys/devices/virtual/misc/device-mapper",
+                    [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
+    }
+
+    for (auto fstab_rec : early_fstab_recs) {
+        if (!early_mount_one(fstab_rec)) goto done;
+    }
+    success = true;
+
+done:
+    device_close();
+    return success;
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -1011,8 +994,16 @@
     std::string bootscript = property_get("ro.boot.init_rc");
     if (bootscript.empty()) {
         parser.ParseConfig("/init.rc");
+        parser.set_is_system_etc_init_loaded(
+                parser.ParseConfig("/system/etc/init"));
+        parser.set_is_vendor_etc_init_loaded(
+                parser.ParseConfig("/vendor/etc/init"));
+        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
     } else {
         parser.ParseConfig(bootscript);
+        parser.set_is_system_etc_init_loaded(true);
+        parser.set_is_vendor_etc_init_loaded(true);
+        parser.set_is_odm_etc_init_loaded(true);
     }
 
     ActionManager& am = ActionManager::GetInstance();
diff --git a/init/init_parser.h b/init/init_parser.h
index 5ed30ad..f66ba52 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -41,6 +41,18 @@
     bool ParseConfig(const std::string& path);
     void AddSectionParser(const std::string& name,
                           std::unique_ptr<SectionParser> parser);
+    void set_is_system_etc_init_loaded(bool loaded) {
+        is_system_etc_init_loaded_ = loaded;
+    }
+    void set_is_vendor_etc_init_loaded(bool loaded) {
+        is_vendor_etc_init_loaded_ = loaded;
+    }
+    void set_is_odm_etc_init_loaded(bool loaded) {
+        is_odm_etc_init_loaded_ = loaded;
+    }
+    bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
+    bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
+    bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
 
 private:
     Parser();
@@ -50,6 +62,9 @@
     bool ParseConfigDir(const std::string& path);
 
     std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+    bool is_system_etc_init_loaded_ = false;
+    bool is_vendor_etc_init_loaded_ = false;
+    bool is_odm_etc_init_loaded_ = false;
 };
 
 #endif
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ce197ee..decd644 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -49,6 +50,7 @@
 
 #include <fs_mgr.h>
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include "bootimg.h"
 
@@ -57,6 +59,8 @@
 #include "util.h"
 #include "log.h"
 
+using android::base::StringPrintf;
+
 #define PERSISTENT_PROPERTY_DIR  "/data/property"
 #define FSTAB_PREFIX "/fstab."
 #define RECOVERY_MOUNT_POINT "/recovery"
@@ -245,6 +249,13 @@
       return true;
     }
 
+    // http://b/35166374: don't allow init to make arbitrarily large allocations.
+    if (len > 0xffff) {
+      LOG(ERROR) << "sys_prop: RecvString asked to read huge string: " << len;
+      errno = ENOMEM;
+      return false;
+    }
+
     std::vector<char> chars(len);
     if (!RecvChars(&chars[0], len, timeout_ms)) {
       return false;
@@ -386,12 +397,11 @@
         return;
     }
 
-    /* Check socket options here */
     struct ucred cr;
     socklen_t cr_size = sizeof(cr);
     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
         close(s);
-        PLOG(ERROR) << "Unable to receive socket options";
+        PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
         return;
     }
 
@@ -399,14 +409,13 @@
     uint32_t timeout_ms = kDefaultSocketTimeout;
 
     uint32_t cmd = 0;
-
     if (!socket.RecvUint32(&cmd, &timeout_ms)) {
         PLOG(ERROR) << "sys_prop: error while reading command from the socket";
         socket.SendUint32(PROP_ERROR_READ_CMD);
         return;
     }
 
-    switch(cmd) {
+    switch (cmd) {
     case PROP_MSG_SETPROP: {
         char prop_name[PROP_NAME_MAX];
         char prop_value[PROP_VALUE_MAX];
@@ -437,7 +446,9 @@
         handle_property_set(socket, name, value, false);
         break;
       }
+
     default:
+        LOG(ERROR) << "sys_prop: invalid command " << cmd;
         socket.SendUint32(PROP_ERROR_INVALID_CMD);
         break;
     }
@@ -598,6 +609,8 @@
     load_override_properties();
     /* Read persistent properties after all default values have been loaded. */
     load_persistent_properties();
+    uint64_t start_ns = boot_clock::now().time_since_epoch().count();
+    property_set("ro.boottime.persistent_properties", StringPrintf("%" PRIu64, start_ns).c_str());
 }
 
 void load_recovery_id_prop() {
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
new file mode 100644
index 0000000..4d784aa
--- /dev/null
+++ b/init/property_service_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include <gtest/gtest.h>
+
+TEST(property_service, very_long_name_35166374) {
+  // Connect to the property service directly...
+  int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  ASSERT_NE(fd, -1);
+
+  static const char* property_service_socket = "/dev/socket/" PROP_SERVICE_NAME;
+  sockaddr_un addr = {};
+  addr.sun_family = AF_LOCAL;
+  strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
+
+  socklen_t addr_len = strlen(property_service_socket) + offsetof(sockaddr_un, sun_path) + 1;
+  ASSERT_NE(connect(fd, reinterpret_cast<sockaddr*>(&addr), addr_len), -1);
+
+  // ...so we can send it a malformed request.
+  uint32_t msg = PROP_MSG_SETPROP2;
+  uint32_t size = 0xffffffff;
+  uint32_t data = 0xdeadbeef;
+
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(msg)), send(fd, &msg, sizeof(msg), 0));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(size)), send(fd, &size, sizeof(size), 0));
+  ASSERT_EQ(static_cast<ssize_t>(sizeof(data)), send(fd, &data, sizeof(data), 0));
+  ASSERT_EQ(0, close(fd));
+}
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 7e27c3e..1915ced 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -165,7 +165,7 @@
 
     /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
     { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
-                                              "system/bin/hw/android.hardware.bluetooth@1.0-service" },
+                                              "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
 
     /* A non-privileged zygote that spawns isolated processes for web rendering. */
     { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
@@ -180,6 +180,7 @@
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/debuggerd" },
+    { 00700, AID_ROOT,      AID_ROOT,      0, "system/bin/secilc" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 0b0dc09..718d76b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -46,6 +46,12 @@
             suffix: "64",
         },
     },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
 }
 
 test_libraries = [
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
index c5f58b4..ae5c416 100644
--- a/libcutils/tests/multiuser_test.cpp
+++ b/libcutils/tests/multiuser_test.cpp
@@ -17,59 +17,61 @@
 #include <cutils/multiuser.h>
 #include <gtest/gtest.h>
 
+static constexpr auto ERR_GID = static_cast<gid_t>(-1);
+
 TEST(MultiuserTest, TestMerge) {
-    EXPECT_EQ(0, multiuser_get_uid(0, 0));
-    EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
-    EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
-    EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
-    EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
-    EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
-    EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
-    EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+    EXPECT_EQ(0U, multiuser_get_uid(0, 0));
+    EXPECT_EQ(1000U, multiuser_get_uid(0, 1000));
+    EXPECT_EQ(10000U, multiuser_get_uid(0, 10000));
+    EXPECT_EQ(50000U, multiuser_get_uid(0, 50000));
+    EXPECT_EQ(1000000U, multiuser_get_uid(10, 0));
+    EXPECT_EQ(1001000U, multiuser_get_uid(10, 1000));
+    EXPECT_EQ(1010000U, multiuser_get_uid(10, 10000));
+    EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));
 }
 
 TEST(MultiuserTest, TestSplitUser) {
-    EXPECT_EQ(0, multiuser_get_user_id(0));
-    EXPECT_EQ(0, multiuser_get_user_id(1000));
-    EXPECT_EQ(0, multiuser_get_user_id(10000));
-    EXPECT_EQ(0, multiuser_get_user_id(50000));
-    EXPECT_EQ(10, multiuser_get_user_id(1000000));
-    EXPECT_EQ(10, multiuser_get_user_id(1001000));
-    EXPECT_EQ(10, multiuser_get_user_id(1010000));
-    EXPECT_EQ(10, multiuser_get_user_id(1050000));
+    EXPECT_EQ(0U, multiuser_get_user_id(0));
+    EXPECT_EQ(0U, multiuser_get_user_id(1000));
+    EXPECT_EQ(0U, multiuser_get_user_id(10000));
+    EXPECT_EQ(0U, multiuser_get_user_id(50000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1000000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1001000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1010000));
+    EXPECT_EQ(10U, multiuser_get_user_id(1050000));
 }
 
 TEST(MultiuserTest, TestSplitApp) {
-    EXPECT_EQ(0, multiuser_get_app_id(0));
-    EXPECT_EQ(1000, multiuser_get_app_id(1000));
-    EXPECT_EQ(10000, multiuser_get_app_id(10000));
-    EXPECT_EQ(50000, multiuser_get_app_id(50000));
-    EXPECT_EQ(0, multiuser_get_app_id(1000000));
-    EXPECT_EQ(1000, multiuser_get_app_id(1001000));
-    EXPECT_EQ(10000, multiuser_get_app_id(1010000));
-    EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+    EXPECT_EQ(0U, multiuser_get_app_id(0));
+    EXPECT_EQ(1000U, multiuser_get_app_id(1000));
+    EXPECT_EQ(10000U, multiuser_get_app_id(10000));
+    EXPECT_EQ(50000U, multiuser_get_app_id(50000));
+    EXPECT_EQ(0U, multiuser_get_app_id(1000000));
+    EXPECT_EQ(1000U, multiuser_get_app_id(1001000));
+    EXPECT_EQ(10000U, multiuser_get_app_id(1010000));
+    EXPECT_EQ(50000U, multiuser_get_app_id(1050000));
 }
 
 TEST(MultiuserTest, TestCache) {
-    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 0));
-    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 1000));
-    EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
-    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 50000));
-    EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));
+    EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));
 }
 
 TEST(MultiuserTest, TestExt) {
-    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 0));
-    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 1000));
-    EXPECT_EQ(30000, multiuser_get_ext_gid(0, 10000));
-    EXPECT_EQ(-1, multiuser_get_ext_gid(0, 50000));
-    EXPECT_EQ(1030000, multiuser_get_ext_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 1000));
+    EXPECT_EQ(30000U, multiuser_get_ext_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 50000));
+    EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));
 }
 
 TEST(MultiuserTest, TestShared) {
-    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
-    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
-    EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
-    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 50000));
-    EXPECT_EQ(1050000, multiuser_get_shared_gid(10, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));
+    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));
+    EXPECT_EQ(1050000U, multiuser_get_shared_gid(10, 10000));
 }
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 0441fb6..b762ac1 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -101,7 +101,7 @@
     // should always be able to read its port.
     for (int port : {10000, 12345, 15999, 20202, 25000}) {
         for (int type : {SOCK_DGRAM, SOCK_STREAM}) {
-            server = socket_inaddr_any_server(port, SOCK_DGRAM);
+            server = socket_inaddr_any_server(port, type);
             if (server != INVALID_SOCKET) {
                 EXPECT_EQ(port, socket_get_local_port(server));
             }
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 1f08eb4..42f0f37 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -227,19 +227,30 @@
 // successful return, it will be pointing to the last character in the
 // tag line (i.e. the character before the start of the next line).
 //
+// lineNum = 0 removes verbose comments and requires us to cache the
+// content rather than make direct raw references since the content
+// will disappear after the call. A non-zero lineNum means we own the
+// data and it will outlive the call.
+//
 // Returns 0 on success, nonzero on failure.
 static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
     char* cp;
     unsigned long val = strtoul(*pData, &cp, 10);
     if (cp == *pData) {
-        fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n",
+                    lineNum);
+        }
         errno = EINVAL;
         return -1;
     }
 
     uint32_t tagIndex = val;
     if (tagIndex != val) {
-        fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": tag number too large on line %d\n",
+                    lineNum);
+        }
         errno = ERANGE;
         return -1;
     }
@@ -248,7 +259,10 @@
     }
 
     if (*cp == '\n') {
-        fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": missing tag string on line %d\n",
+                    lineNum);
+        }
         errno = EINVAL;
         return -1;
     }
@@ -259,7 +273,10 @@
     size_t tagLen = cp - tag;
 
     if (!isspace(*cp)) {
-        fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+        if (lineNum) {
+            fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n",
+                    lineNum);
+        }
         errno = EINVAL;
         return -1;
     }
@@ -293,9 +310,18 @@
 #endif
     *pData = cp;
 
-    if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
-            MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
-        return 0;
+    if (lineNum) {
+        if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+                MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
+            return 0;
+        }
+    } else {
+        // cache
+        if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+                MapString(std::string(tag, tagLen)),
+                MapString(std::string(fmt, fmtLen)))))) {
+            return 0;
+        }
     }
     errno = EMLINK;
     return -1;
@@ -455,12 +481,55 @@
     if (map) delete map;
 }
 
+// Cache miss, go to logd to acquire a public reference.
+// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
+static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+    // call event tag service to arrange for a new tag
+    char *buf = NULL;
+    // Can not use android::base::StringPrintf, asprintf + free instead.
+    static const char command_template[] = "getEventTag id=%u";
+    int ret = asprintf(&buf, command_template, tag);
+    if (ret > 0) {
+        // Add some buffer margin for an estimate of the full return content.
+        char *cp;
+        size_t size = ret - strlen(command_template) +
+            strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+        if (size > (size_t)ret) {
+            cp = static_cast<char*>(realloc(buf, size));
+            if (cp) {
+                buf = cp;
+            } else {
+                size = ret;
+            }
+        } else {
+            size = ret;
+        }
+        // Ask event log tag service for an existing entry
+        if (__send_log_msg(buf, size) >= 0) {
+            buf[size - 1] = '\0';
+            unsigned long val = strtoul(buf, &cp, 10); // return size
+            if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+                ++cp;
+                if (!scanTagLine(map, &cp, 0)) {
+                    free(buf);
+                    return map->find(tag);
+                }
+            }
+        }
+        free(buf);
+    }
+    return NULL;
+}
+
 // Look up an entry in the map.
 LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
                                                          size_t *len,
                                                          unsigned int tag) {
     if (len) *len = 0;
     const TagFmt* str = map->find(tag);
+    if (!str) {
+        str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+    }
     if (!str) return NULL;
     if (len) *len = str->first.length();
     return str->first.data();
@@ -471,6 +540,9 @@
         const EventTagMap* map, size_t *len, unsigned int tag) {
     if (len) *len = 0;
     const TagFmt* str = map->find(tag);
+    if (!str) {
+        str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+    }
     if (!str) return NULL;
     if (len) *len = str->second.length();
     return str->second.data();
diff --git a/liblog/logprint.c b/liblog/logprint.c
index e61850d..af52528 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -625,14 +625,14 @@
 }
 
 static bool findChar(const char** cp, size_t* len, int c) {
-    while (*len && isspace(**cp)) {
-        ++*cp;
-        --*len;
+    while ((*len) && isspace(*(*cp))) {
+        ++(*cp);
+        --(*len);
     }
     if (c == INT_MAX) return *len;
-    if (*len && (**cp == c)) {
-        ++*cp;
-        --*len;
+    if ((*len) && (*(*cp) == c)) {
+        ++(*cp);
+        --(*len);
         return true;
     }
     return false;
@@ -991,7 +991,7 @@
 LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
         struct logger_entry *buf,
         AndroidLogEntry *entry,
-        const EventTagMap *map __unused,
+        const EventTagMap *map __unused, // only on !__ANDROID__
         char *messageBuf, int messageBufLen)
 {
     size_t inCount;
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
index 25a53aa..2c80acd 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -86,19 +86,22 @@
 
 #if !defined(_WIN32)
 
-inline Condition::Condition() {
-    pthread_cond_init(&mCond, NULL);
+inline Condition::Condition() : Condition(PRIVATE) {
 }
 inline Condition::Condition(int type) {
+    pthread_condattr_t attr;
+    pthread_condattr_init(&attr);
+#if defined(__linux__)
+    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+#endif
+
     if (type == SHARED) {
-        pthread_condattr_t attr;
-        pthread_condattr_init(&attr);
         pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
-        pthread_cond_init(&mCond, &attr);
-        pthread_condattr_destroy(&attr);
-    } else {
-        pthread_cond_init(&mCond, NULL);
     }
+
+    pthread_cond_init(&mCond, &attr);
+    pthread_condattr_destroy(&attr);
+
 }
 inline Condition::~Condition() {
     pthread_cond_destroy(&mCond);
@@ -109,7 +112,7 @@
 inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
     struct timespec ts;
 #if defined(__linux__)
-    clock_gettime(CLOCK_REALTIME, &ts);
+    clock_gettime(CLOCK_MONOTONIC, &ts);
 #else // __APPLE__
     // Apple doesn't support POSIX clocks.
     struct timeval t;
diff --git a/libutils/include/utils/Singleton.h b/libutils/include/utils/Singleton.h
index 7cc4c18..a989a47 100644
--- a/libutils/include/utils/Singleton.h
+++ b/libutils/include/utils/Singleton.h
@@ -26,6 +26,16 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+// Singleton<TYPE> may be used in multiple libraries, only one of which should
+// define the static member variables using ANDROID_SINGLETON_STATIC_INSTANCE.
+// Turn off -Wundefined-var-template so other users don't get:
+// instantiation of variable 'android::Singleton<TYPE>::sLock' required here,
+// but no definition is available
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundefined-var-template"
+#endif
+
 template <typename TYPE>
 class ANDROID_API Singleton
 {
@@ -56,11 +66,9 @@
     static TYPE* sInstance;
 };
 
-template <typename TYPE>
-Mutex Singleton<TYPE>::sLock;
-
-template <typename TYPE>
-TYPE* Singleton<TYPE>::sInstance;
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
 
 /*
  * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index ec6b67f..ea606a1 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -18,33 +18,74 @@
 
 cc_test {
     name: "libutils_tests",
+    host_supported: true,
 
     srcs: [
-        "BlobCache_test.cpp",
         "BitSet_test.cpp",
-        "Looper_test.cpp",
         "LruCache_test.cpp",
-        "RefBase_test.cpp",
+        "Singleton_test.cpp",
         "String8_test.cpp",
         "StrongPointer_test.cpp",
-        "SystemClock_test.cpp",
         "Unicode_test.cpp",
         "Vector_test.cpp",
     ],
 
-    shared_libs: [
-        "libz",
-        "liblog",
-        "libcutils",
-        "libutils",
+    target: {
+        android: {
+            srcs: [
+                "BlobCache_test.cpp",
+                "Looper_test.cpp",
+                "RefBase_test.cpp",
+                "SystemClock_test.cpp",
+            ],
+            shared_libs: [
+                "libz",
+                "liblog",
+                "libcutils",
+                "libutils",
+                "libbase",
+                "libdl",
+            ],
+        },
+        linux: {
+            srcs: [
+                "Looper_test.cpp",
+                "RefBase_test.cpp",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libutils",
+                "liblog",
+                "libbase",
+            ],
+            host_ldlibs: ["-ldl"],
+        },
+    },
+
+    required: [
+        "libutils_tests_singleton1",
+        "libutils_tests_singleton2",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
     ],
 }
 
-cc_test_host {
-    name: "libutils_tests_host",
-    srcs: ["Vector_test.cpp"],
-    static_libs: [
-        "libutils",
-        "liblog",
-    ],
+cc_test_library {
+    name: "libutils_tests_singleton1",
+    host_supported: true,
+    relative_install_path: "libutils_tests",
+    srcs: ["Singleton_test1.cpp"],
+}
+
+cc_test_library {
+    name: "libutils_tests_singleton2",
+    host_supported: true,
+    relative_install_path: "libutils_tests",
+    srcs: ["Singleton_test2.cpp"],
+    shared_libs: ["libutils_tests_singleton1"],
 }
diff --git a/libutils/tests/Singleton_test.cpp b/libutils/tests/Singleton_test.cpp
new file mode 100644
index 0000000..9acd3c3
--- /dev/null
+++ b/libutils/tests/Singleton_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Singleton_test"
+
+#include <dlfcn.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <utils/Singleton.h>
+
+#include <gtest/gtest.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+TEST(SingletonTest, bug35674422) {
+    std::string path = android::base::GetExecutableDirectory();
+    // libutils_tests_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
+    // definition of SingletonTestData, load it first.
+    std::string lib = android::base::StringPrintf("%s/libutils_tests_singleton1.so", path.c_str());
+    void* handle1 = dlopen(lib.c_str(), RTLD_NOW);
+    ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+    // libutils_tests_singleton2.so references SingletonTestData but should not
+    // have a definition
+    lib = android::base::StringPrintf("%s/libutils_tests_singleton2.so", path.c_str());
+    void* handle2 = dlopen(lib.c_str(), RTLD_NOW);
+    ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+    using has_fn_t = decltype(&singletonHasInstance);
+    using get_fn_t = decltype(&singletonGetInstanceContents);
+    using set_fn_t = decltype(&singletonSetInstanceContents);
+
+    has_fn_t has1 = reinterpret_cast<has_fn_t>(dlsym(handle1, "singletonHasInstance"));
+    ASSERT_TRUE(has1 != nullptr) << dlerror();
+    has_fn_t has2 = reinterpret_cast<has_fn_t>(dlsym(handle2, "singletonHasInstance"));
+    ASSERT_TRUE(has2 != nullptr) << dlerror();
+    get_fn_t get1 = reinterpret_cast<get_fn_t>(dlsym(handle1, "singletonGetInstanceContents"));
+    ASSERT_TRUE(get1 != nullptr) << dlerror();
+    get_fn_t get2 = reinterpret_cast<get_fn_t>(dlsym(handle2, "singletonGetInstanceContents"));
+    ASSERT_TRUE(get2 != nullptr) << dlerror();
+    set_fn_t set1 = reinterpret_cast<set_fn_t>(dlsym(handle2, "singletonSetInstanceContents"));
+    ASSERT_TRUE(set1 != nullptr) << dlerror();
+
+    EXPECT_FALSE(has1());
+    EXPECT_FALSE(has2());
+    set1(12345678U);
+    EXPECT_TRUE(has1());
+    EXPECT_TRUE(has2());
+    EXPECT_EQ(12345678U, get1());
+    EXPECT_EQ(12345678U, get2());
+}
+
+}
diff --git a/libutils/tests/Singleton_test.h b/libutils/tests/Singleton_test.h
new file mode 100644
index 0000000..c77d9ff
--- /dev/null
+++ b/libutils/tests/Singleton_test.h
@@ -0,0 +1,41 @@
+/*
+ * 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 ANDROID_UTILS_SINGLETON_TEST_H
+#define ANDROID_UTILS_SINGLETON_TEST_H
+
+#include <sys/cdefs.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+struct SingletonTestData : Singleton<SingletonTestData> {
+    unsigned int contents;
+};
+
+__BEGIN_DECLS
+
+unsigned int singletonGetInstanceContents();
+void singletonSetInstanceContents(unsigned int);
+bool singletonHasInstance();
+
+__END_DECLS
+
+}
+
+#endif // ANDROID_UTILS_SINGLETON_TEST_H
+
diff --git a/libutils/tests/Singleton_test1.cpp b/libutils/tests/Singleton_test1.cpp
new file mode 100644
index 0000000..4a91ec0
--- /dev/null
+++ b/libutils/tests/Singleton_test1.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <utils/Singleton.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and
+// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.
+ANDROID_SINGLETON_STATIC_INSTANCE(SingletonTestData);
+
+void singletonSetInstanceContents(unsigned int contents) {
+    SingletonTestData::getInstance().contents = contents;
+}
+
+unsigned int singletonGetInstanceContents() {
+    return SingletonTestData::getInstance().contents;
+}
+
+bool singletonHasInstance() {
+    return SingletonTestData::hasInstance();
+}
+
+}
diff --git a/libutils/tests/Singleton_test2.cpp b/libutils/tests/Singleton_test2.cpp
new file mode 100644
index 0000000..eb2a9df
--- /dev/null
+++ b/libutils/tests/Singleton_test2.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <utils/Singleton.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and
+// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.
+
+void singletonSetInstanceContents(unsigned int contents) {
+    SingletonTestData::getInstance().contents = contents;
+}
+
+unsigned int singletonGetInstanceContents() {
+    return SingletonTestData::getInstance().contents;
+}
+
+bool singletonHasInstance() {
+    return SingletonTestData::hasInstance();
+}
+
+}
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
index 323a6f2..153cf96 100644
--- a/libutils/tests/StrongPointer_test.cpp
+++ b/libutils/tests/StrongPointer_test.cpp
@@ -21,13 +21,13 @@
 
 using namespace android;
 
-class Foo : public LightRefBase<Foo> {
+class SPFoo : public LightRefBase<SPFoo> {
 public:
-    explicit Foo(bool* deleted_check) : mDeleted(deleted_check) {
+    explicit SPFoo(bool* deleted_check) : mDeleted(deleted_check) {
         *mDeleted = false;
     }
 
-    ~Foo() {
+    ~SPFoo() {
         *mDeleted = true;
     }
 private:
@@ -36,13 +36,13 @@
 
 TEST(StrongPointer, move) {
     bool isDeleted;
-    Foo* foo = new Foo(&isDeleted);
+    SPFoo* foo = new SPFoo(&isDeleted);
     ASSERT_EQ(0, foo->getStrongCount());
     ASSERT_FALSE(isDeleted) << "Already deleted...?";
-    sp<Foo> sp1(foo);
+    sp<SPFoo> sp1(foo);
     ASSERT_EQ(1, foo->getStrongCount());
     {
-        sp<Foo> sp2 = std::move(sp1);
+        sp<SPFoo> sp2 = std::move(sp1);
         ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
         ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
         // The strong count isn't increasing, let's double check the old object
@@ -52,7 +52,7 @@
     ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
     {
         // Now let's double check it deletes on time
-        sp<Foo> sp2 = std::move(sp1);
+        sp<SPFoo> sp2 = std::move(sp1);
     }
     ASSERT_TRUE(isDeleted) << "foo was leaked!";
 }
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7f852d4..d67dee5 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -67,8 +67,8 @@
     std::vector<const char*> argv_hold;
     std::vector<std::string> envs;
     std::vector<const char*> envp_hold;
-    int output_fd;
-    int error_fd;
+    int output_fd; // duplication of fileno(output) (below)
+    int error_fd;  // duplication of fileno(error) (below)
 
     // library
     int fds[2];    // From popen call
@@ -284,6 +284,15 @@
         return;
     }
     context->output = fdopen(context->output_fd, "web");
+    if (context->output == NULL) {
+        logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
+        return;
+    }
+    if (context->stderr_stdout) {
+        close_error(context);
+        context->error = context->output;
+        context->error_fd = context->output_fd;
+    }
 
     context->outByteCount = 0;
 }
@@ -788,6 +797,7 @@
         // Simulate shell stderr redirect parsing
         if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
 
+        // Append to file not implemented, just open file
         size_t skip = (argv[i][2] == '>') + 2;
         if (!strcmp(&argv[i][skip], "/dev/null")) {
             context->stderr_null = true;
@@ -799,16 +809,29 @@
                     "stderr redirection to file %s unsupported, skipping\n",
                     &argv[i][skip]);
         }
+        // Only the first one
+        break;
+    }
+
+    const char* filename = NULL;
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stdout redirect parsing
+        if (argv[i][0] != '>') continue;
+
+        // Append to file not implemented, just open file
+        filename = &argv[i][(argv[i][1] == '>') + 1];
+        // Only the first one
+        break;
     }
 
     // Deal with setting up file descriptors and FILE pointers
-    if (context->error_fd >= 0) {
+    if (context->error_fd >= 0) { // Is an error file descriptor supplied?
         if (context->error_fd == context->output_fd) {
             context->stderr_stdout = true;
-        } else if (context->stderr_null) {
+        } else if (context->stderr_null) { // redirection told us to close it
             close(context->error_fd);
             context->error_fd = -1;
-        } else {
+        } else { // All Ok, convert error to a FILE pointer
             context->error = fdopen(context->error_fd, "web");
             if (!context->error) {
                 context->retval = -errno;
@@ -819,22 +842,32 @@
             }
         }
     }
-    if (context->output_fd >= 0) {
-        context->output = fdopen(context->output_fd, "web");
-        if (!context->output) {
-            context->retval = -errno;
-            fprintf(context->stderr_stdout ? stdout : context->error,
-                    "Failed to fdopen(output_fd=%d) %s\n", context->output_fd,
-                    strerror(errno));
-            goto exit;
+    if (context->output_fd >= 0) { // Is an output file descriptor supplied?
+        if (filename) { // redirect to file, close the supplied file descriptor.
+            close(context->output_fd);
+            context->output_fd = -1;
+        } else { // All Ok, convert output to a FILE pointer
+            context->output = fdopen(context->output_fd, "web");
+            if (!context->output) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : context->error,
+                        "Failed to fdopen(output_fd=%d) %s\n",
+                        context->output_fd, strerror(errno));
+                goto exit;
+            }
         }
     }
+    if (filename) { // We supplied an output file redirected in command line
+        context->output = fopen(filename, "web");
+    }
+    // Deal with 2>&1
     if (context->stderr_stdout) context->error = context->output;
+    // Deal with 2>/dev/null
     if (context->stderr_null) {
         context->error_fd = -1;
         context->error = NULL;
     }
-    // Only happens if output=stdout
+    // Only happens if output=stdout or output=filename
     if ((context->output_fd < 0) && context->output) {
         context->output_fd = fileno(context->output);
     }
@@ -1351,6 +1384,8 @@
         for (int i = optind ; i < argc ; i++) {
             // skip stderr redirections of _all_ kinds
             if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
+            // skip stdout redirections of _all_ kinds
+            if (argv[i][0] == '>') continue;
 
             err = android_log_addFilterString(context->logformat, argv[i]);
             if (err < 0) {
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 1c3feba..22aca17 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -27,11 +27,12 @@
     -fno-builtin \
 
 # -----------------------------------------------------------------------------
-# Benchmarks (actually a gTest where the result code does not matter)
+# Benchmarks
 # ----------------------------------------------------------------------------
 
 benchmark_src_files := \
-    logcat_benchmark.cpp
+    logcat_benchmark.cpp \
+    exec_benchmark.cpp \
 
 # Build benchmarks for the device. Run with:
 #   adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -40,7 +41,8 @@
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
 LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_TEST)
+LOCAL_SHARED_LIBRARIES := libbase liblogcat
+include $(BUILD_NATIVE_BENCHMARK)
 
 # -----------------------------------------------------------------------------
 # Unit tests.
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
new file mode 100644
index 0000000..c30a5f5
--- /dev/null
+++ b/logcat/tests/exec_benchmark.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 <stdio.h>
+
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <log/logcat.h>
+
+// Dump the statistics and report results
+
+static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        FILE* fp = popen(cmd, "r");
+        std::string ret;
+        android::base::ReadFdToString(fileno(fp), &ret);
+        pclose(fp);
+    }
+}
+
+static void BM_logcat_stat_popen_libc(benchmark::State& state) {
+    logcat_popen_libc(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_libc);
+
+static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        android_logcat_context ctx;
+        FILE* fp = android_logcat_popen(&ctx, cmd);
+        std::string ret;
+        android::base::ReadFdToString(fileno(fp), &ret);
+        android_logcat_pclose(&ctx, fp);
+    }
+}
+
+static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
+    logcat_popen_liblogcat(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_liblogcat);
+
+static void logcat_system_libc(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        system(cmd);
+    }
+}
+
+static void BM_logcat_stat_system_libc(benchmark::State& state) {
+    logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_libc);
+
+static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
+    while (state.KeepRunning()) {
+        android_logcat_system(cmd);
+    }
+}
+
+static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
+    logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_liblogcat);
+
+// Dump the logs and report results
+
+static void BM_logcat_dump_popen_libc(benchmark::State& state) {
+    logcat_popen_libc(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_libc);
+
+static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
+    logcat_popen_liblogcat(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_liblogcat);
+
+static void BM_logcat_dump_system_libc(benchmark::State& state) {
+    logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_libc);
+
+static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
+    logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
index dd85164..8d88628 100644
--- a/logcat/tests/logcat_benchmark.cpp
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -18,19 +18,22 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <gtest/gtest.h>
+#include <benchmark/benchmark.h>
 
 static const char begin[] = "--------- beginning of ";
 
-TEST(logcat, sorted_order) {
-    FILE *fp;
+static void BM_logcat_sorted_order(benchmark::State& state) {
+    FILE* fp;
 
-    ASSERT_TRUE(NULL != (fp = popen(
-      "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
-      "r")));
+    if (!state.KeepRunning()) return;
+
+    fp = popen(
+        "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
+        "r");
+    if (!fp) return;
 
     class timestamp {
-    private:
+       private:
         int month;
         int day;
         int hour;
@@ -39,44 +42,39 @@
         int millisecond;
         bool ok;
 
-    public:
-        void init(const char *buffer)
-        {
+       public:
+        void init(const char* buffer) {
             ok = false;
             if (buffer != NULL) {
-                ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ",
-                    &month, &day, &hour, &minute, &second, &millisecond) == 6;
+                ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", &month, &day, &hour,
+                            &minute, &second, &millisecond) == 6;
             }
         }
 
-        explicit timestamp(const char *buffer)
-        {
+        explicit timestamp(const char* buffer) {
             init(buffer);
         }
 
-        bool operator< (timestamp &T)
-        {
-            return !ok || !T.ok
-             || (month < T.month)
-             || ((month == T.month)
-              && ((day < T.day)
-               || ((day == T.day)
-                && ((hour < T.hour)
-                 || ((hour == T.hour)
-                  && ((minute < T.minute)
-                   || ((minute == T.minute)
-                    && ((second < T.second)
-                     || ((second == T.second)
-                      && (millisecond < T.millisecond))))))))));
+        bool operator<(timestamp& T) {
+            return !ok || !T.ok || (month < T.month) ||
+                   ((month == T.month) &&
+                    ((day < T.day) ||
+                     ((day == T.day) &&
+                      ((hour < T.hour) ||
+                       ((hour == T.hour) &&
+                        ((minute < T.minute) ||
+                         ((minute == T.minute) &&
+                          ((second < T.second) ||
+                           ((second == T.second) &&
+                            (millisecond < T.millisecond))))))))));
         }
 
-        bool valid(void)
-        {
+        bool valid(void) {
             return ok;
         }
     } last(NULL);
 
-    char *last_buffer = NULL;
+    char* last_buffer = NULL;
     char buffer[5120];
 
     int count = 0;
@@ -114,15 +112,22 @@
 
     // Allow few fails, happens with readers active
     fprintf(stderr, "%s: %d/%d out of order entries\n",
-            (next_lt_last)
-                ? ((next_lt_last <= max_ok)
-                    ? "WARNING"
-                    : "ERROR")
-                : "INFO",
+            (next_lt_last) ? ((next_lt_last <= max_ok) ? "WARNING" : "ERROR")
+                           : "INFO",
             next_lt_last, count);
 
-    EXPECT_GE(max_ok, next_lt_last);
+    if (next_lt_last > max_ok) {
+        fprintf(stderr, "EXPECT_GE(max_ok=%d, next_lt_last=%d)\n", max_ok,
+                next_lt_last);
+    }
 
     // sample statistically too small
-    EXPECT_LT(100, count);
+    if (count < 100) {
+        fprintf(stderr, "EXPECT_LT(100, count=%d)\n", count);
+    }
+
+    state.KeepRunning();
 }
+BENCHMARK(BM_logcat_sorted_order);
+
+BENCHMARK_MAIN();
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 74e0ea5..6ad7351 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -301,6 +301,7 @@
 
     const char *name = NULL;
     const char *format = NULL;
+    const char *id = NULL;
     for (int i = 1; i < argc; ++i) {
         static const char _name[] = "name=";
         if (!strncmp(argv[i], _name, strlen(_name))) {
@@ -313,6 +314,21 @@
             format = argv[i] + strlen(_format);
             continue;
         }
+
+        static const char _id[] = "id=";
+        if (!strncmp(argv[i], _id, strlen(_id))) {
+            id = argv[i] + strlen(_id);
+            continue;
+        }
+    }
+
+    if (id) {
+        if (format || name) {
+            cli->sendMsg("can not mix id= with either format= or name=");
+            return 0;
+        }
+        cli->sendMsg(package_string(mBuf.formatEntry(atoi(id), uid)).c_str());
+        return 0;
     }
 
     cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index da63e12..9feef32 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -140,6 +140,9 @@
                                   const char *name, const char *format) {
         return tags.formatGetEventTag(uid, name, format);
     }
+    std::string formatEntry(uint32_t tag, uid_t uid) {
+        return tags.formatEntry(tag, uid);
+    }
     const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
 
     // helper must be protected directly or implicitly by lock()/unlock()
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index a109592..64aa219 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -836,6 +836,11 @@
     return ret;
 }
 
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid) {
+    android::RWLock::AutoRLock readLock(rwlock);
+    return formatEntry_locked(tag, uid);
+}
+
 std::string LogTags::formatGetEventTag(uid_t uid,
                                        const char* name, const char* format) {
     bool all = name && (name[0] == '*') && !name[1];
diff --git a/logd/LogTags.h b/logd/LogTags.h
index 37a6d96..4457c46 100644
--- a/logd/LogTags.h
+++ b/logd/LogTags.h
@@ -106,6 +106,7 @@
     // reverse lookup from tag
     const char* tagToName(uint32_t tag) const;
     const char* tagToFormat(uint32_t tag) const;
+    std::string formatEntry(uint32_t tag, uid_t uid);
     // find associated tag
     uint32_t nameToTag(const char* name) const;
 
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index adf583b..8a35059 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -836,6 +836,23 @@
 #endif
 }
 
+TEST(logd, getEventTag_42) {
+#ifdef __ANDROID__
+    char buffer[256];
+    memset(buffer, 0, sizeof(buffer));
+    snprintf(buffer, sizeof(buffer), "getEventTag id=42");
+    send_to_control(buffer, sizeof(buffer));
+    buffer[sizeof(buffer) - 1] = '\0';
+    char *cp;
+    long ret = strtol(buffer, &cp, 10);
+    EXPECT_GT(ret, 16);
+    EXPECT_TRUE(strstr(buffer, "\t(to life the universe etc|3)") != NULL);
+    EXPECT_TRUE(strstr(buffer, "answer") != NULL);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
 TEST(logd, getEventTag_newentry) {
 #ifdef __ANDROID__
     char buffer[256];