Merge changes Ied888249,Id8857c45

* changes:
  init: check the arguments of builtins during the build
  init: don't log in expand_props directly
diff --git a/adb/Android.bp b/adb/Android.bp
index 47dafff..06cfcbf 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -27,6 +27,7 @@
         "-DADB_HOST=1",         // overridden by adbd_defaults
         "-DALLOW_ADBD_ROOT=0",  // overridden by adbd_defaults
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
+        "-DENABLE_FASTDEPLOY=1", // enable fast deploy
     ],
     cpp_std: "experimental",
 
@@ -270,22 +271,33 @@
         "client/console.cpp",
         "client/adb_install.cpp",
         "client/line_printer.cpp",
+        "client/fastdeploy.cpp",
+        "client/fastdeploycallbacks.cpp",
         "shell_service_protocol.cpp",
     ],
 
+    generated_headers: [
+        "bin2c_fastdeployagent",
+        "bin2c_fastdeployagentscript"
+    ],
+
     static_libs: [
         "libadb_host",
+        "libandroidfw",
         "libbase",
         "libcutils",
         "libcrypto_utils",
         "libcrypto",
+        "libfastdeploy_host",
         "libdiagnose_usb",
         "liblog",
         "libmdnssd",
+        "libprotobuf-cpp-lite",
         "libusb",
         "libutils",
         "liblog",
-        "libcutils",
+        "libziparchive",
+        "libz",
     ],
 
     stl: "libc++_static",
@@ -295,10 +307,6 @@
     // will violate ODR
     shared_libs: [],
 
-    required: [
-        "deploypatchgenerator",
-    ],
-
     // Archive adb, adb.exe.
     dist: {
         targets: [
@@ -658,3 +666,90 @@
         },
     },
 }
+
+// Note: using pipe for xxd to control the variable name generated
+// the default name used by xxd is the path to the input file.
+java_genrule {
+    name: "bin2c_fastdeployagent",
+    out: ["deployagent.inc"],
+    srcs: [":deployagent"],
+    cmd: "(echo 'unsigned char kDeployAgent[] = {' && xxd -i <$(in) && echo '};') > $(out)",
+}
+
+genrule {
+    name: "bin2c_fastdeployagentscript",
+    out: ["deployagentscript.inc"],
+    srcs: ["fastdeploy/deployagent/deployagent.sh"],
+    cmd: "(echo 'unsigned char kDeployAgentScript[] = {' && xxd -i <$(in) && echo '};') > $(out)",
+}
+
+cc_library_host_static {
+    name: "libfastdeploy_host",
+    defaults: ["adb_defaults"],
+    srcs: [
+        "fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp",
+        "fastdeploy/deploypatchgenerator/patch_utils.cpp",
+        "fastdeploy/proto/ApkEntry.proto",
+    ],
+    static_libs: [
+        "libadb_host",
+        "libandroidfw",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libmdnssd",
+        "libusb",
+        "libutils",
+        "libziparchive",
+        "libz",
+    ],
+    stl: "libc++_static",
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+    target: {
+        windows: {
+            enabled: true,
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+}
+
+cc_test_host {
+    name: "fastdeploy_test",
+    defaults: ["adb_defaults"],
+    srcs: [
+        "fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp",
+        "fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
+    ],
+    static_libs: [
+        "libadb_host",
+        "libandroidfw",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "libfastdeploy_host",
+        "liblog",
+        "libmdnssd",
+        "libprotobuf-cpp-lite",
+        "libusb",
+        "libutils",
+        "libziparchive",
+        "libz",
+    ],
+    target: {
+        windows: {
+            enabled: true,
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+    data: [
+        "fastdeploy/testdata/rotating_cube-release.apk",
+    ],
+}
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/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index f4e8664..fbae219 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -27,6 +27,9 @@
 #include "androidfw/ZipFileRO.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
+#include "deployagent.inc"        // Generated include via build rule.
+#include "deployagentscript.inc"  // Generated include via build rule.
+#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
 #include "fastdeploycallbacks.h"
 #include "sysdeps.h"
 
@@ -35,6 +38,8 @@
 static constexpr long kRequiredAgentVersion = 0x00000002;
 
 static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
+static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
 
 static bool g_use_localagent = false;
 
@@ -71,46 +76,32 @@
     g_use_localagent = use_localagent;
 }
 
-// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
-static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
-    std::string adb_dir = android::base::GetExecutableDirectory();
-    if (adb_dir.empty()) {
-        error_exit("Could not determine location of adb!");
-    }
-
-    if (g_use_localagent) {
-        const char* product_out = getenv("ANDROID_PRODUCT_OUT");
-        if (product_out == nullptr) {
-            error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
-                       local_path);
-        }
-        return android::base::StringPrintf("%s%s", product_out, local_path);
-    } else {
-        return adb_dir + sdk_path;
-    }
-}
-
 static bool deploy_agent(bool checkTimeStamps) {
     std::vector<const char*> srcs;
-    std::string jar_path =
-            get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar");
-    std::string script_path =
-            get_agent_component_host_path("/system/bin/deployagent", "/deployagent");
-    srcs.push_back(jar_path.c_str());
-    srcs.push_back(script_path.c_str());
-
-    if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
-        // on windows the shell script might have lost execute permission
-        // so need to set this explicitly
-        const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
-        std::string chmodCommand =
-                android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
-        int ret = send_shell_command(chmodCommand);
-        if (ret != 0) {
-            error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
-        }
-    } else {
-        error_exit("Error pushing agent files to device");
+    // TODO: Deploy agent from bin2c directly instead of writing to disk first.
+    TemporaryFile tempAgent;
+    android::base::WriteFully(tempAgent.fd, kDeployAgent, sizeof(kDeployAgent));
+    srcs.push_back(tempAgent.path);
+    if (!do_sync_push(srcs, kDeviceAgentFile, checkTimeStamps)) {
+        error_exit("Failed to push fastdeploy agent to device.");
+    }
+    srcs.clear();
+    // TODO: Deploy agent from bin2c directly instead of writing to disk first.
+    TemporaryFile tempAgentScript;
+    android::base::WriteFully(tempAgentScript.fd, kDeployAgentScript, sizeof(kDeployAgentScript));
+    srcs.push_back(tempAgentScript.path);
+    if (!do_sync_push(srcs, kDeviceAgentScript, checkTimeStamps)) {
+        error_exit("Failed to push fastdeploy agent script to device.");
+    }
+    srcs.clear();
+    // on windows the shell script might have lost execute permission
+    // so need to set this explicitly
+    const char* kChmodCommandPattern = "chmod 777 %s";
+    std::string chmodCommand =
+            android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
+    int ret = send_shell_command(chmodCommand);
+    if (ret != 0) {
+        error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
     }
 
     return true;
@@ -238,34 +229,15 @@
     }
 }
 
