Merge "libmemtrack: check return value for getMemory()"
diff --git a/adb/Android.mk b/adb/Android.mk
index c51e0b8..b444afa 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -216,9 +216,9 @@
 LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
 LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
 LOCAL_SANITIZE := $(adb_host_sanitize)
+LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_STATIC_LIBRARIES := \
     libadb \
-    libbase \
     libcrypto_utils \
     libcrypto \
     libcutils \
@@ -232,7 +232,7 @@
 LOCAL_LDFLAGS_windows := -municode
 LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
 LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
-LOCAL_LDLIBS_windows := -lws2_32 -luserenv -static -lwinpthread
+LOCAL_LDLIBS_windows := -lws2_32 -luserenv
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 
 LOCAL_MULTILIB := first
@@ -249,7 +249,7 @@
 
 # Use wmain instead of main
 LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_windows := -lws2_32 -lgdi32 -static -lwinpthread
+LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 
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/commandline.cpp b/adb/commandline.cpp
index 5a2206f..3de7be6 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -2115,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) {
@@ -2233,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/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/properties.h b/base/include/android-base/properties.h
index e2324b7..4de5e57 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -66,6 +66,12 @@
                      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 acd6c0f..32c0128 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -108,8 +108,10 @@
   ts.tv_nsec = ns.count();
 }
 
+using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
+
 static void UpdateTimeSpec(timespec& ts,
-                           const std::chrono::time_point<std::chrono::steady_clock>& timeout) {
+                           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) {
@@ -119,13 +121,16 @@
   }
 }
 
