Merge changes from topic "services_serve_interfaces_test"

* changes:
  Adds new property for service name -> PID.
  Adds a library to parse service info from init_rc files for use in tests.
  Adds a visibility rule for init defaults.
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index cf5fbc8..d1910f1 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -320,6 +320,10 @@
 }
 
 std::string GetLogFilePath() {
+    // https://issuetracker.google.com/112588493
+    const char* path = getenv("ANDROID_ADB_LOG_PATH");
+    if (path) return path;
+
 #if defined(_WIN32)
     const char log_name[] = "adb.log";
     WCHAR temp_path[MAX_PATH];
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 11a3dfd..3c03eb2 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -96,9 +96,8 @@
         " version                  show version num\n"
         "\n"
         "networking:\n"
-        " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
-        " disconnect [HOST[:PORT]]\n"
-        "     disconnect from given TCP/IP device [default port=5555], or all\n"
+        " connect HOST[:PORT]      connect to a device via TCP/IP\n"
+        " disconnect [[HOST]:PORT] disconnect from given TCP/IP device, or all\n"
         " forward --list           list all forward socket connections\n"
         " forward [--no-rebind] LOCAL REMOTE\n"
         "     forward socket connection using:\n"
@@ -130,7 +129,7 @@
         "     -a: preserve file timestamp and mode\n"
         " sync [all|data|odm|oem|product|system|system_ext|vendor]\n"
         "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
-        "     -l: list but don't copy\n"
+        "     -l: list files that would be copied, but don't copy them\n"
         "\n"
         "shell:\n"
         " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
@@ -1615,13 +1614,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) error_exit("usage: adb connect <host>[:<port>]");
+        if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
+        if (argc > 2) error_exit("usage: adb disconnect [HOST[:PORT]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1891,7 +1890,10 @@
     } else if (!strcmp(argv[0], "track-jdwp")) {
         return adb_connect_command("track-jdwp");
     } else if (!strcmp(argv[0], "track-devices")) {
-        return adb_connect_command("host:track-devices");
+        if (argc > 2 || (argc == 2 && strcmp(argv[1], "-l"))) {
+            error_exit("usage: adb track-devices [-l]");
+        }
+        return adb_connect_command(argc == 2 ? "host:track-devices-l" : "host:track-devices");
     } else if (!strcmp(argv[0], "raw")) {
         if (argc != 2) {
             error_exit("usage: adb raw SERVICE");
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 1333724..98468b5 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -93,7 +93,7 @@
         }
     } else {
         std::string addr(spec.substr(4));
-        port_value = -1;
+        port_value = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
         //        on an address that isn't 'localhost' is unsupported.
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index f5ec0f1..3a2f60c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -20,38 +20,71 @@
 
 #include <gtest/gtest.h>
 
-TEST(socket_spec, parse_tcp_socket_spec) {
+TEST(socket_spec, parse_tcp_socket_spec_just_port) {
     std::string hostname, error, serial;
     int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
     EXPECT_EQ("", hostname);
     EXPECT_EQ(5037, port);
     EXPECT_EQ("", serial);
+}
 
-    // Bad ports:
+TEST(socket_spec, parse_tcp_socket_spec_bad_ports) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &serial, &error));
+}
 
+TEST(socket_spec, parse_tcp_socket_spec_host_and_port) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("localhost", hostname);
     EXPECT_EQ(1234, port);
     EXPECT_EQ("localhost:1234", serial);
+}
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+TEST(socket_spec, parse_tcp_socket_spec_host_no_port) {
+    std::string hostname, error, serial;
+    int port;
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+    EXPECT_EQ("localhost", hostname);
+    EXPECT_EQ(5555, port);
+    EXPECT_EQ("localhost:5555", serial);
+}
+
+TEST(socket_spec, parse_tcp_socket_spec_host_bad_ports) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &serial, &error));
+}
 
-    // IPv6:
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_and_port) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("::1", hostname);
     EXPECT_EQ(1234, port);
     EXPECT_EQ("[::1]:1234", serial);
+}
 
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_no_port) {
+    std::string hostname, error, serial;
+    int port;
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
+    EXPECT_EQ("::1", hostname);
+    EXPECT_EQ(5555, port);
+    EXPECT_EQ("[::1]:5555", serial);
+}
+
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_bad_ports) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &serial, &error));
 }
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 8bc925f..3d1d620 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -542,9 +542,7 @@
     // for the first time, even if no update occurred.
     if (tracker->update_needed) {
         tracker->update_needed = false;
-
-        std::string transports = list_transports(tracker->long_output);
-        device_tracker_send(tracker, transports);
+        device_tracker_send(tracker, list_transports(tracker->long_output));
     }
 }
 
@@ -587,13 +585,11 @@
     update_transport_status();
 
     // Notify `adb track-devices` clients.
-    std::string transports = list_transports(false);
-
     device_tracker* tracker = device_tracker_list;
     while (tracker != nullptr) {
         device_tracker* next = tracker->next;
         // This may destroy the tracker if the connection is closed.
-        device_tracker_send(tracker, transports);
+        device_tracker_send(tracker, list_transports(tracker->long_output));
         tracker = next;
     }
 }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2f0bf7d..ac15ce4 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1009,7 +1009,7 @@
     Fstab candidates;
     for (const auto& entry : fstab) {
         FstabEntry new_entry = entry;
-        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) ||
+        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
             !fs_mgr_wants_overlayfs(&new_entry)) {
             continue;
         }
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index da1013e..9152677 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -16,6 +16,7 @@
 
 #include "libdm/dm_target.h"
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <sys/types.h>
 
@@ -149,6 +150,25 @@
     return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
 }
 
+// Computes the percentage of complition for snapshot status.
+// @sectors_initial is the number of sectors_allocated stored right before
+// starting the merge.
+double DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,
+                                      uint64_t sectors_initial) {
+    uint64_t s = status.sectors_allocated;
+    uint64_t t = status.total_sectors;
+    uint64_t m = status.metadata_sectors;
+    uint64_t i = sectors_initial == 0 ? t : sectors_initial;
+
+    if (t <= s || i <= s) {
+        return 0.0;
+    }
+    if (s == 0 || t == 0 || s <= m) {
+        return 100.0;
+    }
+    return 100.0 / (i - m) * (i - s);
+}
+
 bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
     DeviceMapper& dm = DeviceMapper::Instance();
     DmTargetTypeInfo info;
@@ -165,35 +185,29 @@
 }
 
 bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+    // Try to parse the line as it should be
+    int args = sscanf(text.c_str(), "%" PRIu64 "/%" PRIu64 " %" PRIu64, &status->sectors_allocated,
+                      &status->total_sectors, &status->metadata_sectors);
+    if (args == 3) {
+        return true;
+    }
     auto sections = android::base::Split(text, " ");
+    if (sections.size() == 0) {
+        LOG(ERROR) << "could not parse empty status";
+        return false;
+    }
+    // Error codes are: "Invalid", "Overflow" and "Merge failed"
     if (sections.size() == 1) {
-        // This is probably an error code, "Invalid" is possible as is "Overflow"
-        // on 4.4+.
+        if (text == "Invalid" || text == "Overflow") {
+            status->error = text;
+            return true;
+        }
+    } else if (sections.size() == 2 && text == "Merge failed") {
         status->error = text;
         return true;
     }
-    if (sections.size() != 2) {
-        LOG(ERROR) << "snapshot status should have two components";
-        return false;
-    }
-    auto sector_info = android::base::Split(sections[0], "/");
-    if (sector_info.size() != 2) {
-        LOG(ERROR) << "snapshot sector info should have two components";
-        return false;
-    }
-    if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
-        LOG(ERROR) << "could not parse metadata sectors";
-        return false;
-    }
-    if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
-        LOG(ERROR) << "could not parse sectors allocated";
-        return false;
-    }
-    if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
-        LOG(ERROR) << "could not parse total sectors";
-        return false;
-    }
-    return true;
+    LOG(ERROR) << "could not parse snapshot status: wrong format";
+    return false;
 }
 
 std::string DmTargetCrypt::GetParameterString() const {
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index da1c4a9..eed21dc 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -456,6 +456,87 @@
     }
 }
 