-static std::string get_patch_generator_command() {
-    if (g_use_localagent) {
-        // This should never happen on a Windows machine
-        const char* host_out = getenv("ANDROID_HOST_OUT");
-        if (host_out == nullptr) {
-            error_exit(
-                    "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
-                    "is not defined");
-        }
-        return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
-                                           host_out);
-    }
-
-    std::string adb_dir = android::base::GetExecutableDirectory();
-    if (adb_dir.empty()) {
-        error_exit("Could not locate deploypatchgenerator.jar");
-    }
-    return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
-                                       adb_dir.c_str());
-}
-
 void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
-    std::string generatePatchCommand = android::base::StringPrintf(
-            R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
-            patchPath);
-    int returnCode = system(generatePatchCommand.c_str());
-    if (returnCode != 0) {
-        error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
+    DeployPatchGenerator generator(false);
+    unique_fd patchFd(adb_open(patchPath, O_WRONLY | O_CREAT | O_CLOEXEC));
+    if (patchFd < 0) {
+        perror_exit("adb: failed to create %s", patchPath);
+    }
+    bool success = generator.CreatePatch(apkPath, metadataPath, patchFd);
+    if (!success) {
+        error_exit("Failed to create patch for %s", apkPath);
     }
 }
 
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index de97068..0fb14c4 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -222,7 +222,7 @@
 bool Subprocess::ForkAndExec(std::string* error) {
     unique_fd child_stdinout_sfd, child_stderr_sfd;
     unique_fd parent_error_sfd, child_error_sfd;
-    char pts_name[PATH_MAX];
+    const char* pts_name = nullptr;
 
     if (command_.empty()) {
         __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
@@ -283,10 +283,22 @@
     cenv.push_back(nullptr);
 
     if (type_ == SubprocessType::kPty) {
-        int fd;
-        pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
+        unique_fd pty_master(posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC));
+        if (pty_master == -1) {
+            *error =
+                    android::base::StringPrintf("failed to create pty master: %s", strerror(errno));
+            return false;
+        }
+        if (unlockpt(pty_master.get()) != 0) {
+            *error = android::base::StringPrintf("failed to unlockpt pty master: %s",
+                                                 strerror(errno));
+            return false;
+        }
+
+        pid_ = fork();
+        pts_name = ptsname(pty_master.get());
         if (pid_ > 0) {
-          stdinout_sfd_.reset(fd);
+            stdinout_sfd_ = std::move(pty_master);
         }
     } else {
         if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 1ba0de0..95e1a28 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -13,27 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-
 java_binary {
     name: "deployagent",
     sdk_version: "24",
     srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
     static_libs: ["apkzlib_zip"],
-    wrapper: "deployagent/deployagent.sh",
     proto: {
         type: "lite",
     },
     dex_preopt: {
         enabled: false,
     }
-}
-
-java_binary_host {
-    name: "deploypatchgenerator",
-    srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
-    static_libs: ["apkzlib"],
-    manifest: "deploypatchgenerator/manifest.txt",
-    proto: {
-        type: "full",
-    }
-}
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
index 2d3b135..a8103c4 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -181,7 +181,7 @@
     private static void extractMetaData(String packageName) throws IOException {
         File apkFile = getFileFromPackageName(packageName);
         APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
-        apkMetaData.writeDelimitedTo(System.out);
+        apkMetaData.writeTo(System.out);
     }
 
     private static int createInstallSession(String[] args) throws IOException {
@@ -223,7 +223,6 @@
         }
 
         int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
-
         if (writeExitCode == 0) {
             return commitInstallSession(sessionId);
         } else {
@@ -285,7 +284,6 @@
             if (oldDataLen > 0) {
                 PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
             }
-
             newDataBytesWritten += copyLen + oldDataLen;
         }
 
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
index f0f00e1..c60f9a6 100644
--- a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
@@ -39,7 +39,7 @@
 class PatchUtils {
     private static final long NEGATIVE_MASK = 1L << 63;
     private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
-    public static final String SIGNATURE = "HAMADI/IHD";
+    public static final String SIGNATURE = "FASTDEPLOY";
 
     private static long getOffsetFromEntry(StoredEntry entry) {
         return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
new file mode 100644
index 0000000..22c9243
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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 "deploy_patch_generator.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "adb_unique_fd.h"
+#include "android-base/file.h"
+#include "patch_utils.h"
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+void DeployPatchGenerator::Log(const char* fmt, ...) {
+    if (!is_verbose_) {
+        return;
+    }
+    va_list ap;
+    va_start(ap, fmt);
+    vprintf(fmt, ap);
+    printf("\n");
+    va_end(ap);
+}
+
+void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
+    Log("Filename: %s", entry.filename().c_str());
+    Log("CRC32: 0x%08llX", entry.crc32());
+    Log("Data Offset: %lld", entry.dataoffset());
+    Log("Compressed Size: %lld", entry.compressedsize());
+    Log("Uncompressed Size: %lld", entry.uncompressedsize());
+}
+
+void DeployPatchGenerator::APKMetaDataToLog(const char* file, const APKMetaData& metadata) {
+    if (!is_verbose_) {
+        return;
+    }
+    Log("APK Metadata: %s", file);
+    for (int i = 0; i < metadata.entries_size(); i++) {
+        const APKEntry& entry = metadata.entries(i);
+        APKEntryToLog(entry);
+    }
+}
+
+void DeployPatchGenerator::ReportSavings(const std::vector<SimpleEntry>& identicalEntries,
+                                         uint64_t totalSize) {
+    long totalEqualBytes = 0;
+    int totalEqualFiles = 0;
+    for (size_t i = 0; i < identicalEntries.size(); i++) {
+        if (identicalEntries[i].deviceEntry != nullptr) {
+            totalEqualBytes += identicalEntries[i].localEntry->compressedsize();
+            totalEqualFiles++;
+        }
+    }
+    float savingPercent = (totalEqualBytes * 100.0f) / totalSize;
+    fprintf(stderr, "Detected %d equal APK entries\n", totalEqualFiles);
+    fprintf(stderr, "%ld bytes are equal out of %" PRIu64 " (%.2f%%)\n", totalEqualBytes, totalSize,
+            savingPercent);
+}
+
+void DeployPatchGenerator::GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
+                                         const char* localApkPath, borrowed_fd output) {
+    unique_fd input(adb_open(localApkPath, O_RDONLY | O_CLOEXEC));
+    size_t newApkSize = adb_lseek(input, 0L, SEEK_END);
+    adb_lseek(input, 0L, SEEK_SET);
+
+    PatchUtils::WriteSignature(output);
+    PatchUtils::WriteLong(newApkSize, output);
+    size_t currentSizeOut = 0;
+    // Write data from the host upto the first entry we have that matches a device entry. Then write
+    // the metadata about the device entry and repeat for all entries that match on device. Finally
+    // write out any data left. If the device and host APKs are exactly the same this ends up
+    // writing out zip metadata from the local APK followed by offsets to the data to use from the
+    // device APK.
+    for (auto&& entry : entriesToUseOnDevice) {
+        int64_t deviceDataOffset = entry.deviceEntry->dataoffset();
+        int64_t hostDataOffset = entry.localEntry->dataoffset();
+        int64_t deviceDataLength = entry.deviceEntry->compressedsize();
+        int64_t deltaFromDeviceDataStart = hostDataOffset - currentSizeOut;
+        PatchUtils::WriteLong(deltaFromDeviceDataStart, output);
+        if (deltaFromDeviceDataStart > 0) {
+            PatchUtils::Pipe(input, output, deltaFromDeviceDataStart);
+        }
+        PatchUtils::WriteLong(deviceDataOffset, output);
+        PatchUtils::WriteLong(deviceDataLength, output);
+        adb_lseek(input, deviceDataLength, SEEK_CUR);
+        currentSizeOut += deltaFromDeviceDataStart + deviceDataLength;
+    }
+    if (currentSizeOut != newApkSize) {
+        PatchUtils::WriteLong(newApkSize - currentSizeOut, output);
+        PatchUtils::Pipe(input, output, newApkSize - currentSizeOut);
+        PatchUtils::WriteLong(0, output);
+        PatchUtils::WriteLong(0, output);
+    }
+}
+
+bool DeployPatchGenerator::CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+                                       borrowed_fd output) {
+    std::string content;
+    APKMetaData deviceApkMetadata;
+    if (android::base::ReadFileToString(deviceApkMetadataPath, &content)) {
+        deviceApkMetadata.ParsePartialFromString(content);
+    } else {
+        // TODO: What do we want to do if we don't find any metadata.
+        // The current fallback behavior is to build a patch with the contents of |localApkPath|.
+    }
+
+    APKMetaData localApkMetadata = PatchUtils::GetAPKMetaData(localApkPath);
+    // Log gathered metadata info.
+    APKMetaDataToLog(deviceApkMetadataPath, deviceApkMetadata);
+    APKMetaDataToLog(localApkPath, localApkMetadata);
+
+    std::vector<SimpleEntry> identicalEntries;
+    uint64_t totalSize =
+            BuildIdenticalEntries(identicalEntries, localApkMetadata, deviceApkMetadata);
+    ReportSavings(identicalEntries, totalSize);
+    GeneratePatch(identicalEntries, localApkPath, output);
+    return true;
+}
+
+uint64_t DeployPatchGenerator::BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
+                                                     const APKMetaData& localApkMetadata,
+                                                     const APKMetaData& deviceApkMetadata) {
+    uint64_t totalSize = 0;
+    for (int i = 0; i < localApkMetadata.entries_size(); i++) {
+        const APKEntry& localEntry = localApkMetadata.entries(i);
+        totalSize += localEntry.compressedsize();
+        for (int j = 0; j < deviceApkMetadata.entries_size(); j++) {
+            const APKEntry& deviceEntry = deviceApkMetadata.entries(j);
+            if (deviceEntry.crc32() == localEntry.crc32() &&
+                deviceEntry.filename().compare(localEntry.filename()) == 0) {
+                SimpleEntry simpleEntry;
+                simpleEntry.localEntry = const_cast<APKEntry*>(&localEntry);
+                simpleEntry.deviceEntry = const_cast<APKEntry*>(&deviceEntry);
+                APKEntryToLog(localEntry);
+                outIdenticalEntries.push_back(simpleEntry);
+                break;
+            }
+        }
+    }
+    std::sort(outIdenticalEntries.begin(), outIdenticalEntries.end(),
+              [](const SimpleEntry& lhs, const SimpleEntry& rhs) {
+                  return lhs.localEntry->dataoffset() < rhs.localEntry->dataoffset();
+              });
+    return totalSize;
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
new file mode 100644
index 0000000..30e41a5
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
@@ -0,0 +1,102 @@
+/*
+ * 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 <vector>
+
+#include "adb_unique_fd.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+/**
+ * This class is responsible for creating a patch that can be accepted by the deployagent. The
+ * patch format is documented in GeneratePatch.
+ */
+class DeployPatchGenerator {
+  public:
+    /**
+     * Simple struct to hold mapping between local metadata and device metadata.
+     */
+    struct SimpleEntry {
+        com::android::fastdeploy::APKEntry* localEntry;
+        com::android::fastdeploy::APKEntry* deviceEntry;
+    };
+
+    /**
+     * If |is_verbose| is true ApkEntries that are similar between device and host are written to
+     * the console.
+     */
+    explicit DeployPatchGenerator(bool is_verbose) : is_verbose_(is_verbose) {}
+    /**
+     * Given a |localApkPath|, and the |deviceApkMetadataPath| from an installed APK this function
+     * writes a patch to the given |output|.
+     */
+    bool CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+                     android::base::borrowed_fd output);
+
+  private:
+    bool is_verbose_;
+
+    /**
+     * Log function only logs data to stdout when |is_verbose_| is true.
+     */
+    void Log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+
+    /**
+     * Helper function to log the APKMetaData structure. If |is_verbose_| is false this function
+     * early outs. |file| is the path to the file represented by |metadata|. This function is used
+     * for debugging / information.
+     */
+    void APKMetaDataToLog(const char* file, const com::android::fastdeploy::APKMetaData& metadata);
+    /**
+     * Helper function to log APKEntry.
+     */
+    void APKEntryToLog(const com::android::fastdeploy::APKEntry& entry);
+
+    /**
+     * Helper function to report savings by fastdeploy. This function prints out savings even with
+     * |is_verbose_| set to false. |totalSize| is used to show a percentage of savings. Note:
+     * |totalSize| is the size of the ZipEntries. Not the size of the entire file. The metadata of
+     * the zip data needs to be sent across with every iteration.
+     * [Patch format]
+     * |Fixed String| Signature
+     * |long|         New Size of Apk
+     * |Packets[]|    Array of Packets
+     *
+     * [Packet Format]
+     * |long|     Size of data to use from patch
+     * |byte[]|   Patch data
+     * |long|     Offset of data to use already on device
+     * |long|     Length of data to read from device APK
+     * TODO(b/138306784): Move the patch format to a proto.
+     */
+    void ReportSavings(const std::vector<SimpleEntry>& identicalEntries, uint64_t totalSize);
+
+    /**
+     * This enumerates each entry in |entriesToUseOnDevice| and builds a patch file copying data
+     * from |localApkPath| where we are unable to use entries already on the device. The new patch
+     * is written to |output|. The entries are expected to be sorted by data offset from lowest to
+     * highest.
+     */
+    void GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
+                       const char* localApkPath, android::base::borrowed_fd output);
+
+  protected:
+    uint64_t BuildIdenticalEntries(
+            std::vector<SimpleEntry>& outIdenticalEntries,
+            const com::android::fastdeploy::APKMetaData& localApkMetadata,
+            const com::android::fastdeploy::APKMetaData& deviceApkMetadataPath);
+};
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
new file mode 100644
index 0000000..9cdc44e
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "deploy_patch_generator.h"
+#include "patch_utils.h"
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+static std::string GetTestFile(const std::string& name) {
+    return "fastdeploy/testdata/" + name;
+}
+
+class TestPatchGenerator : DeployPatchGenerator {
+  public:
+    TestPatchGenerator() : DeployPatchGenerator(false) {}
+    void GatherIdenticalEntries(std::vector<DeployPatchGenerator::SimpleEntry>& outIdenticalEntries,
+                                const APKMetaData& metadataA, const APKMetaData& metadataB) {
+        BuildIdenticalEntries(outIdenticalEntries, metadataA, metadataB);
+    }
+};
+
+TEST(DeployPatchGeneratorTest, IdenticalFileEntries) {
+    std::string apkPath = GetTestFile("rotating_cube-release.apk");
+    APKMetaData metadataA = PatchUtils::GetAPKMetaData(apkPath.c_str());
+    TestPatchGenerator generator;
+    std::vector<DeployPatchGenerator::SimpleEntry> entries;
+    generator.GatherIdenticalEntries(entries, metadataA, metadataA);
+    // Expect the entry count to match the number of entries in the metadata.
+    const uint32_t identicalCount = entries.size();
+    const uint32_t entriesCount = metadataA.entries_size();
+    EXPECT_EQ(identicalCount, entriesCount);
+}
+
+TEST(DeployPatchGeneratorTest, NoDeviceMetadata) {
+    std::string apkPath = GetTestFile("rotating_cube-release.apk");
+    // Get size of our test apk.
+    long apkSize = 0;
+    {
+        unique_fd apkFile(adb_open(apkPath.c_str(), O_RDWR));
+        apkSize = adb_lseek(apkFile, 0L, SEEK_END);
+    }
+
+    // Create a patch that is 100% different.
+    TemporaryFile output;
+    DeployPatchGenerator generator(true);
+    generator.CreatePatch(apkPath.c_str(), "", output.fd);
+
+    // Expect a patch file that has a size at least the size of our initial APK.
+    long patchSize = adb_lseek(output.fd, 0L, SEEK_END);
+    EXPECT_GT(patchSize, apkSize);
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/manifest.txt b/adb/fastdeploy/deploypatchgenerator/manifest.txt
deleted file mode 100644
index 5c00505..0000000
--- a/adb/fastdeploy/deploypatchgenerator/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.fastdeploy.DeployPatchGenerator
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
new file mode 100644
index 0000000..f11ddd1
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "patch_utils.h"
+
+#include <androidfw/ZipFileRO.h>
+#include <stdio.h>
+
+#include "adb_io.h"
+#include "android-base/endian.h"
+#include "sysdeps.h"
+
+using namespace com::android;
+using namespace com::android::fastdeploy;
+using namespace android::base;
+
+static constexpr char kSignature[] = "FASTDEPLOY";
+
+APKMetaData PatchUtils::GetAPKMetaData(const char* apkPath) {
+    APKMetaData apkMetaData;
+#undef open
+    std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+#define open ___xxx_unix_open
+    if (zipFile == nullptr) {
+        printf("Could not open %s", apkPath);
+        exit(1);
+    }
+    void* cookie;
+    if (zipFile->startIteration(&cookie)) {
+        android::ZipEntryRO entry;
+        while ((entry = zipFile->nextEntry(cookie)) != NULL) {
+            char fileName[256];
+            // Make sure we have a file name.
+            // TODO: Handle filenames longer than 256.
+            if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
+                continue;
+            }
+
+            uint32_t uncompressedSize, compressedSize, crc32;
+            int64_t dataOffset;
+            zipFile->getEntryInfo(entry, nullptr, &uncompressedSize, &compressedSize, &dataOffset,
+                                  nullptr, &crc32);
+            APKEntry* apkEntry = apkMetaData.add_entries();
+            apkEntry->set_crc32(crc32);
+            apkEntry->set_filename(fileName);
+            apkEntry->set_compressedsize(compressedSize);
+            apkEntry->set_uncompressedsize(uncompressedSize);
+            apkEntry->set_dataoffset(dataOffset);
+        }
+    }
+    return apkMetaData;
+}
+
+void PatchUtils::WriteSignature(borrowed_fd output) {
+    WriteFdExactly(output, kSignature, sizeof(kSignature) - 1);
+}
+
+void PatchUtils::WriteLong(int64_t value, borrowed_fd output) {
+    int64_t toLittleEndian = htole64(value);
+    WriteFdExactly(output, &toLittleEndian, sizeof(int64_t));
+}
+
+void PatchUtils::Pipe(borrowed_fd input, borrowed_fd output, size_t amount) {
+    constexpr static int BUFFER_SIZE = 128 * 1024;
+    char buffer[BUFFER_SIZE];
+    size_t transferAmount = 0;
+    while (transferAmount != amount) {
+        long chunkAmount =
+                amount - transferAmount > BUFFER_SIZE ? BUFFER_SIZE : amount - transferAmount;
+        long readAmount = adb_read(input, buffer, chunkAmount);
+        WriteFdExactly(output, buffer, readAmount);
+        transferAmount += readAmount;
+    }
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.h b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
new file mode 100644
index 0000000..0ebfe8f
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
@@ -0,0 +1,46 @@
+/*
+ * 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 "adb_unique_fd.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+/**
+ * Helper class that mirrors the PatchUtils from deploy agent.
+ */
+class PatchUtils {
+  public:
+    /**
+     * This function takes a local APK file and builds the APKMetaData required by the patching
+     * algorithm. The if this function has an error a string is printed to the terminal and exit(1)
+     * is called.
+     */
+    static com::android::fastdeploy::APKMetaData GetAPKMetaData(const char* file);
+    /**
+     * Writes a fixed signature string to the header of the patch.
+     */
+    static void WriteSignature(android::base::borrowed_fd output);
+    /**
+     * Writes an int64 to the |output| reversing the bytes.
+     */
+    static void WriteLong(int64_t value, android::base::borrowed_fd output);
+    /**
+     * Copy |amount| of data from |input| to |output|.
+     */
+    static void Pipe(android::base::borrowed_fd input, android::base::borrowed_fd output,
+                     size_t amount);
+};
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
new file mode 100644
index 0000000..a7eeebf
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "patch_utils.h"
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sstream>
+#include <string>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+static std::string GetTestFile(const std::string& name) {
+    return "fastdeploy/testdata/" + name;
+}
+
+bool FileMatchesContent(android::base::borrowed_fd input, const char* contents,
+                        ssize_t contentsSize) {
+    adb_lseek(input, 0, SEEK_SET);
+    // Use a temp buffer larger than any test contents.
+    constexpr int BUFFER_SIZE = 2048;
+    char buffer[BUFFER_SIZE];
+    bool result = true;
+    // Validate size of files is equal.
+    ssize_t readAmount = adb_read(input, buffer, BUFFER_SIZE);
+    EXPECT_EQ(readAmount, contentsSize);
+    result = memcmp(buffer, contents, readAmount) == 0;
+    for (int i = 0; i < readAmount; i++) {
+        printf("%x", buffer[i]);
+    }
+    printf(" == ");
+    for (int i = 0; i < contentsSize; i++) {
+        printf("%x", contents[i]);
+    }
+    printf("\n");
+
+    return result;
+}
+
+TEST(PatchUtilsTest, SwapLongWrites) {
+    TemporaryFile output;
+    PatchUtils::WriteLong(0x0011223344556677, output.fd);
+    adb_lseek(output.fd, 0, SEEK_SET);
+    const char expected[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
+    EXPECT_TRUE(FileMatchesContent(output.fd, expected, 8));
+}
+
+TEST(PatchUtilsTest, PipeWritesAmountToOutput) {
+    std::string expected("Some Data");
+    TemporaryFile input;
+    TemporaryFile output;
+    // Populate input file.
+    WriteFdExactly(input.fd, expected);
+    adb_lseek(input.fd, 0, SEEK_SET);
+    // Open input file for read, and output file for write.
+    PatchUtils::Pipe(input.fd, output.fd, expected.size());
+    // Validate pipe worked
+    EXPECT_TRUE(FileMatchesContent(output.fd, expected.c_str(), expected.size()));
+}
+
+TEST(PatchUtilsTest, SignatureConstMatches) {
+    std::string apkFile = GetTestFile("rotating_cube-release.apk");
+    TemporaryFile output;
+    PatchUtils::WriteSignature(output.fd);
+    std::string contents("FASTDEPLOY");
+    EXPECT_TRUE(FileMatchesContent(output.fd, contents.c_str(), contents.size()));
+}
+
+TEST(PatchUtilsTest, GatherMetadata) {
+    std::string apkFile = GetTestFile("rotating_cube-release.apk");
+    APKMetaData metadata = PatchUtils::GetAPKMetaData(apkFile.c_str());
+    std::string expectedMetadata;
+    android::base::ReadFileToString(GetTestFile("rotating_cube-metadata-release.data"),
+                                    &expectedMetadata);
+    std::string actualMetadata;
+    metadata.SerializeToString(&actualMetadata);
+    EXPECT_EQ(expectedMetadata, actualMetadata);
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
deleted file mode 100644
index 24b2eab..0000000
--- a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-package com.android.fastdeploy;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.StringBuilder;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.ArrayList;
-
-import java.nio.charset.StandardCharsets;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.AbstractMap.SimpleEntry;
-
-import com.android.fastdeploy.APKMetaData;
-import com.android.fastdeploy.APKEntry;
-
-public final class DeployPatchGenerator {
-    private static final int BUFFER_SIZE = 128 * 1024;
-
-    public static void main(String[] args) {
-        try {
-            if (args.length < 2) {
-                showUsage(0);
-            }
-
-            boolean verbose = false;
-            if (args.length > 2) {
-                String verboseFlag = args[2];
-                if (verboseFlag.compareTo("--verbose") == 0) {
-                    verbose = true;
-                }
-            }
-
-            StringBuilder sb = null;
-            String apkPath = args[0];
-            String deviceMetadataPath = args[1];
-            File hostFile = new File(apkPath);
-
-            List<APKEntry> deviceZipEntries = getMetadataFromFile(deviceMetadataPath);
-            System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
-            if (verbose) {
-                sb = new StringBuilder();
-                for (APKEntry entry : deviceZipEntries) {
-                    APKEntryToString(entry, sb);
-                }
-                System.err.println(sb.toString());
-            }
-
-            List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
-            System.err.println("Host Entries (" + hostFileEntries.size() + ")");
-            if (verbose) {
-                sb = new StringBuilder();
-                for (APKEntry entry : hostFileEntries) {
-                    APKEntryToString(entry, sb);
-                }
-                System.err.println(sb.toString());
-            }
-
-            List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet =
-                getIdenticalContents(deviceZipEntries, hostFileEntries);
-            reportIdenticalContents(identicalContentsEntrySet, hostFile);
-
-            if (verbose) {
-                sb = new StringBuilder();
-                for (SimpleEntry<APKEntry, APKEntry> identicalEntry : identicalContentsEntrySet) {
-                    APKEntry entry = identicalEntry.getValue();
-                    APKEntryToString(entry, sb);
-                }
-                System.err.println("Identical Entries (" + identicalContentsEntrySet.size() + ")");
-                System.err.println(sb.toString());
-            }
-
-            createPatch(identicalContentsEntrySet, hostFile, System.out);
-        } catch (Exception e) {
-            System.err.println("Error: " + e);
-            e.printStackTrace();
-            System.exit(2);
-        }
-        System.exit(0);
-    }
-
-    private static void showUsage(int exitCode) {
-        System.err.println("usage: deploypatchgenerator <apkpath> <deviceapkmetadata> [--verbose]");
-        System.err.println("");
-        System.exit(exitCode);
-    }
-
-    private static void APKEntryToString(APKEntry entry, StringBuilder outputString) {
-        outputString.append(String.format("Filename: %s\n", entry.getFileName()));
-        outputString.append(String.format("CRC32: 0x%08X\n", entry.getCrc32()));
-        outputString.append(String.format("Data Offset: %d\n", entry.getDataOffset()));
-        outputString.append(String.format("Compressed Size: %d\n", entry.getCompressedSize()));
-        outputString.append(String.format("Uncompressed Size: %d\n", entry.getUncompressedSize()));
-    }
-
-    private static List<APKEntry> getMetadataFromFile(String deviceMetadataPath) throws IOException {
-        InputStream is = new FileInputStream(new File(deviceMetadataPath));
-        APKMetaData apkMetaData = APKMetaData.parseDelimitedFrom(is);
-        return apkMetaData.getEntriesList();
-    }
-
-    private static List<SimpleEntry<APKEntry, APKEntry>> getIdenticalContents(
-        List<APKEntry> deviceZipEntries, List<APKEntry> hostZipEntries) throws IOException {
-        List<SimpleEntry<APKEntry, APKEntry>> identicalContents =
-            new ArrayList<SimpleEntry<APKEntry, APKEntry>>();
-
-        for (APKEntry deviceZipEntry : deviceZipEntries) {
-            for (APKEntry hostZipEntry : hostZipEntries) {
-                if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32() &&
-                    deviceZipEntry.getFileName().equals(hostZipEntry.getFileName())) {
-                    identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
-                }
-            }
-        }
-
-        Collections.sort(identicalContents, new Comparator<SimpleEntry<APKEntry, APKEntry>>() {
-            @Override
-            public int compare(
-                SimpleEntry<APKEntry, APKEntry> p1, SimpleEntry<APKEntry, APKEntry> p2) {
-                return Long.compare(p1.getValue().getDataOffset(), p2.getValue().getDataOffset());
-            }
-        });
-
-        return identicalContents;
-    }
-
-    private static void reportIdenticalContents(
-        List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet, File hostFile)
-        throws IOException {
-        long totalEqualBytes = 0;
-        int totalEqualFiles = 0;
-        for (SimpleEntry<APKEntry, APKEntry> entries : identicalContentsEntrySet) {
-            APKEntry hostAPKEntry = entries.getValue();
-            totalEqualBytes += hostAPKEntry.getCompressedSize();
-            totalEqualFiles++;
-        }
-
-        float savingPercent = (float) (totalEqualBytes * 100) / hostFile.length();
-
-        System.err.println("Detected " + totalEqualFiles + " equal APK entries");
-        System.err.println(totalEqualBytes + " bytes are equal out of " + hostFile.length() + " ("
-            + savingPercent + "%)");
-    }
-
-    static void createPatch(List<SimpleEntry<APKEntry, APKEntry>> zipEntrySimpleEntrys,
-        File hostFile, OutputStream patchStream) throws IOException, PatchFormatException {
-        FileInputStream hostFileInputStream = new FileInputStream(hostFile);
-
-        patchStream.write(PatchUtils.SIGNATURE.getBytes(StandardCharsets.US_ASCII));
-        PatchUtils.writeFormattedLong(hostFile.length(), patchStream);
-
-        byte[] buffer = new byte[BUFFER_SIZE];
-        long totalBytesWritten = 0;
-        Iterator<SimpleEntry<APKEntry, APKEntry>> entrySimpleEntryIterator =
-            zipEntrySimpleEntrys.iterator();
-        while (entrySimpleEntryIterator.hasNext()) {
-            SimpleEntry<APKEntry, APKEntry> entrySimpleEntry = entrySimpleEntryIterator.next();
-            APKEntry deviceAPKEntry = entrySimpleEntry.getKey();
-            APKEntry hostAPKEntry = entrySimpleEntry.getValue();
-
-            long newDataLen = hostAPKEntry.getDataOffset() - totalBytesWritten;
-            long oldDataOffset = deviceAPKEntry.getDataOffset();
-            long oldDataLen = deviceAPKEntry.getCompressedSize();
-
-            PatchUtils.writeFormattedLong(newDataLen, patchStream);
-            PatchUtils.pipe(hostFileInputStream, patchStream, buffer, newDataLen);
-            PatchUtils.writeFormattedLong(oldDataOffset, patchStream);
-            PatchUtils.writeFormattedLong(oldDataLen, patchStream);
-
-            long skip = hostFileInputStream.skip(oldDataLen);
-            if (skip != oldDataLen) {
-                throw new PatchFormatException("skip error: attempted to skip " + oldDataLen
-                    + " bytes but return code was " + skip);
-            }
-            totalBytesWritten += oldDataLen + newDataLen;
-        }
-        long remainderLen = hostFile.length() - totalBytesWritten;
-        PatchUtils.writeFormattedLong(remainderLen, patchStream);
-        PatchUtils.pipe(hostFileInputStream, patchStream, buffer, remainderLen);
-        PatchUtils.writeFormattedLong(0, patchStream);
-        PatchUtils.writeFormattedLong(0, patchStream);
-        patchStream.flush();
-    }
-}
diff --git a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
new file mode 100644
index 0000000..0671bf3
--- /dev/null
+++ b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
@@ -0,0 +1,6 @@
+
+#ǂϫMETA-INF/MANIFEST.MFÇ Q(W
+#ƒ•ŽAndroidManifest.xml1 ä(è
+6¦µ€>#lib/armeabi-v7a/libvulkan_sample.so ÀÒQ(Œ²ì
+ —ã—‘resources.arscôàQ ´(´
+‹œÂÉclasses.dexÁ ÿ(ô
diff --git a/adb/fastdeploy/testdata/rotating_cube-release.apk b/adb/fastdeploy/testdata/rotating_cube-release.apk
new file mode 100644
index 0000000..d47e0ea
--- /dev/null
+++ b/adb/fastdeploy/testdata/rotating_cube-release.apk
Binary files differ
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/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 0ad8d9d..a4e0d76 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -150,6 +150,15 @@
     return true;
 }
 
+std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        return std::nullopt;
+    }
+    return Info(io.flags);
+}
+
 DmDeviceState DeviceMapper::GetState(const std::string& name) const {
     struct dm_ioctl io;
     InitIo(&io, name);
@@ -162,6 +171,24 @@
     return DmDeviceState::SUSPENDED;
 }
 
+bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
+    if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
+        return false;
+    }
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
+
+    if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
+        PLOG(ERROR) << "DM_DEV_SUSPEND "
+                    << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
+        return false;
+    }
+    return true;
+}
+
 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
     std::string ignore_path;
     if (!CreateDevice(name, table, &ignore_path, 0ms)) {
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index da1013e..1a483ec 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>
 
@@ -165,35 +166,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 b28a8f2..16be0d5 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -166,6 +166,34 @@
     ASSERT_TRUE(dev.Destroy());
 }
 
+TEST(libdm, DmSuspendResume) {
+    unique_fd tmp1(CreateTempFile("file_suspend_resume", 512));
+    ASSERT_GE(tmp1, 0);
+
+    LoopDevice loop_a(tmp1, 10s);
+    ASSERT_TRUE(loop_a.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-suspend-resume", table);
+    ASSERT_TRUE(dev.valid());
+    ASSERT_FALSE(dev.path().empty());
+
+    auto& dm = DeviceMapper::Instance();
+
+    // Test Set and Get status of device.
+    vector<DeviceMapper::TargetInfo> targets;
+    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
+
+    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::SUSPENDED));
+    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::SUSPENDED);
+
+    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::ACTIVE));
+    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
+}
+
 TEST(libdm, DmVerityArgsAvb2) {
     std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
     std::string algorithm = "sha1";
@@ -428,6 +456,33 @@
     }
 }
 