-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`.
+// 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();
-  std::chrono::time_point<std::chrono::steady_clock> absolute_timeout = now + relative_timeout;
-  timespec ts;
+  absolute_timeout = now + relative_timeout;
 
   // Find the property's prop_info*.
   const prop_info* pi;
@@ -133,14 +138,25 @@
   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.
+    timespec ts;
     UpdateTimeSpec(ts, absolute_timeout);
-    if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return false;
+    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 true;
@@ -152,5 +168,11 @@
   }
 }
 
+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 c68c2f8..1bbe482 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -150,3 +150,25 @@
   // 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/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 81d70df..f2975d1 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -45,42 +45,6 @@
   return true;
 }
 
-static bool check_dumpable(pid_t pid) {
-  // /proc/<pid> is owned by the effective UID of the process.
-  // Ownership of most of the other files in /proc/<pid> varies based on PR_SET_DUMPABLE.
-  // If PR_GET_DUMPABLE would return 0, they're owned by root, instead.
-  std::string proc_pid_path = android::base::StringPrintf("/proc/%d/", pid);
-  std::string proc_pid_status_path = proc_pid_path + "/status";
-
-  unique_fd proc_pid_fd(open(proc_pid_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
-  if (proc_pid_fd == -1) {
-    return false;
-  }
-  unique_fd proc_pid_status_fd(openat(proc_pid_fd, "status", O_RDONLY | O_CLOEXEC));
-  if (proc_pid_status_fd == -1) {
-    return false;
-  }
-
-  struct stat proc_pid_st;
-  struct stat proc_pid_status_st;
-  if (fstat(proc_pid_fd.get(), &proc_pid_st) != 0 ||
-      fstat(proc_pid_status_fd.get(), &proc_pid_status_st) != 0) {
-    return false;
-  }
-
-  // We can't figure out if a process is dumpable if its effective UID is root, but that's fine
-  // because being root bypasses the PR_SET_DUMPABLE check for ptrace.
-  if (proc_pid_st.st_uid == 0) {
-    return true;
-  }
-
-  if (proc_pid_status_st.st_uid == 0) {
-    return false;
-  }
-
-  return true;
-}
-
 bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
                             int timeout_ms) {
   LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
@@ -114,11 +78,6 @@
     return true;
   };
 
-  if (!check_dumpable(pid)) {
-    dprintf(output_fd.get(), "target pid %d is not dumpable\n", pid);
-    return true;
-  }
-
   sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
   if (sockfd == -1) {
     PLOG(ERROR) << "libdebugger_client: failed to create socket";
diff --git a/debuggerd/libdebuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
index 9d44345..1f4f58a 100644
--- a/debuggerd/libdebuggerd/test/sys/system_properties.h
+++ b/debuggerd/libdebuggerd/test/sys/system_properties.h
@@ -32,7 +32,6 @@
 // This is just enough to get the property code to compile on
 // the host.
 
-#define PROP_NAME_MAX   32
 #define PROP_VALUE_MAX  92
 
 #endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index f1a7ad6..2863a26 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -38,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 25c41b9..a50817e 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -435,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];
@@ -673,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.
@@ -750,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";
@@ -970,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";
@@ -1020,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;
 
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 9decb27..5b2f218 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -49,15 +49,8 @@
     }
 
     // lastly, check the device tree
-    std::string file_name = kAndroidDtDir + "/compatible";
-    std::string dt_value;
-    if (android::base::ReadFileToString(file_name, &dt_value)) {
-        if (dt_value != "android,firmware") {
-            LERROR << "Error finding compatible android DT node";
-            return false;
-        }
-
-        file_name = kAndroidDtDir + "/" + key;
+    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;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4c4a7b6..de74685 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -172,6 +172,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)
@@ -340,29 +358,10 @@
     return f;
 }
 
-static bool is_dt_compatible() {
-    std::string file_name = kAndroidDtDir + "/compatible";
-    std::string dt_value;
-    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 false;
-}
-
 static bool is_dt_fstab_compatible() {
     std::string dt_value;
     std::string file_name = kAndroidDtDir + "/fstab/compatible";
-
-    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 (read_dt_file(file_name, &dt_value)) {
         if (dt_value == "android,fstab") {
             return true;
         }
@@ -399,41 +398,36 @@
         std::string file_name;
         std::string value;
         file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
+        if (!read_dt_file(file_name, &value)) {
             LERROR << "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(android::base::StringPrintf("/%s", dp->d_name));
 
         file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
+        if (!read_dt_file(file_name, &value)) {
             LERROR << "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 = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
+        if (!read_dt_file(file_name, &value)) {
             LERROR << "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 = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
-        if (!android::base::ReadFileToString(file_name, &value)) {
+        if (!read_dt_file(file_name, &value)) {
             LERROR << "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, " ");
@@ -443,6 +437,17 @@
     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)
 {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 95295d8..4e2ac8b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -117,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_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 5b81a54..e8ed6a2 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -858,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 << "'";
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 3e50a40..a014db5 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -107,7 +107,7 @@
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
-int fs_mgr_do_tmpfs_mount(char *n_name);
+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);
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 86b6287..e6eb3bc 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -56,7 +56,7 @@
 class GateKeeperProxy : public BnGateKeeperService {
 public:
     GateKeeperProxy() {
-        hw_device = IGatekeeper::getService("gatekeeper");
+        hw_device = IGatekeeper::getService();
 
         if (hw_device == nullptr) {
             ALOGW("falling back to software GateKeeper");
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index cae6c4c..ed1971a 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -82,7 +82,7 @@
     // Initialize the board HAL - Equivalent of healthd_board_init(config)
     // in charger/recovery mode.
 
-    gHealth = IHealth::getService("health");
+    gHealth = IHealth::getService();
     if (gHealth == nullptr) {
         KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
         return;
diff --git a/include/system/graphics-base.h b/include/system/graphics-base.h
index 2aac2d8..0366592 100644
--- a/include/system/graphics-base.h
+++ b/include/system/graphics-base.h
@@ -1,5 +1,6 @@
 // This file is autogenerated by hidl-gen. Do not edit manually.
 // Source: android.hardware.graphics.common@1.0
+// Root: android.hardware:hardware/interfaces
 
 #ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
 #define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8ab5e30..270afcf 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/init.cpp b/init/init.cpp
index 3cb1276..53e7482 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -43,6 +43,7 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/iosched_policy.h>
 #include <cutils/list.h>
@@ -52,6 +53,8 @@
 
 #include <fstream>
 #include <memory>
+#include <set>
+#include <vector>
 
 #include "action.h"
 #include "bootchart.h"
@@ -616,6 +619,133 @@
     return 0;
 }
 
+/*
+ * Forks, executes the provided program in the child, and waits for the completion in the parent.
+ *
+ * Returns true if the child exited with status code 0, returns false otherwise.
+ */
+static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
+                                                char* const envp[]) {
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        PLOG(ERROR) << "Failed to fork for " << filename;
+        return false;
+    }
+
+    if (child_pid == 0) {
+        // fork succeeded -- this is executing in the child process
+        if (execve(filename, argv, envp) == -1) {
+            PLOG(ERROR) << "Failed to execve " << filename;
+            return false;
+        }
+        // Unreachable because execve will have succeeded and replaced this code
+        // with child process's code.
+        _exit(127);
+        return false;
+    } else {
+        // fork succeeded -- this is executing in the original/parent process
+        int status;
+        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+            PLOG(ERROR) << "Failed to wait for " << filename;
+            return false;
+        }
+
+        if (WIFEXITED(status)) {
+            int status_code = WEXITSTATUS(status);
+            if (status_code == 0) {
+                return true;
+            } else {
+                LOG(ERROR) << filename << " exited with status " << status_code;
+            }
+        } else if (WIFSIGNALED(status)) {
+            LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+        } else if (WIFSTOPPED(status)) {
+            LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+        } else {
+            LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+        }
+
+        return false;
+    }
+}
+
+static constexpr const char plat_policy_cil_file[] = "/plat_sepolicy.cil";
+
+static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
+
+/*
+ * Loads SELinux policy split across platform/system and non-platform/vendor files.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_split_policy() {
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+    // * platform -- policy needed due to logic contained in the system image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
+    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+    //   with newer versions of platform policy.
+    //
+    // secilc is invoked to compile the above three policy files into a single monolithic policy
+    // file. This file is then loaded into the kernel.
+
+    LOG(INFO) << "Compiling SELinux policy";
+
+    // We store the output of the compilation on /dev because this is the most convenient tmpfs
+    // storage mount available this early in the boot sequence.
+    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+    android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+    if (compiled_sepolicy_fd < 0) {
+        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+        return false;
+    }
+
+    const char* compile_args[] = {"/system/bin/secilc", plat_policy_cil_file, "-M", "true", "-c",
+                                  "30",  // TODO: pass in SELinux policy version from build system
+                                  "/mapping_sepolicy.cil", "/nonplat_sepolicy.cil", "-o",
+                                  compiled_sepolicy,
+                                  // We don't care about file_contexts output by the compiler
+                                  "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
+                                  nullptr};
+
+    if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
+        unlink(compiled_sepolicy);
+        return false;
+    }
+    unlink(compiled_sepolicy);
+
+    LOG(INFO) << "Loading compiled SELinux policy";
+    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+        LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+        return false;
+    }
+
+    return true;
+}
+
+/*
+ * Loads SELinux policy from a monolithic file.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_monolithic_policy() {
+    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+    if (selinux_android_load_policy() < 0) {
+        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Loads SELinux policy into the kernel.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_policy() {
+    return selinux_is_split_policy_device() ? selinux_load_split_policy()
+                                            : selinux_load_monolithic_policy();
+}
+
 static void selinux_initialize(bool in_kernel_domain) {
     Timer t;
 
@@ -626,10 +756,9 @@
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
     if (in_kernel_domain) {
-        LOG(INFO) << "Loading SELinux policy...";
-        if (selinux_android_load_policy() < 0) {
-            PLOG(ERROR) << "failed to load policy";
-            security_failure();
+        LOG(INFO) << "Loading SELinux policy";
+        if (!selinux_load_policy()) {
+            panic();
         }
 
         bool kernel_enforcing = (security_getenforce() == 1);
@@ -700,71 +829,15 @@
     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;
+// 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<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
-    // TODO: add std::map<std::string, fstab_rec*> so all required information about
-    // them can be gathered at once in a single loop
-    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;
-    }
-
-    // don't allow verifyatboot for early mounted partitions
-    if ((odm_rec && fs_mgr_is_verifyatboot(odm_rec)) ||
-        (system_rec && fs_mgr_is_verifyatboot(system_rec)) ||
-        (vendor_rec && fs_mgr_is_verifyatboot(vendor_rec))) {
-        LOG(ERROR) << "Early mount partitions can't be verified at boot";
-        return false;
-    }
-
-    // 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)));
-
-    // check for verified partitions
-    bool need_verity = ((odm_rec && fs_mgr_is_verified(odm_rec)) ||
-                        (system_rec && fs_mgr_is_verified(system_rec)) ||
-                        (vendor_rec && fs_mgr_is_verified(vendor_rec)));
-
-    // 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.
-    std::string meta_partition;
-    if (odm_rec && odm_rec->verity_loc) {
-        meta_partition = basename(odm_rec->verity_loc);
-    } else if (system_rec && system_rec->verity_loc) {
-        meta_partition = basename(system_rec->verity_loc);
-    } else if (vendor_rec && vendor_rec->verity_loc) {
-        meta_partition = basename(vendor_rec->verity_loc);
-    }
-
-    bool found_odm = !odm_rec;
-    bool found_system = !system_rec;
-    bool found_vendor = !vendor_rec;
-    bool found_meta = meta_partition.empty();
-    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;
         }
@@ -779,59 +852,121 @@
             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 || 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 || 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 || count_vendor == 2) {
-                    found_vendor = true;
-                }
-
-                create_this_node = true;
-            } else if (!found_meta && (meta_partition == uevent->partition_name)) {
-                LOG(VERBOSE) <<  "early_mount: found (" << uevent->partition_name << ") partition";
-                found_meta = 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_meta && 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;
     });
+}
+
+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;
+
+    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);
+            }
+        }
+    }
+
+    // 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));
+    }
+
+    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() {
+    // skip early mount if we're in recovery mode
+    if (access("/sbin/recovery", F_OK) == 0) {
+        LOG(INFO) << "Early mount skipped (recovery mode)";
+        return true;
+    }
+
+    // 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
@@ -839,10 +974,10 @@
                     [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
     }
 
-    bool success = true;
-    if (odm_rec && !(success = early_mount_one(odm_rec))) goto done;
-    if (system_rec && !(success = early_mount_one(system_rec))) goto done;
-    if (vendor_rec && !(success = early_mount_one(vendor_rec))) goto done;
+    for (auto fstab_rec : early_fstab_recs) {
+        if (!early_mount_one(fstab_rec)) goto done;
+    }
+    success = true;
 
 done:
     device_close();
@@ -992,8 +1127,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 04bcb18..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"
@@ -605,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/service.cpp b/init/service.cpp
index e186f27..ba901fd 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -180,12 +180,6 @@
     }
 
     std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
-    if (prop_name.length() >= PROP_NAME_MAX) {
-        // If the property name would be too long, we can't set it.
-        LOG(ERROR) << "Property name \"init.svc." << name_ << "\" too long; not setting to " << new_state;
-        return;
-    }
-
     property_set(prop_name.c_str(), new_state.c_str());
 
     if (new_state == "running") {
@@ -1040,5 +1034,9 @@
 }
 
 bool ServiceParser::IsValidName(const std::string& name) const {
-    return is_legal_property_name("init.svc." + name);
+    // Property names can be any length, but may only contain certain characters.
+    // Property values can contain any characters, but may only be a certain length.
+    // (The latter restriction is needed because `start` and `stop` work by writing
+    // the service name to the "ctl.start" and "ctl.stop" properties.)
+    return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
 }
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 394a897..c00f175 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -149,10 +149,6 @@
                                               "system/bin/run-as" },
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
-    { 00750, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
-                                           CAP_MASK_LONG(CAP_SETGID) |
-                                           CAP_MASK_LONG(CAP_SYS_PTRACE),
-                                              "system/bin/storaged" },
 
     /* Support FIFO scheduling mode in SurfaceFlinger. */
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
@@ -170,7 +166,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) |
@@ -185,6 +181,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/libsync/Android.bp b/libsync/Android.bp
index a4e5599..b293da4 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -1,3 +1,17 @@
+ndk_headers {
+    name: "libsync_headers",
+    from: "include/ndk",
+    to: "android",
+    srcs: ["include/ndk/sync.h"],
+    license: "NOTICE",
+}
+
+ndk_library {
+    name: "libsync.ndk",
+    symbol_file: "libsync.map.txt",
+    first_version: "26",
+}
+
 cc_defaults {
     name: "libsync_defaults",
     srcs: ["sync.c"],
diff --git a/libsync/NOTICE b/libsync/NOTICE
new file mode 100644
index 0000000..2c8db73
--- /dev/null
+++ b/libsync/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2012-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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libsync/include/android/sync.h b/libsync/include/android/sync.h
new file mode 100644
index 0000000..68f74a0
--- /dev/null
+++ b/libsync/include/android/sync.h
@@ -0,0 +1,69 @@
+/*
+ *  sync.h
+ *
+ *   Copyright 2012 Google, Inc
+ *
+ *  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 __SYS_CORE_SYNC_H
+#define __SYS_CORE_SYNC_H
+
+/* This file contains the legacy sync interface used by Android platform and
+ * device code. The direct contents will be removed over time as code
+ * transitions to using the updated interface in ndk/sync.h. When this file is
+ * empty other than the ndk/sync.h include, that file will be renamed to
+ * replace this one.
+ *
+ * New code should continue to include this file (#include <android/sync.h>)
+ * instead of ndk/sync.h so the eventual rename is seamless, but should only
+ * use the things declared in ndk/sync.h.
+ *
+ * This file used to be called sync/sync.h, but we renamed to that both the
+ * platform and NDK call it android/sync.h. A symlink from the old name to this
+ * one exists temporarily to avoid having to change all sync clients
+ * simultaneously. It will be removed when they've been updated, and probably
+ * after this change has been delivered to AOSP so that integrations don't
+ * break builds.
+ */
+
+#include "../ndk/sync.h"
+
+__BEGIN_DECLS
+
+struct sync_fence_info_data {
+ uint32_t len;
+ char name[32];
+ int32_t status;
+ uint8_t pt_info[0];
+};
+
+struct sync_pt_info {
+ uint32_t len;
+ char obj_name[32];
+ char driver_name[32];
+ int32_t status;
+ uint64_t timestamp_ns;
+ uint8_t driver_data[0];
+};
+
+/* timeout in msecs */
+int sync_wait(int fd, int timeout);
+struct sync_fence_info_data *sync_fence_info(int fd);
+struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
+                                  struct sync_pt_info *itr);
+void sync_fence_info_free(struct sync_fence_info_data *info);
+
+__END_DECLS
+
+#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/ndk/sync.h b/libsync/include/ndk/sync.h
new file mode 100644
index 0000000..758a106
--- /dev/null
+++ b/libsync/include/ndk/sync.h
@@ -0,0 +1,89 @@
+/*
+ *  Copyright 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_SYNC_H
+#define ANDROID_SYNC_H
+
+#include <stdint.h>
+
+#include <linux/sync_file.h>
+
+__BEGIN_DECLS
+
+#if __ANDROID_API__ >= __ANDROID_API_O__
+
+/* Fences indicate the status of an asynchronous task. They are initially
+ * in unsignaled state (0), and make a one-time transition to either signaled
+ * (1) or error (< 0) state. A sync file is a collection of one or more fences;
+ * the sync file's status is error if any of its fences are in error state,
+ * signaled if all of the child fences are signaled, or unsignaled otherwise.
+ *
+ * Sync files are created by various device APIs in response to submitting
+ * tasks to the device. Standard file descriptor lifetime syscalls like dup()
+ * and close() are used to manage sync file lifetime.
+ *
+ * The poll(), ppoll(), or select() syscalls can be used to wait for the sync
+ * file to change status, or (with a timeout of zero) to check its status.
+ *
+ * The functions below provide a few additional sync-specific operations.
+ */
+
+/**
+ * Merge two sync files.
+ *
+ * This produces a new sync file with the given name which has the union of the
+ * two original sync file's fences; redundant fences may be removed.
+ *
+ * If one of the input sync files is signaled or invalid, then this function
+ * may behave like dup(): the new file descriptor refers to the valid/unsignaled
+ * sync file with its original name, rather than a new sync file.
+ *
+ * The original fences remain valid, and the caller is responsible for closing
+ * them.
+ */
+int32_t sync_merge(const char *name, int32_t fd1, int32_t fd2);
+
+/**
+ * Retrieve detailed information about a sync file and its fences.
+ *
+ * The returned sync_file_info must be freed by calling sync_file_info_free().
+ */
+struct sync_file_info *sync_file_info(int32_t fd);
+
+/**
+ * Get the array of fence infos from the sync file's info.
+ *
+ * The returned array is owned by the parent sync file info, and has
+ * info->num_fences entries.
+ */
+inline struct sync_fence_info *sync_get_fence_info(
+        const struct sync_file_info *info) {
+// This header should compile in C, but some C++ projects enable
+// warnings-as-error for C-style casts.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+    return (struct sync_fence_info *)(uintptr_t)(info->sync_fence_info);
+#pragma GCC diagnostic pop
+}
+
+/** Free a struct sync_file_info structure */
+void sync_file_info_free(struct sync_file_info *info);
+
+#endif // __ANDROID_API__ >= __ANDROID_API_O__
+
+__END_DECLS
+
+#endif /* ANDROID_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
deleted file mode 100644
index 50ed0ac..0000000
--- a/libsync/include/sync/sync.h
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- *  sync.h
- *
- *   Copyright 2012 Google, Inc
- *
- *  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 __SYS_CORE_SYNC_H
-#define __SYS_CORE_SYNC_H
-
-#include <sys/cdefs.h>
-#include <stdint.h>
-
-#include <linux/types.h>
-
-__BEGIN_DECLS
-
-struct sync_legacy_merge_data {
- int32_t fd2;
- char name[32];
- int32_t fence;
-};
-
-struct sync_fence_info_data {
- uint32_t len;
- char name[32];
- int32_t status;
- uint8_t pt_info[0];
-};
-
-struct sync_pt_info {
- uint32_t len;
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint64_t timestamp_ns;
- uint8_t driver_data[0];
-};
-
-#define SYNC_IOC_MAGIC		'>'
-
-/**
- * DOC: SYNC_IOC_LEGACY_WAIT - wait for a fence to signal
- *
- * pass timeout in milliseconds.  Waits indefinitely timeout < 0.
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_WAIT	_IOW(SYNC_IOC_MAGIC, 0, __s32)
-
-/**
- * DOC: SYNC_IOC_MERGE - merge two fences
- *
- * Takes a struct sync_merge_data.  Creates a new fence containing copies of
- * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
- * new fence's fd in sync_merge_data.fence
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_MERGE	_IOWR(SYNC_IOC_MAGIC, 1, \
-	struct sync_legacy_merge_data)
-
-/**
- * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
- *
- * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
- * Caller should write the size of the buffer into len.  On return, len is
- * updated to reflect the total size of the sync_fence_info_data including
- * pt_info.
- *
- * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
- * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
- *
- * This is the legacy version of the Sync API before the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_LEGACY_FENCE_INFO	_IOWR(SYNC_IOC_MAGIC, 2,\
-	struct sync_fence_info_data)
-
-struct sync_merge_data {
- char name[32];
- int32_t fd2;
- int32_t fence;
- uint32_t flags;
- uint32_t pad;
-};
-
-struct sync_file_info {
- char name[32];
- int32_t status;
- uint32_t flags;
- uint32_t num_fences;
- uint32_t pad;
-
- uint64_t sync_fence_info;
-};
-
-struct sync_fence_info {
- char obj_name[32];
- char driver_name[32];
- int32_t status;
- uint32_t flags;
- uint64_t timestamp_ns;
-};
-
-/**
- * Mainline API:
- *
- * Opcodes  0, 1 and 2 were burned during a API change to avoid users of the
- * old API to get weird errors when trying to handling sync_files. The API
- * change happened during the de-stage of the Sync Framework when there was
- * no upstream users available.
- */
-
-/**
- * DOC: SYNC_IOC_MERGE - merge two fences
- *
- * Takes a struct sync_merge_data.  Creates a new fence containing copies of
- * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
- * new fence's fd in sync_merge_data.fence
- *
- * This is the new version of the Sync API after the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_MERGE		_IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
-
-/**
- * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
- *
- * Takes a struct sync_file_info. If num_fences is 0, the field is updated
- * with the actual number of fences. If num_fences is > 0, the system will
- * use the pointer provided on sync_fence_info to return up to num_fences of
- * struct sync_fence_info, with detailed fence information.
- *
- * This is the new version of the Sync API after the de-stage that happened
- * on Linux kernel 4.7.
- */
-#define SYNC_IOC_FILE_INFO	_IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
-
-/* timeout in msecs */
-int sync_wait(int fd, int timeout);
-int sync_merge(const char *name, int fd1, int fd2);
-struct sync_fence_info_data *sync_fence_info(int fd);
-struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
-                                  struct sync_pt_info *itr);
-void sync_fence_info_free(struct sync_fence_info_data *info);
-
-__END_DECLS
-
-#endif /* __SYS_CORE_SYNC_H */
diff --git a/libsync/include/sync/sync.h b/libsync/include/sync/sync.h
new file mode 120000
index 0000000..3b17e48
--- /dev/null
+++ b/libsync/include/sync/sync.h
@@ -0,0 +1 @@
+../android/sync.h
\ No newline at end of file
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
new file mode 100644
index 0000000..daa28ae
--- /dev/null
+++ b/libsync/libsync.map.txt
@@ -0,0 +1,32 @@
+#
+# Copyright 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.
+#
+
+LIBSYNC {
+  global:
+    sync_merge; # introduced=26
+    sync_get_fence_info; # introduced=26
+    sync_free_fence_info; # introduced=26
+  local:
+    *;
+};
+
+LIBSYNC_PLATFORM {
+  global:
+    sync_wait;
+    sync_fence_info;
+    sync_pt_info;
+    sync_fence_info_free;
+} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c
index 9ed03db..27dab83 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -27,8 +27,47 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <sync/sync.h>
+#include <android/sync.h>
 
+/* Legacy Sync API */
+
+struct sync_legacy_merge_data {
+ int32_t fd2;
+ char name[32];
+ int32_t fence;
+};
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data.  Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the
+ * new fence's fd in sync_merge_data.fence
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_MERGE   _IOWR(SYNC_IOC_MAGIC, 1, \
+    struct sync_legacy_merge_data)
+
+/**
+ * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len.  On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ *
+ * This is the legacy version of the Sync API before the de-stage that happened
+ * on Linux kernel 4.7.
+ */
+#define SYNC_IOC_LEGACY_FENCE_INFO  _IOWR(SYNC_IOC_MAGIC, 2,\
+    struct sync_fence_info_data)
+
+/* SW Sync API */
 
 struct sw_sync_create_fence_data {
   __u32 value;
@@ -98,13 +137,11 @@
     return data.fence;
 }
 
-struct sync_fence_info_data *sync_fence_info(int fd)
+static struct sync_fence_info_data *legacy_sync_fence_info(int fd)
 {
     struct sync_fence_info_data *legacy_info;
     struct sync_pt_info *legacy_pt_info;
-    struct sync_file_info *info;
-    struct sync_fence_info *fence_info;
-    int err, num_fences, i;
+    int err;
 
     legacy_info = malloc(4096);
     if (legacy_info == NULL)
@@ -112,46 +149,57 @@
 
     legacy_info->len = 4096;
     err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);
-    if (err < 0 && errno != ENOTTY) {
+    if (err < 0) {
         free(legacy_info);
         return NULL;
-    } else if (err == 0) {
-        return legacy_info;
     }
+    return legacy_info;
+}
 
-    info = calloc(1, sizeof(*info));
-    if (info == NULL)
-        goto free;
+static struct sync_file_info *modern_sync_file_info(int fd)
+{
+    struct sync_file_info local_info;
+    struct sync_file_info *info;
+    int err;
+
+    memset(&local_info, 0, sizeof(local_info));
+    err = ioctl(fd, SYNC_IOC_FILE_INFO, &local_info);
+    if (err < 0)
+        return NULL;
+
+    info = calloc(1, sizeof(struct sync_file_info) +
+                  local_info.num_fences * sizeof(struct sync_fence_info));
+    if (!info)
+        return NULL;
+    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
 
     err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
-    if (err < 0)
-        goto free;
-
-    num_fences = info->num_fences;
-
-    if (num_fences) {
-        info->flags = 0;
-        info->num_fences = num_fences;
-        info->sync_fence_info = (uint64_t) calloc(num_fences,
-                                        sizeof(struct sync_fence_info));
-        if ((void *)info->sync_fence_info == NULL)
-            goto free;
-
-        err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
-        if (err < 0) {
-            free((void *)info->sync_fence_info);
-            goto free;
-        }
+    if (err < 0) {
+        free(info);
+        return NULL;
     }
 
+    return info;
+}
+
+static struct sync_fence_info_data *sync_file_info_to_legacy_fence_info(
+    const struct sync_file_info *info)
+{
+    struct sync_fence_info_data *legacy_info;
+    struct sync_pt_info *legacy_pt_info;
+    const struct sync_fence_info *fence_info = sync_get_fence_info(info);
+    const uint32_t num_fences = info->num_fences;
+
+    legacy_info = malloc(4096);
+    if (legacy_info == NULL)
+        return NULL;
     legacy_info->len = sizeof(*legacy_info) +
-                        num_fences * sizeof(struct sync_fence_info);
+                        num_fences * sizeof(struct sync_pt_info);
     strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));
     legacy_info->status = info->status;
 
     legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;
-    fence_info = (struct sync_fence_info *)info->sync_fence_info;
-    for (i = 0 ; i < num_fences ; i++) {
+    for (uint32_t i = 0; i < num_fences; i++) {
         legacy_pt_info[i].len = sizeof(*legacy_pt_info);
         strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,
                 sizeof(legacy_pt_info->obj_name));
@@ -161,14 +209,81 @@
         legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;
     }
 
-    free((void *)info->sync_fence_info);
-    free(info);
     return legacy_info;
+}
 
-free:
-    free(legacy_info);
-    free(info);
-    return NULL;
+static struct sync_file_info* legacy_fence_info_to_sync_file_info(
+                                    struct sync_fence_info_data *legacy_info)
+{
+    struct sync_file_info *info;
+    struct sync_pt_info *pt;
+    struct sync_fence_info *fence;
+    size_t num_fences;
+    int err;
+
+    pt = NULL;
+    num_fences = 0;
+    while ((pt = sync_pt_info(legacy_info, pt)) != NULL)
+        num_fences++;
+
+    info = calloc(1, sizeof(struct sync_file_info) +
+                     num_fences * sizeof(struct sync_fence_info));
+    if (!info) {
+        free(legacy_info);
+        return NULL;
+    }
+    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
+
+    strlcpy(info->name, legacy_info->name, sizeof(info->name));
+    info->status = legacy_info->status;
+    info->num_fences = num_fences;
+
+    pt = NULL;
+    fence = sync_get_fence_info(info);
+    while ((pt = sync_pt_info(legacy_info, pt)) != NULL) {
+        strlcpy(fence->obj_name, pt->obj_name, sizeof(fence->obj_name));
+        strlcpy(fence->driver_name, pt->driver_name,
+                sizeof(fence->driver_name));
+        fence->status = pt->status;
+        fence->timestamp_ns = pt->timestamp_ns;
+        fence++;
+    }
+
+    return info;
+}
+
+struct sync_fence_info_data *sync_fence_info(int fd)
+{
+    struct sync_fence_info_data *legacy_info;
+
+    legacy_info = legacy_sync_fence_info(fd);
+    if (legacy_info || errno != ENOTTY)
+        return legacy_info;
+
+    struct sync_file_info* file_info;
+    file_info = modern_sync_file_info(fd);
+    if (!file_info)
+        return NULL;
+    legacy_info = sync_file_info_to_legacy_fence_info(file_info);
+    sync_file_info_free(file_info);
+    return legacy_info;
+}
+
+struct sync_file_info* sync_file_info(int32_t fd)
+{
+    struct sync_file_info *info;
+    struct sync_fence_info_data *legacy_info;
+
+    info = modern_sync_file_info(fd);
+    if (info || errno != ENOTTY)
+        return info;
+
+    legacy_info = legacy_sync_fence_info(fd);
+    if (!legacy_info)
+        return NULL;
+    info = legacy_fence_info_to_sync_file_info(legacy_info);
+    sync_fence_info_free(legacy_info);
+    return info;
 }
 
 struct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,
@@ -190,6 +305,11 @@
     free(info);
 }
 
+void sync_file_info_free(struct sync_file_info *info)
+{
+    free(info);
+}
+
 
 int sw_sync_timeline_create(void)
 {
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index 9a5f7d8..f1ffdcf 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -22,7 +22,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <sync/sync.h>
+#include <android/sync.h>
 #include "sw_sync.h"
 
 pthread_mutex_t printf_mutex = PTHREAD_MUTEX_INITIALIZER;
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 401aaee..f08e97e 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -1,5 +1,5 @@
 #include <gtest/gtest.h>
-#include <sync/sync.h>
+#include <android/sync.h>
 #include <sw_sync.h>
 #include <fcntl.h>
 #include <vector>
@@ -172,20 +172,20 @@
         return sync_wait(m_fd, timeout);
     }
     vector<SyncPointInfo> getInfo() const {
-        struct sync_pt_info *pointInfo = nullptr;
         vector<SyncPointInfo> fenceInfo;
-        sync_fence_info_data *info = sync_fence_info(getFd());
+        struct sync_file_info *info = sync_file_info(getFd());
         if (!info) {
             return fenceInfo;
         }
-        while ((pointInfo = sync_pt_info(info, pointInfo))) {
+        const auto fences = sync_get_fence_info(info);
+        for (uint32_t i = 0; i < info->num_fences; i++) {
             fenceInfo.push_back(SyncPointInfo{
-                pointInfo->driver_name,
-                pointInfo->obj_name,
-                pointInfo->timestamp_ns,
-                pointInfo->status});
+                fences[i].driver_name,
+                fences[i].obj_name,
+                fences[i].timestamp_ns,
+                fences[i].status});
         }
-        sync_fence_info_free(info);
+        sync_file_info_free(info);
         return fenceInfo;
     }
     int getSize() const {
@@ -212,6 +212,32 @@
     }
 };
 
+static void CheckModernLegacyInfoMatch(const SyncFence& f) {
+    struct sync_file_info* modern = sync_file_info(f.getFd());
+    struct sync_fence_info_data* legacy = sync_fence_info(f.getFd());
+
+    ASSERT_TRUE(modern != NULL);
+    ASSERT_TRUE(legacy != NULL);
+
+    EXPECT_STREQ(modern->name, legacy->name);
+    EXPECT_EQ(modern->status, legacy->status);
+
+    uint32_t fenceIdx = 0;
+    struct sync_pt_info* pt = sync_pt_info(legacy, NULL);
+    const struct sync_fence_info* fences = sync_get_fence_info(modern);
+    while (fenceIdx < modern->num_fences && pt != NULL) {
+        EXPECT_STREQ(fences[fenceIdx].obj_name, pt->obj_name);
+        EXPECT_STREQ(fences[fenceIdx].driver_name, pt->driver_name);
+        EXPECT_EQ(fences[fenceIdx].status, pt->status);
+        EXPECT_EQ(fences[fenceIdx].timestamp_ns, pt->timestamp_ns);
+
+        fenceIdx++;
+        pt = sync_pt_info(legacy, pt);
+    }
+    EXPECT_EQ(fenceIdx, modern->num_fences);
+    EXPECT_EQ(NULL, pt);
+}
+
 int SyncFence::s_fenceCount = 0;
 
 TEST(AllocTest, Timeline) {
@@ -225,6 +251,7 @@
 
     SyncFence fence(timeline, 1);
     ASSERT_TRUE(fence.isValid());
+    CheckModernLegacyInfoMatch(fence);
 }
 
 TEST(AllocTest, FenceNegative) {
@@ -321,15 +348,21 @@
     timeline.inc(1);
     ASSERT_EQ(a.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 1);
+    CheckModernLegacyInfoMatch(a);
+    CheckModernLegacyInfoMatch(d);
 
     timeline.inc(1);
     ASSERT_EQ(b.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 1);
+    CheckModernLegacyInfoMatch(b);
+    CheckModernLegacyInfoMatch(d);
 
     timeline.inc(1);
     ASSERT_EQ(c.getSignaledCount(), 1);
     ASSERT_EQ(d.getActiveCount(), 0);
     ASSERT_EQ(d.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(c);
+    CheckModernLegacyInfoMatch(d);
 }
 
 TEST(FenceTest, MergeSameFence) {
@@ -343,9 +376,11 @@
     ASSERT_TRUE(selfMergeFence.isValid());
 
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 0);
+    CheckModernLegacyInfoMatch(selfMergeFence);
 
     timeline.inc(5);
     ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(selfMergeFence);
 }
 
 TEST(FenceTest, PollOnDestroyedTimeline) {
@@ -397,14 +432,17 @@
     timelineA.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 2);
     ASSERT_EQ(mergedFence.getSignaledCount(), 1);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     timelineB.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 1);
     ASSERT_EQ(mergedFence.getSignaledCount(), 2);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     timelineC.inc(5);
     ASSERT_EQ(mergedFence.getActiveCount(), 0);
     ASSERT_EQ(mergedFence.getSignaledCount(), 3);
+    CheckModernLegacyInfoMatch(mergedFence);
 
     // confirm you can successfully wait.
     ASSERT_EQ(mergedFence.wait(100), 0);
@@ -560,6 +598,7 @@
         // Merge.
         fence = SyncFence(fence, SyncFence(timeline, syncPoint));
         ASSERT_TRUE(fence.isValid());
+        CheckModernLegacyInfoMatch(fence);
     }
 
     // Confirm our map matches the fence.
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
index 330d6cb..584e5a2 100644
--- a/libsysutils/Android.mk
+++ b/libsysutils/Android.mk
@@ -17,6 +17,7 @@
 LOCAL_CFLAGS := -Werror
 
 LOCAL_SHARED_LIBRARIES := \
+        libbase \
         libcutils \
         liblog \
         libnl
@@ -24,4 +25,3 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
 
 include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
index 13bac09..c7aa1f7 100644
--- a/libsysutils/src/ServiceManager.cpp
+++ b/libsysutils/src/ServiceManager.cpp
@@ -19,34 +19,23 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/system_properties.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <log/log.h>
 #include <sysutils/ServiceManager.h>
 
 ServiceManager::ServiceManager() {
 }
 
-/* The service name should not exceed SERVICE_NAME_MAX to avoid
- * some weird things. This is due to the fact that:
- *
- * - Starting a service is done by writing its name to the "ctl.start"
- *   system property. This triggers the init daemon to actually start
- *   the service for us.
- *
- * - Stopping the service is done by writing its name to "ctl.stop"
- *   in a similar way.
- *
- * - Reading the status of a service is done by reading the property
- *   named "init.svc.<name>"
- *
- * If strlen(<name>) > (PROPERTY_KEY_MAX-1)-9, then you can start/stop
- * the service by writing to ctl.start/stop, but you won't be able to
- * read its state due to the truncation of "init.svc.<name>" into a
- * zero-terminated buffer of PROPERTY_KEY_MAX characters.
- */
-#define SERVICE_NAME_MAX  (PROPERTY_KEY_MAX-10)
+// The length of a service name should not exceed SERVICE_NAME_MAX. Starting
+// a service is done by writing its name to the "ctl.start" system property
+// and stopping a service is done by writing its name to "ctl.stop". If a
+// service name is too long to fit in a property, you won't be able to start
+// or stop it.
+static constexpr size_t SERVICE_NAME_MAX = PROP_VALUE_MAX;
 
 /* The maximum amount of time to wait for a service to start or stop,
  * in micro-seconds (really an approximation) */
@@ -61,13 +50,14 @@
         SLOGE("Service name '%s' is too long", name);
         return 0;
     }
+
     if (isRunning(name)) {
         SLOGW("Service '%s' is already running", name);
         return 0;
     }
 
     SLOGD("Starting service '%s'", name);
-    property_set("ctl.start", name);
+    android::base::SetProperty("ctl.start", name);
 
     int count = SLEEP_MAX_USEC;
     while(count > 0) {
@@ -90,13 +80,14 @@
         SLOGE("Service name '%s' is too long", name);
         return 0;
     }
+
     if (!isRunning(name)) {
         SLOGW("Service '%s' is already stopped", name);
         return 0;
     }
 
     SLOGD("Stopping service '%s'", name);
-    property_set("ctl.stop", name);
+    android::base::SetProperty("ctl.stop", name);
 
     int count = SLEEP_MAX_USEC;
     while(count > 0) {
@@ -116,19 +107,6 @@
 }
 
 bool ServiceManager::isRunning(const char *name) {
-    char propVal[PROPERTY_VALUE_MAX];
-    char propName[PROPERTY_KEY_MAX];
-    int  ret;
-
-    ret = snprintf(propName, sizeof(propName), "init.svc.%s", name);
-    if (ret > (int)sizeof(propName)-1) {
-        SLOGD("Service name '%s' is too long", name);
-        return false;
-    }
-
-    if (property_get(propName, propVal, NULL)) {
-        if (!strcmp(propVal, "running"))
-            return true;
-    }
-    return false;
+    std::string property_name = android::base::StringPrintf("init.svc.%s", name);
+    return (android::base::GetProperty(property_name, "") == "running");
 }
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/logcat/logcat.cpp b/logcat/logcat.cpp
index 7f852d4..077332a 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
@@ -108,7 +108,7 @@
 
     context = (android_logcat_context_internal*)calloc(
         1, sizeof(android_logcat_context_internal));
-    if (!context) return NULL;
+    if (!context) return nullptr;
 
     context->fds[0] = -1;
     context->fds[1] = -1;
@@ -139,10 +139,10 @@
     log_device_t(const char* d, bool b) {
         device = d;
         binary = b;
-        next = NULL;
+        next = nullptr;
         printed = false;
-        logger = NULL;
-        logger_list = NULL;
+        logger = nullptr;
+        logger_list = nullptr;
     }
 };
 
@@ -162,7 +162,7 @@
 static void close_output(android_logcat_context_internal* context) {
     // split output_from_error
     if (context->error == context->output) {
-        context->output = NULL;
+        context->output = nullptr;
         context->output_fd = -1;
     }
     if (context->error && (context->output_fd == fileno(context->error))) {
@@ -182,7 +182,7 @@
             }
             fclose(context->output);
         }
-        context->output = NULL;
+        context->output = nullptr;
     }
     if (context->output_fd >= 0) {
         if (context->output_fd != fileno(stdout)) {
@@ -198,7 +198,7 @@
 static void close_error(android_logcat_context_internal* context) {
     // split error_from_output
     if (context->output == context->error) {
-        context->error = NULL;
+        context->error = nullptr;
         context->error_fd = -1;
     }
     if (context->output && (context->error_fd == fileno(context->output))) {
@@ -218,14 +218,12 @@
             }
             fclose(context->error);
         }
-        context->error = NULL;
+        context->error = nullptr;
     }
     if (context->error_fd >= 0) {
         if ((context->error_fd != fileno(stdout)) &&
             (context->error_fd != fileno(stderr))) {
-            if (context->fds[1] == context->error_fd) {
-                context->fds[1] = -1;
-            }
+            if (context->fds[1] == context->error_fd) context->fds[1] = -1;
             close(context->error_fd);
         }
         context->error_fd = -1;
@@ -236,9 +234,7 @@
     int err;
 
     // Can't rotate logs if we're not outputting to a file
-    if (context->outputFileName == NULL) {
-        return;
-    }
+    if (!context->outputFileName) return;
 
     close_output(context);
 
@@ -257,7 +253,7 @@
             "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
 
         std::string file0;
-        if (i - 1 == 0) {
+        if (!(i - 1)) {
             file0 = android::base::StringPrintf("%s", context->outputFileName);
         } else {
             file0 =
@@ -265,7 +261,7 @@
                                             maxRotationCountDigits, i - 1);
         }
 
-        if ((file0.length() == 0) || (file1.length() == 0)) {
+        if (!file0.length() || !file1.length()) {
             perror("while rotating log files");
             break;
         }
@@ -284,6 +280,15 @@
         return;
     }
     context->output = fdopen(context->output_fd, "web");
+    if (!context->output) {
+        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;
 }
@@ -296,9 +301,7 @@
 
 static bool regexOk(android_logcat_context_internal* context,
                     const AndroidLogEntry& entry) {
-    if (!context->regex) {
-        return true;
-    }
+    if (!context->regex) return true;
 
     std::string messageString(entry.message, entry.messageLen);
 
@@ -314,7 +317,7 @@
 
     if (dev->binary) {
         if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
-            context->eventTagMap = android_openEventTagMap(NULL);
+            context->eventTagMap = android_openEventTagMap(nullptr);
             context->hasOpenedEventTagMap = true;
         }
         err = android_log_processBinaryLogBuffer(
@@ -325,9 +328,7 @@
     } else {
         err = android_log_processLogBuffer(&buf->entry_v1, &entry);
     }
-    if ((err < 0) && !context->debug) {
-        return;
-    }
+    if ((err < 0) && !context->debug) return;
 
     if (android_log_shouldPrintLine(
             context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
@@ -372,7 +373,7 @@
 
 static void setupOutputAndSchedulingPolicy(
     android_logcat_context_internal* context, bool blocking) {
-    if (context->outputFileName == NULL) return;
+    if (!context->outputFileName) return;
 
     if (blocking) {
         // Lower priority and set to batch scheduling if we are saving
@@ -445,6 +446,8 @@
                     "                  and individually flagged modifying adverbs can be added:\n"
                     "                    color descriptive epoch monotonic printable uid\n"
                     "                    usec UTC year zone\n"
+                    "                  Multiple -v parameters or comma separated list of format and\n"
+                    "                  format modifiers are allowed.\n"
                     // private and undocumented nsec, no signal, too much noise
                     // useful for -T or -t <timestamp> accurate testing though.
                     "  -D, --dividers  Print dividers between each log buffer\n"
@@ -553,10 +556,8 @@
 
     format = android_log_formatFromString(formatString);
 
-    if (format == FORMAT_OFF) {
-        // FORMAT_OFF means invalid string
-        return -1;
-    }
+    // invalid string?
+    if (format == FORMAT_OFF) return -1;
 
     return android_log_setPrintFormat(context->logformat, format);
 }
@@ -583,21 +584,15 @@
 // String to unsigned int, returns -1 if it fails
 static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
                         size_t max = SIZE_MAX) {
-    if (!ptr) {
-        return false;
-    }
+    if (!ptr) return false;
 
     char* endp;
     errno = 0;
     size_t ret = (size_t)strtoll(ptr, &endp, 0);
 
-    if (endp[0] || errno) {
-        return false;
-    }
+    if (endp[0] || errno) return false;
 
-    if ((ret > max) || (ret < min)) {
-        return false;
-    }
+    if ((ret > max) || (ret < min)) return false;
 
     *val = ret;
     return true;
@@ -633,22 +628,16 @@
 
 static char* parseTime(log_time& t, const char* cp) {
     char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
-    if (ep) {
-        return ep;
-    }
+    if (ep) return ep;
     ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
-    if (ep) {
-        return ep;
-    }
+    if (ep) return ep;
     return t.strptime(cp, "%s.%q");
 }
 
 // Find last logged line in <outputFileName>, or <outputFileName>.1
 static log_time lastLogTime(char* outputFileName) {
     log_time retval(log_time::EPOCH);
-    if (!outputFileName) {
-        return retval;
-    }
+    if (!outputFileName) return retval;
 
     std::string directory;
     char* file = strrchr(outputFileName, '/');
@@ -664,9 +653,7 @@
 
     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
                                             closedir);
-    if (!dir.get()) {
-        return retval;
-    }
+    if (!dir.get()) return retval;
 
     log_time now(android_log_clockid());
 
@@ -674,10 +661,10 @@
     log_time modulo(0, NS_PER_SEC);
     struct dirent* dp;
 
-    while ((dp = readdir(dir.get())) != NULL) {
-        if ((dp->d_type != DT_REG) || (strncmp(dp->d_name, file, len) != 0) ||
+    while (!!(dp = readdir(dir.get()))) {
+        if ((dp->d_type != DT_REG) || !!strncmp(dp->d_name, file, len) ||
             (dp->d_name[len] && ((dp->d_name[len] != '.') ||
-                                 (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
+                                 (strtoll(dp->d_name + 1, nullptr, 10) != 1)))) {
             continue;
         }
 
@@ -685,17 +672,13 @@
         file_name += "/";
         file_name += dp->d_name;
         std::string file;
-        if (!android::base::ReadFileToString(file_name, &file)) {
-            continue;
-        }
+        if (!android::base::ReadFileToString(file_name, &file)) continue;
 
         bool found = false;
         for (const auto& line : android::base::Split(file, "\n")) {
             log_time t(log_time::EPOCH);
             char* ep = parseTime(t, line.c_str());
-            if (!ep || (*ep != ' ')) {
-                continue;
-            }
+            if (!ep || (*ep != ' ')) continue;
             // determine the time precision of the logs (eg: msec or usec)
             for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
                 if (t.tv_nsec % (mod * 10)) {
@@ -713,13 +696,9 @@
             }
         }
         // We count on the basename file to be the definitive end, so stop here.
-        if (!dp->d_name[len] && found) {
-            break;
-        }
+        if (!dp->d_name[len] && found) break;
     }
-    if (retval == log_time::EPOCH) {
-        return retval;
-    }
+    if (retval == log_time::EPOCH) return retval;
     // tail_time prints matching or higher, round up by the modulo to prevent
     // a replay of the last entry we have just checked.
     retval += modulo;
@@ -727,32 +706,29 @@
 }
 
 const char* getenv(android_logcat_context_internal* context, const char* name) {
-    if (!context->envp || !name || !*name) return NULL;
+    if (!context->envp || !name || !*name) return nullptr;
 
     for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
         if (strncmp(context->envp[i], name, len)) continue;
         if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
     }
-    return NULL;
+    return nullptr;
 }
 
 }  // namespace android
 
 void reportErrorName(const char** current, const char* name,
                      bool blockSecurity) {
-    if (*current) {
-        return;
+    if (*current) return;
+    if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
+        *current = name;
     }
-    if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
-        return;
-    }
-    *current = name;
 }
 
 static int __logcat(android_logcat_context_internal* context) {
     using namespace android;
     int err;
-    int hasSetLogFormat = 0;
+    bool hasSetLogFormat = false;
     bool clearLog = false;
     bool allSelected = false;
     bool getLogSize = false;
@@ -760,11 +736,11 @@
     bool printStatistics = false;
     bool printDividers = false;
     unsigned long setLogSize = 0;
-    char* setPruneList = NULL;
-    char* setId = NULL;
+    char* setPruneList = nullptr;
+    char* setId = nullptr;
     int mode = ANDROID_LOG_RDONLY;
     std::string forceFilters;
-    log_device_t* devices = NULL;
+    log_device_t* devices = nullptr;
     log_device_t* dev;
     struct logger_list* logger_list;
     size_t tail_lines = 0;
@@ -774,10 +750,10 @@
 
     // object instantiations before goto's can happen
     log_device_t unexpected("unexpected", false);
-    const char* openDeviceFail = NULL;
-    const char* clearFail = NULL;
-    const char* setSizeFail = NULL;
-    const char* getSizeFail = NULL;
+    const char* openDeviceFail = nullptr;
+    const char* clearFail = nullptr;
+    const char* setSizeFail = nullptr;
+    const char* getSizeFail = nullptr;
     int argc = context->argc;
     char* const* argv = context->argv;
 
@@ -788,6 +764,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 +776,29 @@
                     "stderr redirection to file %s unsupported, skipping\n",
                     &argv[i][skip]);
         }
+        // Only the first one
+        break;
+    }
+
+    const char* filename = nullptr;
+    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 +809,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 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;
+        context->error = nullptr;
     }
-    // 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);
     }
@@ -845,12 +845,16 @@
 
     context->logformat = android_log_format_new();
 
-    if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+    if (argc == 2 && !strcmp(argv[1], "--help")) {
         show_help(context);
         context->retval = EXIT_SUCCESS;
         goto exit;
     }
 
+    // meant to catch comma-delimited values, but cast a wider
+    // net for stability dealing with possible mistaken inputs.
+    static const char delimiters[] = ",:; \t\n\r\f";
+
     // danger: getopt is _not_ reentrant
     optind = 1;
     for (;;) {
@@ -865,43 +869,40 @@
         static const char print_str[] = "print";
         // clang-format off
         static const struct option long_options[] = {
-          { "binary",        no_argument,       NULL,   'B' },
-          { "buffer",        required_argument, NULL,   'b' },
-          { "buffer-size",   optional_argument, NULL,   'g' },
-          { "clear",         no_argument,       NULL,   'c' },
-          { debug_str,       no_argument,       NULL,   0 },
-          { "dividers",      no_argument,       NULL,   'D' },
-          { "file",          required_argument, NULL,   'f' },
-          { "format",        required_argument, NULL,   'v' },
+          { "binary",        no_argument,       nullptr, 'B' },
+          { "buffer",        required_argument, nullptr, 'b' },
+          { "buffer-size",   optional_argument, nullptr, 'g' },
+          { "clear",         no_argument,       nullptr, 'c' },
+          { debug_str,       no_argument,       nullptr, 0 },
+          { "dividers",      no_argument,       nullptr, 'D' },
+          { "file",          required_argument, nullptr, 'f' },
+          { "format",        required_argument, nullptr, 'v' },
           // hidden and undocumented reserved alias for --regex
-          { "grep",          required_argument, NULL,   'e' },
+          { "grep",          required_argument, nullptr, 'e' },
           // hidden and undocumented reserved alias for --max-count
-          { "head",          required_argument, NULL,   'm' },
-          { id_str,          required_argument, NULL,   0 },
-          { "last",          no_argument,       NULL,   'L' },
-          { "max-count",     required_argument, NULL,   'm' },
-          { pid_str,         required_argument, NULL,   0 },
-          { print_str,       no_argument,       NULL,   0 },
-          { "prune",         optional_argument, NULL,   'p' },
-          { "regex",         required_argument, NULL,   'e' },
-          { "rotate-count",  required_argument, NULL,   'n' },
-          { "rotate-kbytes", required_argument, NULL,   'r' },
-          { "statistics",    no_argument,       NULL,   'S' },
+          { "head",          required_argument, nullptr, 'm' },
+          { id_str,          required_argument, nullptr, 0 },
+          { "last",          no_argument,       nullptr, 'L' },
+          { "max-count",     required_argument, nullptr, 'm' },
+          { pid_str,         required_argument, nullptr, 0 },
+          { print_str,       no_argument,       nullptr, 0 },
+          { "prune",         optional_argument, nullptr, 'p' },
+          { "regex",         required_argument, nullptr, 'e' },
+          { "rotate-count",  required_argument, nullptr, 'n' },
+          { "rotate-kbytes", required_argument, nullptr, 'r' },
+          { "statistics",    no_argument,       nullptr, 'S' },
           // hidden and undocumented reserved alias for -t
-          { "tail",          required_argument, NULL,   't' },
+          { "tail",          required_argument, nullptr, 't' },
           // support, but ignore and do not document, the optional argument
-          { wrap_str,        optional_argument, NULL,   0 },
-          { NULL,            0,                 NULL,   0 }
+          { wrap_str,        optional_argument, nullptr, 0 },
+          { nullptr,         0,                 nullptr, 0 }
         };
         // clang-format on
 
         ret = getopt_long(argc, argv,
                           ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
                           &option_index);
-
-        if (ret < 0) {
-            break;
-        }
+        if (ret < 0) break;
 
         switch (ret) {
             case 0:
@@ -943,8 +944,7 @@
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
-                    setId = optarg && optarg[0] ? optarg : NULL;
-                    break;
+                    setId = (optarg && optarg[0]) ? optarg : nullptr;
                 }
                 break;
 
@@ -1012,7 +1012,7 @@
                 break;
 
             case 'm': {
-                char* end = NULL;
+                char* end = nullptr;
                 if (!getSizeTArg(optarg, &context->maxCount)) {
                     logcat_panic(context, HELP_FALSE,
                                  "-%c \"%s\" isn't an "
@@ -1076,19 +1076,23 @@
                 break;
 
             case 'b': {
+                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg),
+                                                               free);
+                optarg = buffers.get();
                 unsigned idMask = 0;
-                while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
-                    if (strcmp(optarg, "default") == 0) {
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(optarg = strtok_r(optarg, delimiters, &sv))) {
+                    if (!strcmp(optarg, "default")) {
                         idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
-                    } else if (strcmp(optarg, "all") == 0) {
+                    } else if (!strcmp(optarg, "all")) {
                         allSelected = true;
                         idMask = (unsigned)-1;
                     } else {
                         log_id_t log_id = android_name_to_log_id(optarg);
                         const char* name = android_log_id_to_name(log_id);
 
-                        if (strcmp(name, optarg) != 0) {
+                        if (!!strcmp(name, optarg)) {
                             logcat_panic(context, HELP_TRUE,
                                          "unknown buffer %s\n", optarg);
                             goto exit;
@@ -1096,19 +1100,15 @@
                         if (log_id == LOG_ID_SECURITY) allSelected = false;
                         idMask |= (1 << log_id);
                     }
-                    optarg = NULL;
+                    optarg = nullptr;
                 }
 
                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
                     const char* name = android_log_id_to_name((log_id_t)i);
                     log_id_t log_id = android_name_to_log_id(name);
 
-                    if (log_id != (log_id_t)i) {
-                        continue;
-                    }
-                    if ((idMask & (1 << i)) == 0) {
-                        continue;
-                    }
+                    if (log_id != (log_id_t)i) continue;
+                    if (!(idMask & (1 << i))) continue;
 
                     bool found = false;
                     for (dev = devices; dev; dev = dev->next) {
@@ -1116,13 +1116,9 @@
                             found = true;
                             break;
                         }
-                        if (!dev->next) {
-                            break;
-                        }
+                        if (!dev->next) break;
                     }
-                    if (found) {
-                        continue;
-                    }
+                    if (found) continue;
 
                     bool binary =
                         !strcmp(name, "events") || !strcmp(name, "security");
@@ -1143,7 +1139,7 @@
                 break;
 
             case 'f':
-                if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
+                if ((tail_time == log_time::EPOCH) && !tail_lines) {
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
@@ -1166,20 +1162,28 @@
                 }
                 break;
 
-            case 'v':
+            case 'v': {
                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
                     show_format_help(context);
                     context->retval = EXIT_SUCCESS;
                     goto exit;
                 }
-                err = setLogFormat(context, optarg);
-                if (err < 0) {
-                    logcat_panic(context, HELP_FORMAT,
-                                 "Invalid parameter \"%s\" to -v\n", optarg);
-                    goto exit;
+                std::unique_ptr<char, void (*)(void*)> formats(strdup(optarg),
+                                                               free);
+                optarg = formats.get();
+                unsigned idMask = 0;
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(optarg = strtok_r(optarg, delimiters, &sv))) {
+                    err = setLogFormat(context, optarg);
+                    if (err < 0) {
+                        logcat_panic(context, HELP_FORMAT,
+                                     "Invalid parameter \"%s\" to -v\n", optarg);
+                        goto exit;
+                    }
+                    optarg = nullptr;
+                    if (err) hasSetLogFormat = true;
                 }
-                hasSetLogFormat |= err;
-                break;
+            } break;
 
             case 'Q':
 #define KERNEL_OPTION "androidboot.logcat="
@@ -1286,13 +1290,13 @@
         }
     }
 
-    if (context->logRotateSizeKBytes != 0 && context->outputFileName == NULL) {
+    if (!!context->logRotateSizeKBytes && !context->outputFileName) {
         logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
         goto exit;
     }
 
-    if (setId != NULL) {
-        if (context->outputFileName == NULL) {
+    if (!!setId) {
+        if (!context->outputFileName) {
             logcat_panic(context, HELP_TRUE,
                          "--id='%s' requires -f as well\n", setId);
             goto exit;
@@ -1304,22 +1308,29 @@
         bool file_ok = android::base::ReadFileToString(file_name, &file);
         android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
                                          getuid(), getgid());
-        if (!file_ok || (file.compare(setId) == 0)) {
-            setId = NULL;
-        }
+        if (!file_ok || !file.compare(setId)) setId = nullptr;
     }
 
-    if (hasSetLogFormat == 0) {
+    if (!hasSetLogFormat) {
         const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
 
-        if (logFormat != NULL) {
-            err = setLogFormat(context, logFormat);
-            if ((err < 0) && context->error) {
-                fprintf(context->error,
-                        "invalid format in ANDROID_PRINTF_LOG '%s'\n",
-                        logFormat);
+        if (!!logFormat) {
+            std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
+                                                           free);
+            char* sv = nullptr;  // protect against -ENOMEM above
+            char* arg = formats.get();
+            while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                err = setLogFormat(context, arg);
+                // environment should not cause crash of logcat
+                if ((err < 0) && context->error) {
+                    fprintf(context->error,
+                            "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+                }
+                arg = nullptr;
+                if (err > 0) hasSetLogFormat = true;
             }
-        } else {
+        }
+        if (!hasSetLogFormat) {
             setLogFormat(context, "threadtime");
         }
     }
@@ -1336,7 +1347,7 @@
         // Add from environment variable
         const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
-        if (env_tags_orig != NULL) {
+        if (!!env_tags_orig) {
             err = android_log_addFilterString(context->logformat,
                                               env_tags_orig);
 
@@ -1351,6 +1362,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) {
@@ -1389,7 +1402,7 @@
                 for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
                     std::string file;
 
-                    if (i == 0) {
+                    if (!i) {
                         file = android::base::StringPrintf(
                             "%s", context->outputFileName);
                     } else {
@@ -1397,7 +1410,7 @@
                             context->outputFileName, maxRotationCountDigits, i);
                     }
 
-                    if (file.length() == 0) {
+                    if (!file.length()) {
                         perror("while clearing log files");
                         reportErrorName(&clearFail, dev->device, allSelected);
                         break;
@@ -1405,7 +1418,7 @@
 
                     err = unlink(file.c_str());
 
-                    if (err < 0 && errno != ENOENT && clearFail == NULL) {
+                    if (err < 0 && errno != ENOENT && !clearFail) {
                         perror("while clearing log files");
                         reportErrorName(&clearFail, dev->device, allSelected);
                     }
@@ -1472,7 +1485,7 @@
         size_t len = strlen(setPruneList);
         // extra 32 bytes are needed by android_logger_set_prune_list
         size_t bLen = len + 32;
-        char* buf = NULL;
+        char* buf = nullptr;
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
@@ -1492,7 +1505,7 @@
         char* buf;
 
         for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
-             delete[] buf, buf = NULL, --retry) {
+             delete[] buf, buf = nullptr, --retry) {
             if (getPruneList) {
                 android_logger_get_prune_list(logger_list, buf, len);
             } else {
@@ -1501,7 +1514,7 @@
             buf[len - 1] = '\0';
             if (atol(buf) < 3) {
                 delete[] buf;
-                buf = NULL;
+                buf = nullptr;
                 break;
             }
             size_t ret = atol(buf) + 1;
@@ -1521,19 +1534,13 @@
         char* cp = buf + len - 1;
         *cp = '\0';
         bool truncated = *--cp != '\f';
-        if (!truncated) {
-            *cp = '\0';
-        }
+        if (!truncated) *cp = '\0';
 
         // squash out the byte count
         cp = buf;
         if (!truncated) {
-            while (isdigit(*cp)) {
-                ++cp;
-            }
-            if (*cp == '\n') {
-                ++cp;
-            }
+            while (isdigit(*cp)) ++cp;
+            if (*cp == '\n') ++cp;
         }
 
         len = strlen(cp);
@@ -1542,32 +1549,28 @@
         goto close;
     }
 
-    if (getLogSize || setLogSize || clearLog) {
-        goto close;
-    }
+    if (getLogSize || setLogSize || clearLog) goto close;
 
-    setupOutputAndSchedulingPolicy(context, (mode & ANDROID_LOG_NONBLOCK) == 0);
+    setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
     if (context->stop) goto close;
 
     // LOG_EVENT_INT(10, 12345);
     // LOG_EVENT_LONG(11, 0x1122334455667788LL);
     // LOG_EVENT_STRING(0, "whassup, doc?");
 
-    dev = NULL;
+    dev = nullptr;
 
     while (!context->stop &&
            (!context->maxCount || (context->printCount < context->maxCount))) {
         struct log_msg log_msg;
         int ret = android_logger_list_read(logger_list, &log_msg);
-        if (ret == 0) {
+        if (!ret) {
             logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
             break;
         }
 
         if (ret < 0) {
-            if (ret == -EAGAIN) {
-                break;
-            }
+            if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
                 logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
@@ -1583,9 +1586,7 @@
 
         log_device_t* d;
         for (d = devices; d; d = d->next) {
-            if (android_name_to_log_id(d->device) == log_msg.id()) {
-                break;
-            }
+            if (android_name_to_log_id(d->device) == log_msg.id()) break;
         }
         if (!d) {
             context->devCount = 2; // set to Multiple
@@ -1651,9 +1652,7 @@
     android_logcat_context_internal* context = ctx;
 
     int save_errno = EBUSY;
-    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) {
-        goto exit;
-    }
+    if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
 
     if (pipe(context->fds) < 0) {
         save_errno = errno;
@@ -1690,11 +1689,11 @@
     for (auto& str : context->args) {
         context->argv_hold.push_back(str.c_str());
     }
-    context->argv_hold.push_back(NULL);
+    context->argv_hold.push_back(nullptr);
     for (auto& str : context->envs) {
         context->envp_hold.push_back(str.c_str());
     }
-    context->envp_hold.push_back(NULL);
+    context->envp_hold.push_back(nullptr);
 
     context->argc = context->argv_hold.size() - 1;
     context->argv = (char* const*)&context->argv_hold[0];
@@ -1703,7 +1702,7 @@
 #ifdef DEBUG
     fprintf(stderr, "argv[%d] = {", context->argc);
     for (auto str : context->argv_hold) {
-        fprintf(stderr, " \"%s\"", str ?: "NULL");
+        fprintf(stderr, " \"%s\"", str ?: "nullptr");
     }
     fprintf(stderr, " }\n");
     fflush(stderr);
@@ -1749,11 +1748,14 @@
 int android_logcat_destroy(android_logcat_context* ctx) {
     android_logcat_context_internal* context = *ctx;
 
-    *ctx = NULL;
+    if (!context) return -EBADF;
+
+    *ctx = nullptr;
 
     context->stop = true;
 
     while (context->thread_stopped == false) {
+        // Makes me sad, replace thread_stopped with semaphore.  Short lived.
         sched_yield();
     }
 
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/rootdir/Android.mk b/rootdir/Android.mk
index 7d0c87d..03f878a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -116,6 +116,12 @@
   EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
   LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
 endif
+
+EXPORT_GLOBAL_GCOV_OPTIONS :=
+ifeq ($(NATIVE_COVERAGE),true)
+  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+endif
+
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
@@ -163,6 +169,7 @@
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
 
 bcp_md5 :=
 bcp_dep :=
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 32817fa..2e2ab74 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -10,3 +10,4 @@
     export BOOTCLASSPATH %BOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
+    %EXPORT_GLOBAL_GCOV_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index fe7e3d0..c6546b9 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -409,6 +409,7 @@
     mkdir /data/misc/profiles/cur 0771 system system
     mkdir /data/misc/profiles/ref 0771 system system
     mkdir /data/misc/profman 0770 system shell
+    mkdir /data/misc/gcov 0770 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
diff --git a/storaged/main.cpp b/storaged/main.cpp
index f5a8f39..672f453 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -44,20 +44,6 @@
 
 storaged_t storaged;
 
-static int drop_privs() {
-    // privilege setting
-    struct sched_param param;
-    memset(&param, 0, sizeof(param));
-
-    if (set_sched_policy(0, SP_BACKGROUND) < 0) return -1;
-
-    if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) return -1;
-
-    if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) return -1;
-
-    return 0;
-}
-
 // Function of storaged's main thread
 void* storaged_main(void* s) {
     storaged_t* storaged = (storaged_t*)s;
@@ -133,10 +119,6 @@
         if (fd_emmc < 0)
             fd_emmc = TEMP_FAILURE_RETRY(open(mmc0_ext_csd, O_RDONLY));
 
-        if (drop_privs() != 0) {
-            return -1;
-        }
-
         storaged.set_privileged_fds(fd_emmc);
 
         // Start the main thread of storaged
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index bb7c623..a24c7fb 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -1,6 +1,7 @@
 service storaged /system/bin/storaged
     class main
+    priority 10
     file /d/mmc0/mmc0:0001/ext_csd r
     writepid /dev/cpuset/system-background/tasks
     user root
-    group system package_info
\ No newline at end of file
+    group package_info
\ No newline at end of file