+TEST(libdm, ParseStatusText) {
+    DmTargetSnapshot::Status status;
+
+    // Bad inputs
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("X", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456 789", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456/789", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456/789", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 / 456 789", &status));
+
+    // Good input
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("123/456 789", &status));
+    EXPECT_EQ(status.sectors_allocated, 123);
+    EXPECT_EQ(status.total_sectors, 456);
+    EXPECT_EQ(status.metadata_sectors, 789);
+
+    // Known error codes
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Invalid", &status));
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Merge failed", &status));
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Overflow", &status));
+}
+
+TEST(libdm, DmSnapshotMergePercent) {
+    DmTargetSnapshot::Status status;
+
+    // Correct input
+    status.sectors_allocated = 1000;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 1.0);
+
+    status.sectors_allocated = 500;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 49.0);
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 51.0);
+
+    status.sectors_allocated = 0;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
+
+    status.sectors_allocated = 500;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 500;
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
+
+    status.sectors_allocated = 500;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 500), 1.0);
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 1000), 51.0);
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status, 1000), 49.0);
+
+    // Robustness
+    status.sectors_allocated = 2000;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+    status.sectors_allocated = 2000;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 2000;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+    status.sectors_allocated = 2000;
+    status.total_sectors = 0;
+    status.metadata_sectors = 2000;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+    status.sectors_allocated = 1000;
+    status.total_sectors = 0;
+    status.metadata_sectors = 1000;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 0), 0.0);
+}
+
 TEST(libdm, CryptArgs) {
     DmTargetCrypt target1(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
     ASSERT_EQ(target1.name(), "crypt");
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 722922d..ab7c2db 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -216,6 +216,7 @@
         std::string error;
     };
 
+    static double MergePercent(const Status& status, uint64_t sectors_initial = 0);
     static bool ParseStatusText(const std::string& text, Status* status);
     static bool ReportsOverflow(const std::string& target_type);
 
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index d6fb006..2738457 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -49,6 +49,7 @@
     std::cerr << "  delete <dm-name>" << std::endl;
     std::cerr << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  info <dm-name>" << std::endl;
     std::cerr << "  status <dm-name>" << std::endl;
     std::cerr << "  resume <dm-name>" << std::endl;
     std::cerr << "  suspend <dm-name>" << std::endl;
@@ -196,19 +197,12 @@
     char** argv_;
 };
 