+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, 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.h b/fs_mgr/libdm/include/libdm/dm.h
index 9c0c2f3..c6b37cf 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -27,6 +27,7 @@
 
 #include <chrono>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -70,15 +71,37 @@
         uint64_t dev_;
     };
 
+    class Info {
+        uint32_t flags_;
+
+      public:
+        explicit Info(uint32_t flags) : flags_(flags) {}
+
+        bool IsActiveTablePresent() const { return flags_ & DM_ACTIVE_PRESENT_FLAG; }
+        bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; }
+        bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; }
+        bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; }
+        bool IsSuspended() const { return flags_ & DM_SUSPEND_FLAG; }
+    };
+
     // Removes a device mapper device with the given name.
     // Returns 'true' on success, false otherwise.
     bool DeleteDevice(const std::string& name);
 
+    // Fetches and returns the complete state of the underlying device mapper
+    // device with given name.
+    std::optional<Info> GetDetailedInfo(const std::string& name) const;
+
     // Returns the current state of the underlying device mapper device
     // with given name.
     // One of INVALID, SUSPENDED or ACTIVE.
     DmDeviceState GetState(const std::string& name) const;
 
+    // Puts the given device to the specified status, which must be either:
+    // - SUSPENDED: suspend the device, or
+    // - ACTIVE: resumes the device.
+    bool ChangeState(const std::string& name, DmDeviceState state);
+
     // Creates a device, loads the given table, and activates it. If the device
     // is not able to be activated, it is destroyed, and false is returned.
     // After creation, |path| contains the result of calling
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index c12e3b2..8797ea9 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -1060,7 +1060,7 @@
     if (sRetrofitDap.has_value()) {
         return *sRetrofitDap;
     }
-    return !android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false);
+    return android::base::GetBoolProperty("ro.boot.dynamic_partitions_retrofit", false);
 }
 
 bool MetadataBuilder::IsRetrofitMetadata() const {
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index c2a0f33..642f2c1 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -15,10 +15,13 @@
 
 adb remount tests
 
---help        This help
---serial      Specify device (must if multiple are present)
---color       Dress output with highlighting colors
---print-time  Report the test duration
+--color                     Dress output with highlighting colors
+--help                      This help
+--no-wait-screen            Do not wait for display screen to settle
+--print-time                Report the test duration
+--serial                    Specify device (must if multiple are present)
+--wait-adb <duration>       adb wait timeout
+--wait-fastboot <duration>  fastboot wait timeout
 
 Conditions:
  - Must be a userdebug build.
@@ -53,6 +56,7 @@
 
 ADB_WAIT=4m
 FASTBOOT_WAIT=2m
+screen_wait=true
 
 ##
 ##  Helper Functions
@@ -185,7 +189,7 @@
 [ "USAGE: adb_cat <file> >stdout
 
 Returns: content of file to stdout with carriage returns skipped,
-         true of the file exists" ]
+         true if the file exists" ]
 adb_cat() {
     local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
     local ret=${?}
@@ -193,6 +197,17 @@
     return ${ret}
 }
 
+[ "USAGE: adb_ls <dirfile> >stdout
+
+Returns: filename or directoru content to stdout with carriage returns skipped,
+         true if the ls had no errors" ]
+adb_ls() {
+    local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
+    local ret=${?}
+    echo "${OUTPUT}" | tr -d '\r'
+    return ${ret}
+}
+
 [ "USAGE: adb_reboot
 
 Returns: true if the reboot command succeeded" ]
@@ -436,6 +451,10 @@
 -n - echo newline at exit
 TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
 wait_for_screen() {
+  if ! ${screen_wait}; then
+    adb_wait
+    return
+  fi
   exit_function=true
   if [ X"-n" = X"${1}" ]; then
     exit_function=echo
@@ -743,6 +762,9 @@
 
 OPTIONS=`getopt --alternative --unquoted \
                 --longoptions help,serial:,colour,color,no-colour,no-color \
+                --longoptions wait-adb:,wait-fastboot: \
+                --longoptions wait-screen,wait-display \
+                --longoptions no-wait-screen,no-wait-display \
                 --longoptions gtest_print_time,print-time \
                 -- "?hs:" ${*}` ||
   ( echo "${USAGE}" >&2 ; false ) ||
@@ -766,9 +788,23 @@
     --no-color | --no-colour)
       color=false
       ;;
+    --no-wait-display | --no-wait-screen)
+      screen_wait=false
+      ;;
+    --wait-display | --wait-screen)
+      screen_wait=true
+      ;;
     --print-time | --gtest_print_time)
       print_time=true
       ;;
+    --wait-adb)
+      ADB_WAIT=${2}
+      shift
+      ;;
+    --wait-fastboot)
+      FASTBOOT_WAIT=${2}
+      shift
+      ;;
     --)
       shift
       break