-static int DmCreateCmdHandler(int argc, char** argv) {
-    if (argc < 1) {
-        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
-        return -EINVAL;
-    }
-    std::string name = argv[0];
-
+static bool parse_table_args(DmTable* table, int argc, char** argv) {
     // Parse extended options first.
-    DmTable table;
     int arg_index = 1;
     while (arg_index < argc && argv[arg_index][0] == '-') {
         if (strcmp(argv[arg_index], "-ro") == 0) {
-            table.set_readonly(true);
+            table->set_readonly(true);
             arg_index++;
         } else {
             std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
@@ -220,15 +214,30 @@
     TargetParser parser(argc - arg_index, argv + arg_index);
     while (parser.More()) {
         std::unique_ptr<DmTarget> target = parser.Next();
-        if (!target || !table.AddTarget(std::move(target))) {
+        if (!target || !table->AddTarget(std::move(target))) {
             return -EINVAL;
         }
     }
 
-    if (table.num_targets() == 0) {
+    if (table->num_targets() == 0) {
         std::cerr << "Must define at least one target." << std::endl;
         return -EINVAL;
     }
+    return 0;
+}
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+        return -EINVAL;
+    }
+    std::string name = argv[0];
+
+    DmTable table;
+    int ret = parse_table_args(&table, argc, argv);
+    if (ret) {
+        return ret;
+    }
 
     DeviceMapper& dm = DeviceMapper::Instance();
     if (!dm.CreateDevice(name, table)) {
@@ -254,6 +263,27 @@
     return 0;
 }
 
+static int DmReplaceCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl replace <dm-name> <targets...>" << std::endl;
+        return -EINVAL;
+    }
+    std::string name = argv[0];
+
+    DmTable table;
+    int ret = parse_table_args(&table, argc, argv);
+    if (ret) {
+        return ret;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.LoadTableAndActivate(name, table)) {
+        std::cerr << "Failed to replace device-mapper table to: " << name << std::endl;
+        return -EIO;
+    }
+    return 0;
+}
+
 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
                          [[maybe_unused]] char** argv) {
     std::vector<DmTargetTypeInfo> targets;
@@ -359,6 +389,41 @@
     return 0;
 }
 
+static int InfoCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    auto info = dm.GetDetailedInfo(argv[0]);
+    if (!info) {
+        std::cerr << "Invalid device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+
+    constexpr int spacing = 14;
+    std::cout << std::left << std::setw(spacing) << "device"
+              << ": " << argv[0] << std::endl;
+    std::cout << std::left << std::setw(spacing) << "active"
+              << ": " << std::boolalpha << !info->IsSuspended() << std::endl;
+    std::cout << std::left << std::setw(spacing) << "access"
+              << ": ";
+    if (info->IsReadOnly()) {
+        std::cout << "ro ";
+    } else {
+        std::cout << "rw ";
+    }
+    std::cout << std::endl;
+    std::cout << std::left << std::setw(spacing) << "activeTable"
+              << ": " << std::boolalpha << info->IsActiveTablePresent() << std::endl;
+    std::cout << std::left << std::setw(spacing) << "inactiveTable"
+              << ": " << std::boolalpha << info->IsInactiveTablePresent() << std::endl;
+    std::cout << std::left << std::setw(spacing) << "bufferFull"
+              << ": " << std::boolalpha << info->IsBufferFull() << std::endl;
+    return 0;
+}
+
 static int DumpTable(const std::string& mode, int argc, char** argv) {
     if (argc != 1) {
         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -433,9 +498,11 @@
         // clang-format off
         {"create", DmCreateCmdHandler},
         {"delete", DmDeleteCmdHandler},
+        {"replace", DmReplaceCmdHandler},
         {"list", DmListCmdHandler},
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
+        {"info", InfoCmdHandler},
         {"table", TableCmdHandler},
         {"status", StatusCmdHandler},
         {"resume", ResumeCmdHandler},
diff --git a/init/Android.bp b/init/Android.bp
index 6a0ce49..6501900 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -240,9 +240,10 @@
 
 genrule {
     name: "generated_stub_builtin_function_map",
+    tool_files: ["host_builtin_map.py"],
     out: ["generated_stub_builtin_function_map.h"],
-    srcs: ["builtins.cpp"],
-    cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+    srcs: ["builtins.cpp", "check_builtins.cpp"],
+    cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
 }
 
 cc_binary {
@@ -273,6 +274,7 @@
         "action_manager.cpp",
         "action_parser.cpp",
         "capabilities.cpp",
+        "check_builtins.cpp",
         "epoll.cpp",
         "keychords.cpp",
         "import_parser.cpp",
diff --git a/init/action.cpp b/init/action.cpp
index 0c476df..65ba25d 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -35,9 +35,11 @@
     builtin_arguments.args.resize(args.size());
     builtin_arguments.args[0] = args[0];
     for (std::size_t i = 1; i < args.size(); ++i) {
-        if (!expand_props(args[i], &builtin_arguments.args[i])) {
-            return Error() << "cannot expand '" << args[i] << "'";
+        auto expanded_arg = ExpandProps(args[i]);
+        if (!expanded_arg) {
+            return expanded_arg.error();
         }
+        builtin_arguments.args[i] = std::move(*expanded_arg);
     }
 
     return function(builtin_arguments);
@@ -66,6 +68,30 @@
     return RunBuiltinFunction(func_, args_, kInitContext);
 }
 
+Result<void> Command::CheckCommand() const {
+    auto builtin_arguments = BuiltinArguments("host_init_verifier");
+
+    builtin_arguments.args.resize(args_.size());
+    builtin_arguments.args[0] = args_[0];
+    for (size_t i = 1; i < args_.size(); ++i) {
+        auto expanded_arg = ExpandProps(args_[i]);
+        if (!expanded_arg) {
+            if (expanded_arg.error().message().find("doesn't exist while expanding") !=
+                std::string::npos) {
+                // If we failed because we won't have a property, use an empty string, which is
+                // never returned from the parser, to indicate that this field cannot be checked.
+                builtin_arguments.args[i] = "";
+            } else {
+                return expanded_arg.error();
+            }
+        } else {
+            builtin_arguments.args[i] = std::move(*expanded_arg);
+        }
+    }
+
+    return func_(builtin_arguments);
+}
+
 std::string Command::BuildCommandString() const {
     return Join(args_, ' ');
 }
@@ -105,6 +131,18 @@
     return commands_.size();
 }
 
+size_t Action::CheckAllCommands() const {
+    size_t failures = 0;
+    for (const auto& command : commands_) {
+        if (auto result = command.CheckCommand(); !result) {
+            LOG(ERROR) << "Command '" << command.BuildCommandString() << "' (" << filename_ << ":"
+                       << command.line() << ") failed: " << result.error();
+            ++failures;
+        }
+    }
+    return failures;
+}
+
 void Action::ExecuteOneCommand(std::size_t command) const {
     // We need a copy here since some Command execution may result in
     // changing commands_ vector by importing .rc files through parser
diff --git a/init/action.h b/init/action.h
index 80c1da4..1534bf9 100644
--- a/init/action.h
+++ b/init/action.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_ACTION_H
-#define _INIT_ACTION_H
+#pragma once
 
 #include <map>
 #include <queue>
@@ -41,6 +40,7 @@
 
     Result<void> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
+    Result<void> CheckCommand() const;
 
     int line() const { return line_; }
 
@@ -63,7 +63,7 @@
 
     Result<void> AddCommand(std::vector<std::string>&& args, int line);
     void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
-    std::size_t NumCommands() const;
+    size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
     bool CheckEvent(const EventTrigger& event_trigger) const;
@@ -71,6 +71,7 @@
     bool CheckEvent(const BuiltinAction& builtin_action) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
+    size_t CheckAllCommands() const;
 
     bool oneshot() const { return oneshot_; }
     const std::string& filename() const { return filename_; }
@@ -96,5 +97,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 541c8f2..ebca762 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -23,6 +23,14 @@
 
 ActionManager::ActionManager() : current_command_(0) {}
 
+size_t ActionManager::CheckAllCommands() {
+    size_t failures = 0;
+    for (const auto& action : actions_) {
+        failures += action->CheckAllCommands();
+    }
+    return failures;
+}
+
 ActionManager& ActionManager::GetInstance() {
     static ActionManager instance;
     return instance;
diff --git a/init/action_manager.h b/init/action_manager.h
index 5f47a6d..a2b95ac 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_ACTION_MANAGER_H
-#define _INIT_ACTION_MANAGER_H
+#pragma once
 
 #include <string>
 #include <vector>
@@ -32,6 +31,7 @@
 
     // Exposed for testing
     ActionManager();
+    size_t CheckAllCommands();
 
     void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
@@ -55,5 +55,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index b6e26a1..e75f5cb 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -201,26 +201,26 @@
 static Result<void> do_exec(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec service";
+        return Error() << "Could not create exec service: " << service.error();
     }
-    if (auto result = service->ExecStart(); !result) {
+    if (auto result = (*service)->ExecStart(); !result) {
         return Error() << "Could not start exec service: " << result.error();
     }
 
-    ServiceList::GetInstance().AddService(std::move(service));
+    ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
 static Result<void> do_exec_background(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec background service";
+        return Error() << "Could not create exec background service: " << service.error();
     }
-    if (auto result = service->Start(); !result) {
+    if (auto result = (*service)->Start(); !result) {
         return Error() << "Could not start exec background service: " << result.error();
     }
 
-    ServiceList::GetInstance().AddService(std::move(service));
+    ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
@@ -344,7 +344,7 @@
         if (args.size() == 5) {
             gid = DecodeUid(args[4]);
             if (!gid) {
-                return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+                return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
             }
         }
 
@@ -936,40 +936,17 @@
 }
 
 static Result<void> do_restorecon(const BuiltinArguments& args) {
+    auto restorecon_info = ParseRestorecon(args.args);
+    if (!restorecon_info) {
+        return restorecon_info.error();
+    }
+
+    const auto& [flag, paths] = *restorecon_info;
+
     int ret = 0;
-
-    struct flag_type {const char* name; int value;};
-    static const flag_type flags[] = {
-        {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
-        {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
-        {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
-        {0, 0}
-    };
-
-    int flag = 0;
-
-    bool in_flags = true;
-    for (size_t i = 1; i < args.size(); ++i) {
-        if (android::base::StartsWith(args[i], "--")) {
-            if (!in_flags) {
-                return Error() << "flags must precede paths";
-            }
-            bool found = false;
-            for (size_t j = 0; flags[j].name; ++j) {
-                if (args[i] == flags[j].name) {
-                    flag |= flags[j].value;
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                return Error() << "bad flag " << args[i];
-            }
-        } else {
-            in_flags = false;
-            if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
-                ret = errno;
-            }
+    for (const auto& path : paths) {
+        if (selinux_android_restorecon(path.c_str(), flag) < 0) {
+            ret = errno;
         }
     }
 
@@ -1056,9 +1033,9 @@
                                             const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec service";
+        return Error() << "Could not create exec service: " << service.error();
     }
-    service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
+    (*service)->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
             // TODO (b/122850122): support this in gsi
             if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
@@ -1073,10 +1050,10 @@
             }
         }
     });
-    if (auto result = service->ExecStart(); !result) {
+    if (auto result = (*service)->ExecStart(); !result) {
         return Error() << "Could not start exec service: " << result.error();
     }
-    ServiceList::GetInstance().AddService(std::move(service));
+    ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
new file mode 100644
index 0000000..3bd4774
--- /dev/null
+++ b/init/check_builtins.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+// Note that these check functions cannot check expanded arguments from properties, since they will
+// not know what those properties would be at runtime.  They will be passed an empty string in the
+// situation that the input line had a property expansion without a default value, since an empty
+// string is otherwise an impossible value.  They should therefore disregard checking empty
+// arguments.
+
+#include "check_builtins.h"
+
+#include <sys/time.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "builtin_arguments.h"
+#include "rlimit_parser.h"
+#include "service.h"
+#include "util.h"
+
+using android::base::ParseInt;
+using android::base::StartsWith;
+
+#define ReturnIfAnyArgsEmpty()     \
+    for (const auto& arg : args) { \
+        if (arg.empty()) {         \
+            return {};             \
+        }                          \
+    }
+
+namespace android {
+namespace init {
+
+Result<void> check_chown(const BuiltinArguments& args) {
+    if (!args[1].empty()) {
+        auto uid = DecodeUid(args[1]);
+        if (!uid) {
+            return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
+        }
+    }
+
+    // GID is optional and pushes the index of path out by one if specified.
+    if (args.size() == 4 && !args[2].empty()) {
+        auto gid = DecodeUid(args[2]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
+        }
+    }
+
+    return {};
+}
+
+Result<void> check_exec(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    auto result = Service::MakeTemporaryOneshotService(args.args);
+    if (!result) {
+        return result.error();
+    }
+
+    return {};
+}
+
+Result<void> check_exec_background(const BuiltinArguments& args) {
+    return check_exec(std::move(args));
+}
+
+Result<void> check_load_system_props(const BuiltinArguments& args) {
+    return Error() << "'load_system_props' is deprecated";
+}
+
+Result<void> check_loglevel(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    int log_level = -1;
+    ParseInt(args[1], &log_level);
+    if (log_level < 0 || log_level > 7) {
+        return Error() << "loglevel must be in the range of 0-7";
+    }
+    return {};
+}
+
+Result<void> check_mkdir(const BuiltinArguments& args) {
+    if (args.size() >= 4) {
+        if (!args[3].empty()) {
+            auto uid = DecodeUid(args[3]);
+            if (!uid) {
+                return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
+            }
+        }
+
+        if (args.size() == 5 && !args[4].empty()) {
+            auto gid = DecodeUid(args[4]);
+            if (!gid) {
+                return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
+            }
+        }
+    }
+
+    return {};
+}
+
+Result<void> check_restorecon(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    auto restorecon_info = ParseRestorecon(args.args);
+    if (!restorecon_info) {
+        return restorecon_info.error();
+    }
+
+    return {};
+}
+
+Result<void> check_restorecon_recursive(const BuiltinArguments& args) {
+    return check_restorecon(std::move(args));
+}
+
+Result<void> check_setprop(const BuiltinArguments& args) {
+    const std::string& name = args[1];
+    if (name.empty()) {
+        return {};
+    }
+    const std::string& value = args[2];
+
+    if (!IsLegalPropertyName(name)) {
+        return Error() << "'" << name << "' is not a legal property name";
+    }
+
+    if (!value.empty()) {
+        if (auto result = IsLegalPropertyValue(name, value); !result) {
+            return result.error();
+        }
+    }
+
+    if (StartsWith(name, "ctl.")) {
+        return Error()
+               << "Do not set ctl. properties from init; call the Service functions directly";
+    }
+
+    static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+    if (name == kRestoreconProperty) {
+        return Error() << "Do not set '" << kRestoreconProperty
+                       << "' from init; use the restorecon builtin directly";
+    }
+
+    return {};
+}
+
+Result<void> check_setrlimit(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    auto rlimit = ParseRlimit(args.args);
+    if (!rlimit) return rlimit.error();
+    return {};
+}
+
+Result<void> check_sysclktz(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    struct timezone tz = {};
+    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+        return Error() << "Unable to parse mins_west_of_gmt";
+    }
+    return {};
+}
+
+Result<void> check_wait(const BuiltinArguments& args) {
+    if (args.size() == 3 && !args[2].empty()) {
+        int timeout_int;
+        if (!android::base::ParseInt(args[2], &timeout_int)) {
+            return Error() << "failed to parse timeout";
+        }
+    }
+    return {};
+}
+
+Result<void> check_wait_for_prop(const BuiltinArguments& args) {
+    return check_setprop(std::move(args));
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/check_builtins.h b/init/check_builtins.h
new file mode 100644
index 0000000..c974e88
--- /dev/null
+++ b/init/check_builtins.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<void> check_chown(const BuiltinArguments& args);
+Result<void> check_exec(const BuiltinArguments& args);
+Result<void> check_exec_background(const BuiltinArguments& args);
+Result<void> check_load_system_props(const BuiltinArguments& args);
+Result<void> check_loglevel(const BuiltinArguments& args);
+Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_restorecon(const BuiltinArguments& args);
+Result<void> check_restorecon_recursive(const BuiltinArguments& args);
+Result<void> check_setprop(const BuiltinArguments& args);
+Result<void> check_setrlimit(const BuiltinArguments& args);
+Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_wait(const BuiltinArguments& args);
+Result<void> check_wait_for_prop(const BuiltinArguments& args);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
new file mode 100755
index 0000000..6afcb17
--- /dev/null
+++ b/init/host_builtin_map.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+"""Generates the builtins map to be used by host_init_verifier.
+
+It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
+equivalent check_xxx() if found in check_builtins.cpp.
+
+"""
+
+import re
+import argparse
+
+parser = argparse.ArgumentParser('host_builtin_map.py')
+parser.add_argument('--builtins', required=True, help='Path to builtins.cpp')
+parser.add_argument('--check_builtins', required=True, help='Path to check_builtins.cpp')
+args = parser.parse_args()
+
+CHECK_REGEX = re.compile(r'.+check_(\S+)\(.+')
+check_functions = []
+with open(args.check_builtins) as check_file:
+  for line in check_file:
+    match = CHECK_REGEX.match(line)
+    if match:
+      check_functions.append(match.group(1))
+
+function_map = []
+with open(args.builtins) as builtins_file:
+  in_function_map = False
+  for line in builtins_file:
+    if '// Builtin-function-map start' in line:
+      in_function_map = True
+    elif '// Builtin-function-map end' in line:
+      in_function_map = False
+    elif in_function_map:
+      function_map.append(line)
+
+DO_REGEX = re.compile(r'.+do_([^\}]+).+')
+FUNCTION_REGEX = re.compile(r'(do_[^\}]+)')
+for line in function_map:
+  match = DO_REGEX.match(line)
+  if match:
+    if match.group(1) in check_functions:
+      print line.replace('do_', 'check_'),
+    else:
+      print FUNCTION_REGEX.sub('check_stub', line),
+  else:
+    print line,
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 7c0544a..f9a08a5 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -26,6 +26,7 @@
 
 // android/api-level.h
 #define __ANDROID_API_P__ 28
+#define __ANDROID_API_R__ 30
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index dfde51b..a5a5b1b 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -35,6 +35,7 @@
 #include "action.h"
 #include "action_manager.h"
 #include "action_parser.h"
+#include "check_builtins.h"
 #include "host_import_parser.h"
 #include "host_init_stubs.h"
 #include "parser.h"
@@ -163,7 +164,7 @@
 namespace android {
 namespace init {
 
-static Result<void> do_stub(const BuiltinArguments& args) {
+static Result<void> check_stub(const BuiltinArguments& args) {
     return {};
 }
 
@@ -238,9 +239,10 @@
         LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
         return EXIT_FAILURE;
     }
-    if (parser.parse_error_count() > 0) {
-        LOG(ERROR) << "Failed to parse init script '" << *argv << "' with "
-                   << parser.parse_error_count() << " errors";
+    size_t failures = parser.parse_error_count() + am.CheckAllCommands();
+    if (failures > 0) {
+        LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
+                   << " errors";
         return EXIT_FAILURE;
     }
     return EXIT_SUCCESS;
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index c72b7d6..1a43508 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -29,15 +29,14 @@
         return Error() << "single argument needed for import\n";
     }
 
-    std::string conf_file;
-    bool ret = expand_props(args[1], &conf_file);
-    if (!ret) {
-        return Error() << "error while expanding import";
+    auto conf_file = ExpandProps(args[1]);
+    if (!conf_file) {
+        return Error() << "Could not expand import: " << conf_file.error();
     }
 
-    LOG(INFO) << "Added '" << conf_file << "' to import list";
+    LOG(INFO) << "Added '" << *conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
-    imports_.emplace_back(std::move(conf_file), line);
+    imports_.emplace_back(std::move(*conf_file), line);
     return {};
 }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3761750..17622a3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -174,13 +174,8 @@
         return PROP_ERROR_INVALID_NAME;
     }
 
-    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
-        *error = "Property value too long";
-        return PROP_ERROR_INVALID_VALUE;
-    }
-
-    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
-        *error = "Value is not a UTF8 encoded string";
+    if (auto result = IsLegalPropertyValue(name, value); !result) {
+        *error = result.error().message();
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -648,13 +643,14 @@
             }
 
             std::string raw_filename(fn);
-            std::string expanded_filename;
-            if (!expand_props(raw_filename, &expanded_filename)) {
-                LOG(ERROR) << "Could not expand filename '" << raw_filename << "'";
+            auto expanded_filename = ExpandProps(raw_filename);
+
+            if (!expanded_filename) {
+                LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
                 continue;
             }
 
-            load_properties_from_file(expanded_filename.c_str(), key, properties);
+            load_properties_from_file(expanded_filename->c_str(), key, properties);
         } else {
             value = strchr(key, '=');
             if (!value) continue;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 54be086..143cdfd 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -497,24 +497,28 @@
 // This function returns the Android version with which the vendor SEPolicy was compiled.
 // It is used for version checks such as whether or not vendor_init should be used
 int SelinuxGetVendorAndroidVersion() {
-    if (!IsSplitPolicyDevice()) {
-        // If this device does not split sepolicy files, it's not a Treble device and therefore,
-        // we assume it's always on the latest platform.
-        return __ANDROID_API_FUTURE__;
-    }
+    static int vendor_android_version = [] {
+        if (!IsSplitPolicyDevice()) {
+            // If this device does not split sepolicy files, it's not a Treble device and therefore,
+            // we assume it's always on the latest platform.
+            return __ANDROID_API_FUTURE__;
+        }
 
-    std::string version;
-    if (!GetVendorMappingVersion(&version)) {
-        LOG(FATAL) << "Could not read vendor SELinux version";
-    }
+        std::string version;
+        if (!GetVendorMappingVersion(&version)) {
+            LOG(FATAL) << "Could not read vendor SELinux version";
+        }
 
-    int major_version;
-    std::string major_version_str(version, 0, version.find('.'));
-    if (!ParseInt(major_version_str, &major_version)) {
-        PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
-    }
+        int major_version;
+        std::string major_version_str(version, 0, version.find('.'));
+        if (!ParseInt(major_version_str, &major_version)) {
+            PLOG(FATAL) << "Failed to parse the vendor sepolicy major version "
+                        << major_version_str;
+        }
 
-    return major_version;
+        return major_version;
+    }();
+    return vendor_android_version;
 }
 
 // This function initializes SELinux then execs init to run in the init SELinux context.
diff --git a/init/service.cpp b/init/service.cpp
index 532b1d7..9537843 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -100,9 +100,11 @@
     expanded_args.resize(args.size());
     c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
-        if (!expand_props(args[i], &expanded_args[i])) {
-            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
+        auto expanded_arg = ExpandProps(args[i]);
+        if (!expanded_arg) {
+            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
         }
+        expanded_args[i] = *expanded_arg;
         c_strings.push_back(expanded_args[i].data());
     }
     c_strings.push_back(nullptr);
@@ -642,7 +644,8 @@
     }
 }
 
-std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
+Result<std::unique_ptr<Service>> Service::MakeTemporaryOneshotService(
+        const std::vector<std::string>& args) {
     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
     // SECLABEL can be a - to denote default
     std::size_t command_arg = 1;
@@ -653,13 +656,11 @@
         }
     }
     if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
-        LOG(ERROR) << "exec called with too many supplementary group ids";
-        return nullptr;
+        return Error() << "exec called with too many supplementary group ids";
     }
 
     if (command_arg >= args.size()) {
-        LOG(ERROR) << "exec called without command";
-        return nullptr;
+        return Error() << "exec called without command";
     }
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
@@ -678,8 +679,7 @@
     if (command_arg > 3) {
         uid = DecodeUid(args[2]);
         if (!uid) {
-            LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
-            return nullptr;
+            return Error() << "Unable to decode UID for '" << args[2] << "': " << uid.error();
         }
     }
     Result<gid_t> gid = 0;
@@ -687,16 +687,14 @@
     if (command_arg > 4) {
         gid = DecodeUid(args[3]);
         if (!gid) {
-            LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
-            return nullptr;
+            return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
             auto supp_gid = DecodeUid(args[4 + i]);
             if (!supp_gid) {
-                LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
-                           << "': " << supp_gid.error();
-                return nullptr;
+                return Error() << "Unable to decode GID for '" << args[4 + i]
+                               << "': " << supp_gid.error();
             }
             supp_gids.push_back(*supp_gid);
         }
diff --git a/init/service.h b/init/service.h
index cdf31bb..6f79faa 100644
--- a/init/service.h
+++ b/init/service.h
@@ -71,7 +71,8 @@
             const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
             Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
 
-    static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
+    static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
+            const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
     Result<void> ExecStart();
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 65d96c6..e45e804 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -193,9 +193,9 @@
 Result<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) {
     auto it = args.begin() + 1;
     if (args.size() == 2 && StartsWith(args[1], "$")) {
-        std::string expanded;
-        if (!expand_props(args[1], &expanded)) {
-            return Error() << "Could not expand property '" << args[1] << "'";
+        auto expanded = ExpandProps(args[1]);
+        if (!expanded) {
+            return expanded.error();
         }
 
         // If the property is not set, it defaults to none, in which case there are no keycodes
@@ -204,7 +204,7 @@
             return {};
         }
 
-        args = Split(expanded, ",");
+        args = Split(*expanded, ",");
         it = args.begin();
     }
 
@@ -422,9 +422,11 @@
     FileDescriptor file;
     file.type = args[2];
 
-    if (!expand_props(args[1], &file.name)) {
-        return Error() << "Could not expand property in file path '" << args[1] << "'";
+    auto file_name = ExpandProps(args[1]);
+    if (!file_name) {
+        return Error() << "Could not expand file path ': " << file_name.error();
     }
+    file.name = *file_name;
     if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
         return Error() << "file name must not be relative";
     }
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 6a34acc..c9cc7bd 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -75,15 +75,15 @@
 TEST(service, make_temporary_oneshot_service_invalid_syntax) {
     std::vector<std::string> args;
     // Nothing.
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 
     // No arguments to 'exec'.
     args.push_back("exec");
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 
     // No command in "exec --".
     args.push_back("--");
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 }
 
 TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
@@ -97,7 +97,7 @@
     }
     args.push_back("--");
     args.push_back("/system/bin/id");
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 }
 
 static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
@@ -122,8 +122,9 @@
     }
     args.push_back("/system/bin/toybox");
     args.push_back("id");
-    auto svc = Service::MakeTemporaryOneshotService(args);
-    ASSERT_NE(nullptr, svc);
+    auto service_ret = Service::MakeTemporaryOneshotService(args);
+    ASSERT_TRUE(service_ret);
+    auto svc = std::move(*service_ret);
 
     if (seclabel) {
         ASSERT_EQ("u:r:su:s0", svc->seclabel());
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index c9a09cd..984235d 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -73,6 +73,13 @@
                 auto exec_duration_ms =
                     std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                 wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+            } else if (service->flags() & SVC_ONESHOT) {
+                auto exec_duration = boot_clock::now() - service->time_started();
+                auto exec_duration_ms =
+                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
+                                .count();
+                wait_string = StringPrintf(" oneshot service took %f seconds in background",
+                                           exec_duration_ms / 1000.0f);
             }
         } else {
             name = StringPrintf("Untracked pid %d", pid);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index a13f0c7..00f91d8 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -151,15 +151,15 @@
 void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
                                    SubcontextReply* reply) const {
     for (const auto& arg : expand_args_command.args()) {
-        auto expanded_prop = std::string{};
-        if (!expand_props(arg, &expanded_prop)) {
+        auto expanded_arg = ExpandProps(arg);
+        if (!expanded_arg) {
             auto* failure = reply->mutable_failure();
-            failure->set_error_string("Failed to expand '" + arg + "'");
+            failure->set_error_string(expanded_arg.error().message());
             failure->set_error_errno(0);
             return;
         } else {
             auto* expand_args_reply = reply->mutable_expand_args_reply();
-            expand_args_reply->add_expanded_args(expanded_prop);
+            expand_args_reply->add_expanded_args(*expanded_arg);
         }
     }
 }
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index e120a62..ae89c38 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -166,7 +166,8 @@
         };
         auto result = subcontext.ExpandArgs(args);
         ASSERT_FALSE(result);
-        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error().message());
+        EXPECT_EQ("unexpected end of string in '" + args[1] + "', looking for }",
+                  result.error().message());
     });
 }
 
diff --git a/init/util.cpp b/init/util.cpp
index 8bfb755..0532375 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -41,13 +41,18 @@
 #include <selinux/android.h>
 
 #if defined(__ANDROID__)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+
 #include "reboot_utils.h"
 #include "selabel.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
 
 using android::base::boot_clock;
+using android::base::StartsWith;
 using namespace std::literals::string_literals;
 
 namespace android {
@@ -267,12 +272,10 @@
     return S_ISDIR(info.st_mode);
 }
 
-bool expand_props(const std::string& src, std::string* dst) {
+Result<std::string> ExpandProps(const std::string& src) {
     const char* src_ptr = src.c_str();
 
-    if (!dst) {
-        return false;
-    }
+    std::string dst;
 
     /* - variables can either be $x.y or ${x.y}, in case they are only part
      *   of the string.
@@ -286,19 +289,19 @@
 
         c = strchr(src_ptr, '$');
         if (!c) {
-            dst->append(src_ptr);
-            return true;
+            dst.append(src_ptr);
+            return dst;
         }
 
-        dst->append(src_ptr, c);
+        dst.append(src_ptr, c);
         c++;
 
         if (*c == '$') {
-            dst->push_back(*(c++));
+            dst.push_back(*(c++));
             src_ptr = c;
             continue;
         } else if (*c == '\0') {
-            return true;
+            return dst;
         }
 
         std::string prop_name;
@@ -308,8 +311,7 @@
             const char* end = strchr(c, '}');
             if (!end) {
                 // failed to find closing brace, abort.
-                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
-                return false;
+                return Error() << "unexpected end of string in '" << src << "', looking for }";
             }
             prop_name = std::string(c, end);
             c = end + 1;
@@ -320,29 +322,34 @@
             }
         } else {
             prop_name = c;
-            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
+            if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+                return Error() << "using deprecated syntax for specifying property '" << c
+                               << "', use ${name} instead";
+            } else {
+                LOG(ERROR) << "using deprecated syntax for specifying property '" << c
+                           << "', use ${name} instead";
+            }
             c += prop_name.size();
         }
 
         if (prop_name.empty()) {
-            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
-            return false;
+            return Error() << "invalid zero-length property name in '" << src << "'";
         }
 
         std::string prop_val = android::base::GetProperty(prop_name, "");
         if (prop_val.empty()) {
             if (def_val.empty()) {
-                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
-                return false;
+                return Error() << "property '" << prop_name << "' doesn't exist while expanding '"
+                               << src << "'";
             }
             prop_val = def_val;
         }
 
-        dst->append(prop_val);
+        dst.append(prop_val);
         src_ptr = c;
     }
 
-    return true;
+    return dst;
 }
 
 static std::string init_android_dt_dir() {
@@ -414,6 +421,58 @@
     return true;
 }
 
+Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value) {
+    if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
+        return Error() << "Property value too long";
+    }
+
+    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+        return Error() << "Value is not a UTF8 encoded string";
+    }
+
+    return {};
+}
+
+Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
+        const std::vector<std::string>& args) {
+    struct flag_type {
+        const char* name;
+        int value;
+    };
+    static const flag_type flags[] = {
+            {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
+            {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
+            {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
+            {0, 0}};
+
+    int flag = 0;
+    std::vector<std::string> paths;
+
+    bool in_flags = true;
+    for (size_t i = 1; i < args.size(); ++i) {
+        if (android::base::StartsWith(args[i], "--")) {
+            if (!in_flags) {
+                return Error() << "flags must precede paths";
+            }
+            bool found = false;
+            for (size_t j = 0; flags[j].name; ++j) {
+                if (args[i] == flags[j].name) {
+                    flag |= flags[j].value;
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return Error() << "bad flag " << args[i];
+            }
+        } else {
+            in_flags = false;
+            paths.emplace_back(args[i]);
+        }
+    }
+    return std::pair(flag, paths);
+}
+
 static void InitAborter(const char* abort_message) {
     // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
     // simply abort instead of trying to reboot the system.
diff --git a/init/util.h b/init/util.h
index 6a12fb6..4cccefe 100644
--- a/init/util.h
+++ b/init/util.h
@@ -14,24 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_UTIL_H_
-#define _INIT_UTIL_H_
+#pragma once
 
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #include <chrono>
 #include <functional>
-#include <ostream>
 #include <string>
 
 #include <android-base/chrono_utils.h>
-#include <selinux/label.h>
 
 #include "result.h"
 
 using android::base::boot_clock;
-using namespace std::chrono_literals;
 
 namespace android {
 namespace init {
@@ -52,7 +48,7 @@
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
-bool expand_props(const std::string& src, std::string* dst);
+Result<std::string> ExpandProps(const std::string& src);
 
 // Returns the platform's Android DT directory as specified in the kernel cmdline.
 // If the platform does not configure a custom DT path, returns the standard one (based in procfs).
@@ -62,11 +58,13 @@
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
 bool IsLegalPropertyName(const std::string& name);
+Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+
+Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
+        const std::vector<std::string>& args);
 
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
 bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/libion/ion.c b/libion/ion.c
index b8de5a4..1ecfc78 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -152,6 +152,8 @@
     ion_user_handle_t handle;
     int ret;
 
+    if (!handle_fd) return -EINVAL;
+
     if (!ion_is_legacy(fd)) {
         struct ion_new_allocation_data data = {
             .len = len,
@@ -201,6 +203,7 @@
     int ret;
     struct ion_heap_query query;
 
+    if (!cnt) return -EINVAL;
     memset(&query, 0, sizeof(query));
 
     ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index b3fcb3b..d3b4688 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -18,18 +18,15 @@
     name: "ion-unit-tests",
     cflags: [
         "-g",
-        "-Wall",
-        "-Werror",
         "-Wno-missing-field-initializers",
     ],
     shared_libs: ["libion"],
     srcs: [
-        "ion_test_fixture.cpp",
         "allocate_test.cpp",
-        "formerly_valid_handle_test.cpp",
-        "invalid_values_test.cpp",
-        "map_test.cpp",
-        "device_test.cpp",
         "exit_test.cpp",
+    	"heap_query.cpp",
+        "invalid_values_test.cpp",
+        "ion_test_fixture.cpp",
+        "map_test.cpp",
     ],
 }
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index 3c4524e..5ed01bb 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,95 +14,106 @@
  * limitations under the License.
  */
 
-#include <memory>
 #include <sys/mman.h>
+#include <memory>
 
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
 #include "ion_test_fixture.h"
 
-class Allocate : public IonAllHeapsTest {
-};
+class Allocate : public IonTest {};
 
-TEST_F(Allocate, Allocate)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, Allocate) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-            ASSERT_TRUE(handle != 0);
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            int fd;
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
+            ASSERT_TRUE(fd != 0);
+            ASSERT_EQ(close(fd), 0);  // free the buffer
         }
     }
 }
 