@@ -1145,10 +1181,14 @@
 
 A="Hello World! $(date)"
 echo "${A}" | adb_sh cat - ">/system/hello"
+echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
 echo "${A}" | adb_sh cat - ">/vendor/hello"
 B="`adb_cat /system/hello`" ||
-  die "sytem hello"
+  die "system hello"
 check_eq "${A}" "${B}" /system before reboot
+B="`adb_cat /system/priv-app/hello`" ||
+  die "system priv-app hello"
+check_eq "${A}" "${B}" /system/priv-app before reboot
 B="`adb_cat /vendor/hello`" ||
   die "vendor hello"
 check_eq "${A}" "${B}" /vendor before reboot
@@ -1230,6 +1270,13 @@
 fi
 B="`adb_cat /system/hello`"
 check_eq "${A}" "${B}" /system after reboot
+# If overlayfs has a nested security problem, this will fail.
+B="`adb_ls /system/`" ||
+  dir "adb ls /system"
+[ X"${B}" != X"${B#*priv-app}" ] ||
+  dir "adb ls /system/priv-app"
+B="`adb_cat /system/priv-app/hello`"
+check_eq "${A}" "${B}" /system/priv-app after reboot
 echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
 # Only root can read vendor if sepolicy permissions are as expected.
 adb_root ||