-TEST_F(Allocate, AllocateCached)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, AllocateCached) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
-            ASSERT_TRUE(handle != 0);
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            int fd;
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), ION_FLAG_CACHED, &fd));
+            ASSERT_TRUE(fd != 0);
+            ASSERT_EQ(close(fd), 0);  // free the buffer
         }
     }
 }
 
-TEST_F(Allocate, AllocateCachedNeedsSync)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, AllocateCachedNeedsSync) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
-            ASSERT_TRUE(handle != 0);
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            int fd;
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                      ION_FLAG_CACHED_NEEDS_SYNC, &fd));
+            ASSERT_TRUE(fd != 0);
+            ASSERT_EQ(close(fd), 0);  // free the buffer
         }
     }
 }
 
-TEST_F(Allocate, RepeatedAllocate)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, RepeatedAllocate) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
+            int fd;
 
             for (unsigned int i = 0; i < 1024; i++) {
                 SCOPED_TRACE(::testing::Message() << "iteration " << i);
-                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-                ASSERT_TRUE(handle != 0);
-                ASSERT_EQ(0, ion_free(m_ionFd, handle));
+                ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
+                ASSERT_TRUE(fd != 0);
+                ASSERT_EQ(close(fd), 0);  // free the buffer
             }
         }
     }
 }
 
-TEST_F(Allocate, Zeroed)
-{
+TEST_F(Allocate, Large) {
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+        int fd;
+        ASSERT_EQ(-ENOMEM,
+                  ion_alloc_fd(ionfd, 3UL * 1024 * 1024 * 1024, 0, (1 << heap.heap_id), 0, &fd));
+    }
+}
+
+// Make sure all heaps always return zeroed pages
+TEST_F(Allocate, Zeroed) {
     auto zeroes_ptr = std::make_unique<char[]>(4096);
 
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
         int fds[16];
         for (unsigned int i = 0; i < 16; i++) {
             int map_fd = -1;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr = NULL;
+            void* ptr = NULL;
             ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
 
@@ -116,13 +127,13 @@
             ASSERT_EQ(0, close(fds[i]));
         }
 
-        int newIonFd = ion_open();
+        int new_ionfd = ion_open();
         int map_fd = -1;
 
-        ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
+        ASSERT_EQ(0, ion_alloc_fd(new_ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
         ASSERT_GE(map_fd, 0);
 
-        void *ptr = NULL;
+        void* ptr = NULL;
         ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
@@ -130,14 +141,6 @@
 
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Allocate, Large)
-{
-    for (unsigned int heapMask : m_allHeaps) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        ion_user_handle_t handle = 0;
-        ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
+        ASSERT_EQ(0, ion_close(new_ionfd));
     }
 }
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
deleted file mode 100644
index eb3f7b6..0000000
--- a/libion/tests/device_test.cpp
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <memory>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <linux/ion_test.h>
-
-#include <gtest/gtest.h>
-
-#include <ion/ion.h>
-
-#include "ion_test_fixture.h"
-
-#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
-
-class Device : public IonAllHeapsTest {
- public:
-    virtual void SetUp();
-    virtual void TearDown();
-    int m_deviceFd;
-    void readDMA(int fd, void *buf, size_t size);
-    void writeDMA(int fd, void *buf, size_t size);
-    void readKernel(int fd, void *buf, size_t size);
-    void writeKernel(int fd, void *buf, size_t size);
-    void blowCache();
-    void dirtyCache(void *ptr, size_t size);
-};
-
-void Device::SetUp()
-{
-    IonAllHeapsTest::SetUp();
-    m_deviceFd = open("/dev/ion-test", O_RDONLY);
-    ASSERT_GE(m_deviceFd, 0);
-}
-
-void Device::TearDown()
-{
-    ASSERT_EQ(0, close(m_deviceFd));
-    IonAllHeapsTest::TearDown();
-}
-
-void Device::readDMA(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 0,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::writeDMA(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 1,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::readKernel(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 0,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::writeKernel(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 1,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::blowCache()
-{
-    const size_t bigger_than_cache = 8*1024*1024;
-    void *buf1 = malloc(bigger_than_cache);
-    void *buf2 = malloc(bigger_than_cache);
-    memset(buf1, 0xaa, bigger_than_cache);
-    memcpy(buf2, buf1, bigger_than_cache);
-    free(buf1);
-    free(buf2);
-}
-
-void Device::dirtyCache(void *ptr, size_t size)
-{
-    /* try to dirty cache lines */
-    for (size_t i = size-1; i > 0; i--) {
-        ((volatile char *)ptr)[i];
-        ((char *)ptr)[i] = i;
-    }
-}
-
-TEST_F(Device, KernelReadCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ((char*)buf)[4096] = 0x12;
-        readKernel(map_fd, buf, 4096);
-        ASSERT_EQ(((char*)buf)[4096], 0x12);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelWriteCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeKernel(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAReadCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        readDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAWriteCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelReadCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ((char*)buf)[4096] = 0x12;
-        readKernel(map_fd, buf, 4096);
-        ASSERT_EQ(((char*)buf)[4096], 0x12);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelWriteCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeKernel(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAReadCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ion_sync_fd(m_ionFd, map_fd);
-
-        readDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAWriteCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeDMA(map_fd, buf, 4096);
-
-        ion_sync_fd(m_ionFd, map_fd);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-TEST_F(Device, KernelRead)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ((char*)buf)[4096] = 0x12;
-        readKernel(map_fd, buf, 4096);
-        ASSERT_EQ(((char*)buf)[4096], 0x12);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelWrite)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeKernel(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMARead)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        readDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAWrite)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, IsCached)
-{
-    auto buf_ptr = std::make_unique<char[]>(4096);
-    void *buf = buf_ptr.get();
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        readDMA(map_fd, buf, 4096);
-
-        bool same = true;
-        for (int i = 4096-16; i >= 0; i -= 16)
-            if (((char *)buf)[i] != i)
-                same = false;
-        ASSERT_FALSE(same);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
index cdd3e27..f312389 100644
--- a/libion/tests/exit_test.cpp
+++ b/libion/tests/exit_test.cpp
@@ -22,206 +22,206 @@
 
 #include "ion_test_fixture.h"
 
-class Exit : public IonAllHeapsTest {
-};
+class Exit : public IonTest {};
 
-TEST_F(Exit, WithAlloc)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithAllocFd) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                ion_user_handle_t handle = 0;
+            EXPECT_EXIT(
+                    {
+                        int handle_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-                ASSERT_TRUE(handle != 0);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        ASSERT_EQ(0,
+                                  ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &handle_fd));
+                        ASSERT_NE(-1, handle_fd);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithAllocFd)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int handle_fd = -1;
-
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
-                ASSERT_NE(-1, handle_fd);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
-        }
-    }
-}
-
-TEST_F(Exit, WithRepeatedAllocFd)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithRepeatedAllocFd) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
             for (unsigned int i = 0; i < 1024; i++) {
-                SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+                SCOPED_TRACE(::testing::Message()
+                             << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
                 SCOPED_TRACE(::testing::Message() << "size " << size);
-                ASSERT_EXIT({
-                    int handle_fd = -1;
+                ASSERT_EXIT(
+                        {
+                            int handle_fd = -1;
 
-                    ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
-                    ASSERT_NE(-1, handle_fd);
-                    exit(0);
-                }, ::testing::ExitedWithCode(0), "")
-                        << "failed on heap " << heapMask
-                        << " and size " << size
-                        << " on iteration " << i;
+                            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0,
+                                                      &handle_fd));
+                            ASSERT_NE(-1, handle_fd);
+                            exit(0);
+                        },
+                        ::testing::ExitedWithCode(0), "")
+                        << "failed on heap " << heap.name << ":" << heap.type << ":" << heap.heap_id
+                        << " and size " << size << " on iteration " << i;
             }
         }
     }
 }
 
-
-TEST_F(Exit, WithMapping)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithMapping) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
-        }
-    }
-
-}
-
-TEST_F(Exit, WithPartialMapping)
-{
-    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
-
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
-                ASSERT_GE(map_fd, 0);
-
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-
-                ASSERT_EQ(0, munmap(ptr, size / 2));
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithMappingCached)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithPartialMapping) {
+    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
-        }
-    }
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
 
-}
-
-TEST_F(Exit, WithPartialMappingCached)
-{
-    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
-
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
-                ASSERT_GE(map_fd, 0);
-
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-
-                ASSERT_EQ(0, munmap(ptr, size / 2));
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        ASSERT_EQ(0, munmap(ptr, size / 2));
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithMappingNeedsSync)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithMappingCached) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
-
 }
 