@@ -1351,6 +1398,12 @@
   fi
   B="`adb_cat /system/hello`"
   check_eq "${A}" "${B}" system after flash vendor
+  B="`adb_ls /system/`" ||
+    dir "adb ls /system"
+  [ X"${B}" != X"${B#*priv-app}" ] ||
+    dir "adb ls /system/priv-app"
+  B="`adb_cat /system/priv-app/hello`"
+  check_eq "${A}" "${B}" system/priv-app after flash vendor
   adb_root ||
     die "adb root"
   B="`adb_cat /vendor/hello`"
@@ -1392,15 +1445,17 @@
 echo "${H}"
 [ ${err} = 0 ] &&
   ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
-  adb_sh rm /system/hello </dev/null ||
+  adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
   ( [ -n "${L}" ] && echo "${L}" && false ) ||
   die -t ${T} "cleanup hello"
 B="`adb_cat /system/hello`"
 check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /system/priv-app/hello`"
+check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
 B="`adb_cat /vendor/hello`"
 check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
 
-if [ -n "${scratch_partition}" ]; then
+if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
 
   echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
 
@@ -1542,7 +1597,9 @@
     adb_wait ${ADB_WAIT} ||
     die "adb remount -R"
   if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-       "2" = "`get_property partition.system.verified`" ]; then
+       "2" = "`get_property partition.system.verified`" ] &&
+     [ -n "`get_property ro.boot.verifiedbootstate`" -o \
+       -n "`get_property partition.system.verified`" ]; then
     die "remount -R command failed to disable verity"
   fi
 
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7e6ad5b..4fb0b83 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -49,7 +49,10 @@
     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;
     std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
     std::cerr << std::endl;
@@ -357,6 +360,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;
@@ -399,6 +437,34 @@
     return DumpTable("status", argc, argv);
 }
 
+static int ResumeCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {
+        std::cerr << "Could not resume device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int SuspendCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {
+        std::cerr << "Could not suspend device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    return 0;
+}
+
 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
         // clang-format off
         {"create", DmCreateCmdHandler},
@@ -406,8 +472,11 @@
         {"list", DmListCmdHandler},
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
+        {"info", InfoCmdHandler},
         {"table", TableCmdHandler},
         {"status", StatusCmdHandler},
+        {"resume", ResumeCmdHandler},
+        {"suspend", SuspendCmdHandler},
         // clang-format on
 };
 
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/libion/OWNERS b/libion/OWNERS
new file mode 100644
index 0000000..143ad2d
--- /dev/null
+++ b/libion/OWNERS
@@ -0,0 +1,2 @@
+sspatil@google.com
+hridya@google.com
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(