-TEST_F(Exit, WithPartialMappingNeedsSync)
-{
-    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithPartialMappingCached) {
+    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
 
-                ASSERT_EQ(0, munmap(ptr, size / 2));
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        ASSERT_EQ(0, munmap(ptr, size / 2));
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithMappingNeedsSync) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
+
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+                                                  &map_fd));
+                        ASSERT_GE(map_fd, 0);
+
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithPartialMappingNeedsSync) {
+    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
+
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+                                                  &map_fd));
+                        ASSERT_GE(map_fd, 0);
+
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+
+                        ASSERT_EQ(0, munmap(ptr, size / 2));
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
deleted file mode 100644
index 01ab8f3..0000000
--- a/libion/tests/formerly_valid_handle_test.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/mman.h>
-
-#include <gtest/gtest.h>
-
-#include <ion/ion.h>
-
-#include "ion_test_fixture.h"
-
-class FormerlyValidHandle : public IonTest {
- public:
-    virtual void SetUp();
-    virtual void TearDown();
-    ion_user_handle_t m_handle;
-};
-
-void FormerlyValidHandle::SetUp()
-{
-    IonTest::SetUp();
-    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
-    ASSERT_TRUE(m_handle != 0);
-    ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
-}
-
-void FormerlyValidHandle::TearDown()
-{
-    m_handle = 0;
-}
-
-TEST_F(FormerlyValidHandle, free)
-{
-	ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
-}
-
-TEST_F(FormerlyValidHandle, map)
-{
-    int map_fd;
-    unsigned char *ptr;
-
-    ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-}
-
-TEST_F(FormerlyValidHandle, share)
-{
-    int share_fd;
-
-    ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
-}
diff --git a/libion/tests/heap_query.cpp b/libion/tests/heap_query.cpp
new file mode 100644
index 0000000..bad3bbf
--- /dev/null
+++ b/libion/tests/heap_query.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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 <gtest/gtest.h>
+#include "ion_test_fixture.h"
+
+class HeapQuery : public IonTest {};
+
+TEST_F(HeapQuery, AtleastOneHeap) {
+    ASSERT_GT(ion_heaps.size(), 0);
+}
+
+// TODO: Check if we expect some of the default
+// heap types to be present on all devices.
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
index 77fea17..48fcd72 100644
--- a/libion/tests/invalid_values_test.cpp
+++ b/libion/tests/invalid_values_test.cpp
@@ -16,171 +16,71 @@
 
 #include <sys/mman.h>
 
+#include <memory>
+#include <vector>
+
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
-
 #include "ion_test_fixture.h"
 
-class InvalidValues : public IonAllHeapsTest {
- public:
-    virtual void SetUp();
-    virtual void TearDown();
-    ion_user_handle_t m_validHandle;
-    int m_validShareFd;
-    ion_user_handle_t const m_badHandle = -1;
-};
+class InvalidValues : public IonTest {};
 
-void InvalidValues::SetUp()
-{
-    IonAllHeapsTest::SetUp();
-    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
-      << m_ionFd << " " << m_firstHeap;
-    ASSERT_TRUE(m_validHandle != 0);
-    ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
-}
-
-void InvalidValues::TearDown()
-{
-    ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
-    ASSERT_EQ(0, close(m_validShareFd));
-    m_validHandle = 0;
-    IonAllHeapsTest::TearDown();
-}
-
-TEST_F(InvalidValues, ion_close)
-{
+TEST_F(InvalidValues, ion_close) {
     EXPECT_EQ(-EBADF, ion_close(-1));
 }
 
-TEST_F(InvalidValues, ion_alloc)
-{
-    ion_user_handle_t handle;
-    /* invalid ion_fd */
-    int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion_fd */
-    EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
-    /* no heaps */
-    EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        /* zero size */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
-        /* too large size */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
-        /* bad alignment */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
-        /* NULL handle */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
-    }
-}
-
-TEST_F(InvalidValues, ion_alloc_fd)
-{
+TEST_F(InvalidValues, ion_alloc_fd) {
     int fd;
-    /* invalid ion_fd */
-    int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion_fd */
-    EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
-    /* no heaps */
-    EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        /* zero size */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
-        /* too large size */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
-        /* bad alignment */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
-        /* NULL handle */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    // no heaps
+    EXPECT_EQ(-ENODEV, ion_alloc_fd(ionfd, 4096, 0, 0, 0, &fd));
+    for (const auto& heap : ion_heaps) {
+        // invalid ion_fd
+        int ret = ion_alloc_fd(0, 4096, 0, (1 << heap.heap_id), 0, &fd);
+        EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+        // invalid ion_fd
+        EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, (1 << heap.heap_id), 0, &fd));
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+        // zero size
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 0, 0, (1 << heap.heap_id), 0, &fd));
+        // too large size
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, -1, 0, (1 << heap.heap_id), 0, &fd));
+        // bad alignment
+        // TODO: Current userspace and kernel code completely ignores alignment. So this
+        // test is going to fail. We need to completely remove alignment from the API.
+        // All memory by default is always page aligned. OR actually pass the alignment
+        // down into the kernel and make kernel respect the alignment.
+        // EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, -1, (1 << heap.heap_id), 0, &fd));
+
+        // NULL fd
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, nullptr));
     }
 }
 
-TEST_F(InvalidValues, ion_free)
-{
-    /* invalid ion fd */
-    int ret = ion_free(0, m_validHandle);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
-    /* zero handle */
-    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
-    /* bad handle */
-    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+TEST_F(InvalidValues, ion_query_heap_cnt) {
+    // NULL count
+    EXPECT_EQ(-EINVAL, ion_query_heap_cnt(ionfd, nullptr));
+
+    int heap_count;
+    // bad fd
+    EXPECT_EQ(-EBADF, ion_query_heap_cnt(-1, &heap_count));
 }
 
-TEST_F(InvalidValues, ion_map)
-{
-    int map_fd;
-    unsigned char *ptr;
+TEST_F(InvalidValues, ion_query_get_heaps) {
+    int heap_count;
+    ASSERT_EQ(0, ion_query_heap_cnt(ionfd, &heap_count));
+    ASSERT_GT(heap_count, 0);
 
-    /* invalid ion fd */
-    int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* zero handle */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* bad handle */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* zero length */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* bad prot */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
-    /* bad offset */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
-    /* NULL ptr */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
-    /* NULL map_fd */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
-}
+    // nullptr buffers, still returns success but without
+    // the ion_heap_data.
+    EXPECT_EQ(0, ion_query_get_heaps(ionfd, heap_count, nullptr));
 
-TEST_F(InvalidValues, ion_share)
-{
-    int share_fd;
+    std::unique_ptr<struct ion_heap_data[]> heaps =
+            std::make_unique<struct ion_heap_data[]>(heap_count);
+    // bad fd
+    EXPECT_EQ(-EBADF, ion_query_get_heaps(-1, heap_count, heaps.get()));
 
-    /* invalid ion fd */
-    int ret = ion_share(0, m_validHandle, &share_fd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
-    /* zero handle */
-    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
-    /* bad handle */
-    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
-    /* NULL share_fd */
-    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
-}
-
-TEST_F(InvalidValues, ion_import)
-{
-    ion_user_handle_t handle;
-
-    /* invalid ion fd */
-    int ret = ion_import(0, m_validShareFd, &handle);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
-    /* bad share_fd */
-    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
-    /* invalid share_fd */
-    EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
-    /* NULL handle */
-    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
-}
-
-TEST_F(InvalidValues, ion_sync_fd)
-{
-    /* invalid ion fd */
-    int ret = ion_sync_fd(0, m_validShareFd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
-    /* bad share_fd */
-    EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
-    /* invalid share_fd */
-    EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
+    // invalid heap data pointer
+    EXPECT_EQ(-EFAULT, ion_query_get_heaps(ionfd, heap_count, reinterpret_cast<void*>(0xdeadf00d)));
 }
diff --git a/libion/tests/ion_4.12.h b/libion/tests/ion_4.12.h
new file mode 100644
index 0000000..614510c
--- /dev/null
+++ b/libion/tests/ion_4.12.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ***   To edit the content of this header, modify the corresponding
+ ***   source file (e.g. under external/kernel-headers/original/) then
+ ***   run bionic/libc/kernel/tools/update_all.py
+ ***
+ ***   Any manual change here will be lost the next time this script will
+ ***   be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+struct ion_new_allocation_data {
+  __u64 len;
+  __u32 heap_id_mask;
+  __u32 flags;
+  __u32 fd;
+  __u32 unused;
+};
+#define MAX_HEAP_NAME 32
+struct ion_heap_data {
+  char name[MAX_HEAP_NAME];
+  __u32 type;
+  __u32 heap_id;
+  __u32 reserved0;
+  __u32 reserved1;
+  __u32 reserved2;
+};
+struct ion_heap_query {
+  __u32 cnt;
+  __u32 reserved0;
+  __u64 heaps;
+  __u32 reserved1;
+  __u32 reserved2;
+};
+#define ION_IOC_MAGIC 'I'
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+#endif
diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp
index e20c730..935fe5c 100644
--- a/libion/tests/ion_test_fixture.cpp
+++ b/libion/tests/ion_test_fixture.cpp
@@ -15,59 +15,26 @@
  */
 
 #include <gtest/gtest.h>
-
 #include <ion/ion.h>
 
 #include "ion_test_fixture.h"
 
-IonTest::IonTest() : m_ionFd(-1)
-{
-}
+IonTest::IonTest() : ionfd(-1), ion_heaps() {}
 
 void IonTest::SetUp() {
-    m_ionFd = ion_open();
-    ASSERT_GE(m_ionFd, 0);
+    ionfd = ion_open();
+    ASSERT_GE(ionfd, 0);
+
+    int heap_count;
+    int ret = ion_query_heap_cnt(ionfd, &heap_count);
+    ASSERT_EQ(ret, 0);
+    ASSERT_GT(heap_count, 0);
+
+    ion_heaps.resize(heap_count, {});
+    ret = ion_query_get_heaps(ionfd, heap_count, ion_heaps.data());
+    ASSERT_EQ(ret, 0);
 }
 
 void IonTest::TearDown() {
-    ion_close(m_ionFd);
-}
-
-IonAllHeapsTest::IonAllHeapsTest() :
-        m_firstHeap(0),
-        m_lastHeap(0),
-        m_allHeaps()
-{
-}
-
-void IonAllHeapsTest::SetUp() {
-    int fd = ion_open();
-    ASSERT_GE(fd, 0);
-
-    for (int i = 1; i != 0; i <<= 1) {
-        ion_user_handle_t handle = 0;
-        int ret;
-        ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
-        if (ret == 0 && handle != 0) {
-            ion_free(fd, handle);
-            if (!m_firstHeap) {
-                m_firstHeap = i;
-            }
-            m_lastHeap = i;
-            m_allHeaps.push_back(i);
-        } else {
-            ASSERT_EQ(-ENODEV, ret);
-        }
-    }
-    ion_close(fd);
-
-    EXPECT_NE(0U, m_firstHeap);
-    EXPECT_NE(0U, m_lastHeap);
-
-    RecordProperty("Heaps", m_allHeaps.size());
-    IonTest::SetUp();
-}
-
-void IonAllHeapsTest::TearDown() {
-    IonTest::TearDown();
+    ion_close(ionfd);
 }
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
index 4098214..4f254b8 100644
--- a/libion/tests/ion_test_fixture.h
+++ b/libion/tests/ion_test_fixture.h
@@ -18,29 +18,19 @@
 #define ION_TEST_FIXTURE_H_
 
 #include <gtest/gtest.h>
+#include <vector>
+#include "ion_4.12.h"
 
 using ::testing::Test;
 
 class IonTest : public virtual Test {
- public:
+  public:
     IonTest();
-	virtual ~IonTest() {};
-	virtual void SetUp();
-	virtual void TearDown();
-	int m_ionFd;
-};
-
-class IonAllHeapsTest : public IonTest {
- public:
-    IonAllHeapsTest();
-    virtual ~IonAllHeapsTest() {};
+    virtual ~IonTest(){};
     virtual void SetUp();
     virtual void TearDown();
-
-    unsigned int m_firstHeap;
-    unsigned int m_lastHeap;
-
-    std::vector<unsigned int> m_allHeaps;
+    int ionfd;
+    std::vector<struct ion_heap_data> ion_heaps;
 };
 
 #endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
index c006dc8..f1b47b7 100644
--- a/libion/tests/map_test.cpp
+++ b/libion/tests/map_test.cpp
@@ -15,61 +15,30 @@
  */
 
 #include <sys/mman.h>
+#include <unistd.h>
 
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
-
 #include "ion_test_fixture.h"
 
-class Map : public IonAllHeapsTest {
-};
+class Map : public IonTest {};
 
-TEST_F(Map, MapHandle)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapFd) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-            ASSERT_TRUE(handle != 0);
-
-            int map_fd = -1;
-            unsigned char *ptr = NULL;
-            ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
-            ASSERT_TRUE(ptr != NULL);
-            ASSERT_GE(map_fd, 0);
-
-            ASSERT_EQ(0, close(map_fd));
-
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
-
-            memset(ptr, 0xaa, size);
-
-            ASSERT_EQ(0, munmap(ptr, size));
-        }
-    }
-}
-
-TEST_F(Map, MapFd)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr;
+            void* ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
-
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
@@ -79,53 +48,51 @@
     }
 }
 
-TEST_F(Map, MapOffset)
-{
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+TEST_F(Map, MapOffset) {
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
         int map_fd = -1;
 
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
+        ASSERT_EQ(0, ion_alloc_fd(ionfd, getpagesize() * 2, 0, (1 << heap.heap_id), 0, &map_fd));
         ASSERT_GE(map_fd, 0);
 
-        unsigned char *ptr;
-        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        unsigned char* ptr;
+        ptr = (unsigned char*)mmap(NULL, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
+                                   map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
-        memset(ptr, 0, PAGE_SIZE);
-        memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
+        memset(ptr, 0, getpagesize());
+        memset(ptr + getpagesize(), 0xaa, getpagesize());
 
-        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
+        ASSERT_EQ(0, munmap(ptr, getpagesize() * 2));
 
-        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
+        ptr = (unsigned char*)mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, map_fd,
+                                   getpagesize());
         ASSERT_TRUE(ptr != NULL);
-
         ASSERT_EQ(ptr[0], 0xaa);
-        ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
-
-        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
-
+        ASSERT_EQ(ptr[getpagesize() - 1], 0xaa);
+        ASSERT_EQ(0, munmap(ptr, getpagesize()));
         ASSERT_EQ(0, close(map_fd));
     }
 }
 
-TEST_F(Map, MapCached)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapCached) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
             unsigned int flags = ION_FLAG_CACHED;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr;
+            void* ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
-
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
@@ -135,23 +102,22 @@
     }
 }
 
-TEST_F(Map, MapCachedNeedsSync)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapCachedNeedsSync) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
             unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr;
+            void* ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
-
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
index 4b02116..4add6e6 100644
--- a/libnativeloader/native_loader_namespace.cpp
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -69,10 +69,18 @@
 // "default" always exists.
 Result<NativeLoaderNamespace> NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
   auto ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
-  if (!ns) {
-    ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+  if (ns) return ns;
+  ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+  if (ns) return ns;
+
+  // If nothing is found, return NativeLoaderNamespace constructed from nullptr.
+  // nullptr also means default namespace to the linker.
+  if (!is_bridged) {
+    return NativeLoaderNamespace(kDefaultNamespaceName, static_cast<android_namespace_t*>(nullptr));
+  } else {
+    return NativeLoaderNamespace(kDefaultNamespaceName,
+                                 static_cast<native_bridge_namespace_t*>(nullptr));
   }
-  return ns;
 }
 
 Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 24a745a..b08e061 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -452,48 +452,6 @@
     *codePoint |= 0x3F & byte;
 }
 
-size_t utf8_to_utf32_length(const char *src, size_t src_len)
-{
-    if (src == nullptr || src_len == 0) {
-        return 0;
-    }
-    size_t ret = 0;
-    const char* cur;
-    const char* end;
-    size_t num_to_skip;
-    for (cur = src, end = src + src_len, num_to_skip = 1;
-         cur < end;
-         cur += num_to_skip, ret++) {
-        const char first_char = *cur;
-        num_to_skip = 1;
-        if ((first_char & 0x80) == 0) {  // ASCII
-            continue;
-        }
-        int32_t mask;
-
-        for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
-        }
-    }
-    return ret;
-}
-
-void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
-{
-    if (src == nullptr || src_len == 0 || dst == nullptr) {
-        return;
-    }
-
-    const char* cur = src;
-    const char* const end = src + src_len;
-    char32_t* cur_utf32 = dst;
-    while (cur < end) {
-        size_t num_read;
-        *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read));
-        cur += num_read;
-    }
-    *cur_utf32 = 0;
-}
-
 static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
 {
     uint32_t unicode;
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index a2aaa47..fc6712d 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -129,18 +129,6 @@
 ssize_t utf8_length(const char *src);
 
 /**
- * Measure the length of a UTF-32 string.
- */
-size_t utf8_to_utf32_length(const char *src, size_t src_len);
-
-/**
- * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
- * enough to store the entire converted string as measured by
- * utf8_to_utf32_length plus space for a NUL terminator.
- */
-void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
-
-/**
  * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
  * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
  * can ask to log a message and fail in case the invalid utf8 could have caused an override if no
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 6e38d95..6507711 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -899,6 +899,11 @@
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
+                    if (pid != 0) {
+                        logcat_panic(context, HELP_TRUE, "Only supports one PID argument.\n");
+                        goto exit;
+                    }
+
                     // ToDo: determine runtime PID_MAX?
                     if (!getSizeTArg(optarg, &pid, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 6897663..1d934a2 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -164,8 +164,10 @@
 }
 
 void storaged_t::add_user_ce(userid_t user_id) {
-    load_proto(user_id);
-    proto_loaded[user_id] = true;
+    if (!proto_loaded[user_id]) {
+        load_proto(user_id);
+        proto_loaded[user_id] = true;
+    }
 }
 
 void storaged_t::remove_user_ce(userid_t user_id) {