Add missing transitive dependency to pdx_tool.
am: 331d79d26b

Change-Id: Ie16dec279c96724cb496188d90357a0fa5d39d34
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 9cdc9e9..a48dab9 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -481,8 +481,8 @@
         newClock = "global";
     }
 
-    size_t begin = clockStr.find("[") + 1;
-    size_t end = clockStr.find("]");
+    size_t begin = clockStr.find('[') + 1;
+    size_t end = clockStr.find(']');
     if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) {
         return true;
     }
@@ -543,7 +543,7 @@
     auto listRet = sm->list([&](const auto &interfaces) {
         for (size_t i = 0; i < interfaces.size(); i++) {
             string fqInstanceName = interfaces[i];
-            string::size_type n = fqInstanceName.find("/");
+            string::size_type n = fqInstanceName.find('/');
             if (n == std::string::npos || interfaces[i].size() == n+1)
                 continue;
             hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp
index e866b8b..ea7109b 100644
--- a/cmds/dumpstate/DumpstateUtil.cpp
+++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -103,6 +103,12 @@
     return *this;
 }
 
+CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() {
+    if (!PropertiesHelper::IsUserBuild())
+        values.account_mode_ = SU_ROOT;
+    return *this;
+}
+
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
     values.account_mode_ = DROP_ROOT;
     return *this;
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
index 5a8ce5b..698ceff 100644
--- a/cmds/dumpstate/DumpstateUtil.h
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -89,6 +89,8 @@
         CommandOptionsBuilder& Always();
         /* Sets the command's PrivilegeMode as `SU_ROOT` */
         CommandOptionsBuilder& AsRoot();
+        /* If !IsUserBuild(), sets the command's PrivilegeMode as `SU_ROOT` */
+        CommandOptionsBuilder& AsRootIfAvailable();
         /* Sets the command's PrivilegeMode as `DROP_ROOT` */
         CommandOptionsBuilder& DropRoot();
         /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 5f93e99..79397d2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -697,7 +697,7 @@
     std::string valid_name = entry_name;
 
     // Rename extension if necessary.
-    size_t idx = entry_name.rfind(".");
+    size_t idx = entry_name.rfind('.');
     if (idx != std::string::npos) {
         std::string extension = entry_name.substr(idx);
         std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
@@ -1069,14 +1069,15 @@
         RunCommand(
                 "HARDWARE HALS",
                 {"lshal", std::string("--debug=") + kLsHalDebugPath},
-                CommandOptions::AS_ROOT);
+                CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
 
         ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath);
 
         unlink(kLsHalDebugPath.c_str());
     } else {
         RunCommand(
-                "HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT);
+                "HARDWARE HALS", {"lshal", "--debug"},
+                CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
     }
 
     RunCommand("PRINTENV", {"printenv"});
@@ -1438,7 +1439,7 @@
     return true;
 }
 
-static std::string SHA256_file_hash(std::string filepath) {
+static std::string SHA256_file_hash(const std::string& filepath) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
             | O_CLOEXEC | O_NOFOLLOW)));
     if (fd == -1) {
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a94cf99..92b0c0d 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -477,6 +477,48 @@
     EXPECT_THAT(err, StrEq("stderr\n"));
 }
 
+TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n")
+        return;
+    }
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n")
+        return;
+    }
+    if (PropertiesHelper::IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
 TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
     EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
     EXPECT_THAT(out,
@@ -1053,6 +1095,51 @@
     EXPECT_THAT(err, StrEq("stderr\n"));
 }
 
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnUserBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandAsRootIfAvailableOnUserBuild.txt");
+    if (!PropertiesHelper::IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnDebugBuild) {
+    if (!IsStandalone()) {
+        // TODO: temporarily disabled because it might cause other tests to fail after dropping
+        // to Shell - need to refactor tests to avoid this problem)
+        MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n")
+        return;
+    }
+    CreateFd("RunCommandAsRootIfAvailableOnDebugBuild.txt");
+    if (PropertiesHelper::IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRootIfAvailable().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
 TEST_F(DumpstateUtilTest, RunCommandDropRoot) {
     if (!IsStandalone()) {
         // TODO: temporarily disabled because it might cause other tests to fail after dropping
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index fa6f6df..3227749 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -177,7 +177,7 @@
     }
 
     for (size_t i = 0; i < N; i++) {
-        String16 service_name = std::move(services[i]);
+        const String16& service_name = std::move(services[i]);
         if (IsSkipped(skippedServices, service_name)) continue;
 
         sp<IBinder> service = sm_->checkService(service_name);
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index cfd53e5..16fefe6 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -96,7 +96,7 @@
     }
     int i = 0;
     std::ostringstream actual_stream, expected_stream;
-    for (String16 actual : arg) {
+    for (const String16& actual : arg) {
         std::string actual_str = String8(actual).c_str();
         std::string expected_str = expected[i];
         actual_stream << "'" << actual_str << "' ";
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp
index 3eb39b9..ea0cd9e 100644
--- a/cmds/installd/CacheTracker.cpp
+++ b/cmds/installd/CacheTracker.cpp
@@ -60,7 +60,7 @@
 
     ATRACE_BEGIN("loadStats tree");
     cacheUsed = 0;
-    for (auto path : mDataPaths) {
+    for (const auto& path : mDataPaths) {
         auto cachePath = read_path_inode(path, "cache", kXattrInodeCache);
         auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
         calculate_tree_size(cachePath, &cacheUsed);
@@ -170,7 +170,7 @@
     items.clear();
 
     ATRACE_BEGIN("loadItems");
-    for (auto path : mDataPaths) {
+    for (const auto& path : mDataPaths) {
         loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache));
         loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache));
     }
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 8a79ee1..4a93b1f 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -525,6 +525,9 @@
         if (access(path.c_str(), F_OK) == 0) {
             if (delete_dir_contents(path) != 0) {
                 res = error("Failed to delete contents of " + path);
+            } else if ((flags & (FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE_ONLY)) == 0) {
+                remove_path_xattr(path, kXattrInodeCache);
+                remove_path_xattr(path, kXattrInodeCodeCache);
             }
         }
     }
@@ -1349,7 +1352,7 @@
         const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
-    for (auto packageName : packageNames) {
+    for (const auto& packageName : packageNames) {
         CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     }
     // NOTE: Locking is relaxed on this method, since it's limited to
@@ -1388,7 +1391,7 @@
     }
 
     ATRACE_BEGIN("obb");
-    for (auto packageName : packageNames) {
+    for (const auto& packageName : packageNames) {
         auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
         calculate_tree_size(obbCodePath, &extStats.codeSize);
     }
@@ -1396,7 +1399,7 @@
 
     if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
         ATRACE_BEGIN("code");
-        for (auto codePath : codePaths) {
+        for (const auto& codePath : codePaths) {
             calculate_tree_size(codePath, &stats.codeSize, -1,
                     multiuser_get_shared_gid(0, appId));
         }
@@ -1407,7 +1410,7 @@
         ATRACE_END();
     } else {
         ATRACE_BEGIN("code");
-        for (auto codePath : codePaths) {
+        for (const auto& codePath : codePaths) {
             calculate_tree_size(codePath, &stats.codeSize);
         }
         ATRACE_END();
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f29da17..6f7ab6b 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -67,6 +68,10 @@
     return unique_fd(-1);
 }
 
+static bool is_debug_runtime() {
+    return android::base::GetProperty("persist.sys.dalvik.vm.lib.2", "") == "libartd.so";
+}
+
 static bool clear_profile(const std::string& profile) {
     unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
     if (ufd.get() < 0) {
@@ -267,7 +272,8 @@
                 dex2oat_large_app_threshold);
     }
 
-    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
+    // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
+    const char* dex2oat_bin = is_debug_runtime() ? "/system/bin/dex2oatd" : "/system/bin/dex2oat";
 
     static const char* RUNTIME_ARG = "--runtime-arg";
 
@@ -376,7 +382,7 @@
     }
 
 
-    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name);
+    ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
 
     const char* argv[9  // program name, mandatory arguments and the final NULL
                      + (have_dex2oat_isa_variant ? 1 : 0)
@@ -397,7 +403,7 @@
                      + (has_base_dir ? 1 : 0)
                      + (have_dex2oat_large_app_threshold ? 1 : 0)];
     int i = 0;
-    argv[i++] = DEX2OAT_BIN;
+    argv[i++] = dex2oat_bin;
     argv[i++] = zip_fd_arg;
     argv[i++] = zip_location_arg;
     argv[i++] = input_vdex_fd_arg;
@@ -463,8 +469,8 @@
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
 
-    execv(DEX2OAT_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
+    execv(dex2oat_bin, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", dex2oat_bin, strerror(errno));
 }
 
 /*
@@ -643,7 +649,7 @@
 static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
         const unique_fd& reference_profile_fd) {
     static const size_t MAX_INT_LEN = 32;
-    static const char* PROFMAN_BIN = "/system/bin/profman";
+    const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
 
     std::vector<std::string> profile_args(profiles_fd.size());
     char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
@@ -657,7 +663,7 @@
     // program name, reference profile fd, the final NULL and the profile fds
     const char* argv[3 + profiles_fd.size()];
     int i = 0;
-    argv[i++] = PROFMAN_BIN;
+    argv[i++] = profman_bin;
     argv[i++] = reference_profile_arg;
     for (size_t k = 0; k < profile_args.size(); k++) {
         argv[i++] = profile_args[k].c_str();
@@ -665,8 +671,8 @@
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
 
-    execv(PROFMAN_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+    execv(profman_bin, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", profman_bin, strerror(errno));
     exit(68);   /* only get here on exec failure */
 }
 
@@ -1371,7 +1377,10 @@
 // the profile has changed.
 static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& instruction_set,
         const std::string& compiler_filter, bool profile_was_updated, bool downgrade) {
-    static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer";
+    const char* dexoptanalyzer_bin =
+            is_debug_runtime()
+                    ? "/system/bin/dexoptanalyzerd"
+                    : "/system/bin/dexoptanalyzer";
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
     if (instruction_set.size() >= MAX_INSTRUCTION_SET_LEN) {
@@ -1392,7 +1401,7 @@
         (downgrade ? 1 : 0);
     const char* argv[argc];
     int i = 0;
-    argv[i++] = DEXOPTANALYZER_BIN;
+    argv[i++] = dexoptanalyzer_bin;
     argv[i++] = dex_file_arg.c_str();
     argv[i++] = isa_arg.c_str();
     argv[i++] = compiler_filter_arg.c_str();
@@ -1404,8 +1413,8 @@
     }
     argv[i] = NULL;
 
-    execv(DEXOPTANALYZER_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno));
+    execv(dexoptanalyzer_bin, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", dexoptanalyzer_bin, strerror(errno));
 }
 
 // Prepares the oat dir for the secondary dex files.
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index dd32ac6..93d5c0b 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -756,6 +756,12 @@
     }
 }
 
+void remove_path_xattr(const std::string& path, const char* inode_xattr) {
+    if (removexattr(path.c_str(), inode_xattr) && errno != ENODATA) {
+        PLOG(ERROR) << "Failed to remove xattr " << inode_xattr << " at " << path;
+    }
+}
+
 /**
  * Validate that the path is valid in the context of the provided directory.
  * The path is allowed to have at most one subdirectory and no indirections
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index e938042..a1d8443 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -122,6 +122,7 @@
 
 int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
 std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
+void remove_path_xattr(const std::string& path, const char* inode_xattr);
 
 int validate_system_app_path(const char* path);
 bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 67b5b46..6cbe7e2 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -20,14 +20,18 @@
         "libutils",
         "libhidlbase",
         "libhidltransport",
+        "libhidl-gen-hash",
         "libhidl-gen-utils",
         "libvintf",
     ],
     srcs: [
         "DebugCommand.cpp",
+        "HelpCommand.cpp",
         "Lshal.cpp",
         "ListCommand.cpp",
         "PipeRelay.cpp",
+        "TableEntry.cpp",
+        "TextTable.cpp",
         "utils.cpp",
     ],
 }
@@ -59,6 +63,7 @@
         "libgmock"
     ],
     shared_libs: [
+        "libvintf",
         "android.hardware.tests.baz@1.0"
     ],
     srcs: [
diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h
new file mode 100644
index 0000000..4f128ab
--- /dev/null
+++ b/cmds/lshal/Command.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_
+
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+// Base class for all *Commands
+class Command {
+public:
+    Command(Lshal& lshal) : mLshal(lshal) {}
+    virtual ~Command() = default;
+    // Expect optind to be set by Lshal::main and points to the next argument
+    // to process.
+    virtual Status main(const Arg &arg) = 0;
+
+    virtual void usage() const = 0;
+
+    // e.g. "list"
+    virtual std::string getName() const = 0;
+
+    // e.g. "list HALs"
+    virtual std::string getSimpleDescription() const = 0;
+
+protected:
+    Lshal& mLshal;
+};
+
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp
index 672cad6..622f866 100644
--- a/cmds/lshal/DebugCommand.cpp
+++ b/cmds/lshal/DebugCommand.cpp
@@ -21,12 +21,16 @@
 namespace android {
 namespace lshal {
 
-DebugCommand::DebugCommand(Lshal &lshal) : mLshal(lshal) {
+std::string DebugCommand::getName() const {
+    return "debug";
 }
 
-Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) {
+std::string DebugCommand::getSimpleDescription() const {
+    return "Debug a specified HAL.";
+}
+
+Status DebugCommand::parseArgs(const Arg &arg) {
     if (optind >= arg.argc) {
-        mLshal.usage(command);
         return USAGE;
     }
     mInterfaceName = arg.argv[optind];
@@ -37,8 +41,8 @@
     return OK;
 }
 
-Status DebugCommand::main(const std::string &command, const Arg &arg) {
-    Status status = parseArgs(command, arg);
+Status DebugCommand::main(const Arg &arg) {
+    Status status = parseArgs(arg);
     if (status != OK) {
         return status;
     }
@@ -49,6 +53,19 @@
             mLshal.err());
 }
 
+void DebugCommand::usage() const {
+
+    static const std::string debug =
+            "debug:\n"
+            "    lshal debug <interface> [options [options [...]]] \n"
+            "        Print debug information of a specified interface.\n"
+            "        <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n"
+            "            If instance name is missing `default` is used.\n"
+            "        options: space separated options to IBase::debug.\n";
+
+    mLshal.err() << debug;
+}
+
 }  // namespace lshal
 }  // namespace android
 
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h
index fa0f0fa..9b91084 100644
--- a/cmds/lshal/DebugCommand.h
+++ b/cmds/lshal/DebugCommand.h
@@ -21,6 +21,7 @@
 
 #include <android-base/macros.h>
 
+#include "Command.h"
 #include "utils.h"
 
 namespace android {
@@ -28,14 +29,17 @@
 
 class Lshal;
 
-class DebugCommand {
+class DebugCommand : public Command {
 public:
-    DebugCommand(Lshal &lshal);
-    Status main(const std::string &command, const Arg &arg);
+    DebugCommand(Lshal &lshal) : Command(lshal) {}
+    ~DebugCommand() = default;
+    Status main(const Arg &arg) override;
+    void usage() const override;
+    std::string getSimpleDescription() const override;
+    std::string getName() const override;
 private:
-    Status parseArgs(const std::string &command, const Arg &arg);
+    Status parseArgs(const Arg &arg);
 
-    Lshal &mLshal;
     std::string mInterfaceName;
     std::vector<std::string> mOptions;
 
diff --git a/cmds/lshal/HelpCommand.cpp b/cmds/lshal/HelpCommand.cpp
new file mode 100644
index 0000000..6773ace
--- /dev/null
+++ b/cmds/lshal/HelpCommand.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HelpCommand.h"
+
+#include "Lshal.h"
+
+namespace android {
+namespace lshal {
+
+std::string HelpCommand::GetName() {
+    return "help";
+}
+
+std::string HelpCommand::getSimpleDescription() const {
+    return "Print help message.";
+}
+
+Status HelpCommand::main(const Arg &arg) {
+    if (optind >= arg.argc) {
+        // `lshal help` prints global usage.
+        mLshal.usage();
+        return OK;
+    }
+    (void)usageOfCommand(arg.argv[optind]);
+    return OK;
+}
+
+Status HelpCommand::usageOfCommand(const std::string& c) const {
+    if (c.empty()) {
+        mLshal.usage();
+        return USAGE;
+    }
+    auto command = mLshal.selectCommand(c);
+    if (command == nullptr) {
+        // from HelpCommand::main, `lshal help unknown`
+        mLshal.usage();
+        return USAGE;
+    }
+
+    command->usage();
+    return USAGE;
+
+}
+
+void HelpCommand::usage() const {
+    mLshal.err()
+            << "help:" << std::endl
+            << "    lshal -h" << std::endl
+            << "    lshal --help" << std::endl
+            << "    lshal help" << std::endl
+            << "        Print this help message" << std::endl;
+    mLshal.forEachCommand([&](const Command* e) {
+        mLshal.err() << "    lshal help " << e->getName() << std::endl
+                     << "        Print help message for " << e->getName() << std::endl;
+    });
+
+}
+
+}  // namespace lshal
+}  // namespace android
+
diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h
new file mode 100644
index 0000000..cc709f8
--- /dev/null
+++ b/cmds/lshal/HelpCommand.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_
+
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "Command.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+class Lshal;
+
+class HelpCommand : public Command {
+public:
+    HelpCommand(Lshal &lshal) : Command(lshal) {}
+    ~HelpCommand() = default;
+    Status main(const Arg &arg) override;
+    void usage() const override;
+    std::string getSimpleDescription() const override;
+    std::string getName() const override { return GetName(); }
+    static std::string GetName();
+    Status usageOfCommand(const std::string& c) const;
+};
+
+
+}  // namespace lshal
+}  // namespace android
+
+#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 7c6cfd9..7399692 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -27,6 +27,7 @@
 
 #include <android-base/parseint.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl-hash/Hash.h>
 #include <hidl-util/FQName.h>
 #include <private/android_filesystem_config.h>
 #include <sys/stat.h>
@@ -39,15 +40,30 @@
 #include "utils.h"
 
 using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hidl::base::V1_0::DebugInfo;
+using ::android::hidl::base::V1_0::IBase;
 using ::android::hidl::manager::V1_0::IServiceManager;
 
 namespace android {
 namespace lshal {
 
-ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) {
+NullableOStream<std::ostream> ListCommand::out() const {
+    return mLshal.out();
 }
 
-std::string getCmdline(pid_t pid) {
+NullableOStream<std::ostream> ListCommand::err() const {
+    return mLshal.err();
+}
+
+std::string ListCommand::GetName() {
+    return "list";
+}
+std::string ListCommand::getSimpleDescription() const {
+    return "List HALs.";
+}
+
+std::string ListCommand::parseCmdline(pid_t pid) const {
     std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
     std::string cmdline;
     if (!ifs.is_open()) {
@@ -62,7 +78,7 @@
     if (pair != mCmdlines.end()) {
         return pair->second;
     }
-    mCmdlines[pid] = ::android::lshal::getCmdline(pid);
+    mCmdlines[pid] = parseCmdline(pid);
     return mCmdlines[pid];
 }
 
@@ -73,7 +89,7 @@
     }), pids->end());
 }
 
-bool scanBinderContext(pid_t pid,
+static bool scanBinderContext(pid_t pid,
         const std::string &contextName,
         std::function<void(const std::string&)> eachLine) {
     std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
@@ -113,7 +129,7 @@
             uint64_t ptr;
             if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
                 // Should not reach here, but just be tolerant.
-                mErr << "Could not parse number " << ptrString << std::endl;
+                err() << "Could not parse number " << ptrString << std::endl;
                 return;
             }
             const std::string proc = " proc ";
@@ -122,7 +138,7 @@
                 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
                     int32_t pid;
                     if (!::android::base::ParseInt(pidStr, &pid)) {
-                        mErr << "Could not parse number " << pidStr << std::endl;
+                        err() << "Could not parse number " << pidStr << std::endl;
                         return;
                     }
                     pidInfo->refPids[ptr].push_back(pid);
@@ -159,6 +175,16 @@
     });
 }
 
+const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
+    auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
+    if (pair.second /* did insertion take place? */) {
+        if (!getPidInfo(serverPid, &pair.first->second)) {
+            return nullptr;
+        }
+    }
+    return &pair.first->second;
+}
+
 // Must process hwbinder services first, then passthrough services.
 void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
     f(mServicesTable);
@@ -204,44 +230,18 @@
             }
         }
     }
-}
 
-void ListCommand::printLine(
-        const std::string &interfaceName,
-        const std::string &transport,
-        const std::string &arch,
-        const std::string &threadUsage,
-        const std::string &server,
-        const std::string &serverCmdline,
-        const std::string &address,
-        const std::string &clients,
-        const std::string &clientCmdlines) const {
-    if (mSelectedColumns & ENABLE_INTERFACE_NAME)
-        mOut << std::setw(80) << interfaceName << "\t";
-    if (mSelectedColumns & ENABLE_TRANSPORT)
-        mOut << std::setw(10) << transport << "\t";
-    if (mSelectedColumns & ENABLE_ARCH)
-        mOut << std::setw(5) << arch << "\t";
-    if (mSelectedColumns & ENABLE_THREADS) {
-        mOut << std::setw(8) << threadUsage << "\t";
-    }
-    if (mSelectedColumns & ENABLE_SERVER_PID) {
-        if (mEnableCmdlines) {
-            mOut << std::setw(15) << serverCmdline << "\t";
-        } else {
-            mOut << std::setw(5)  << server << "\t";
-        }
-    }
-    if (mSelectedColumns & ENABLE_SERVER_ADDR)
-        mOut << std::setw(16) << address << "\t";
-    if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
-        if (mEnableCmdlines) {
-            mOut << std::setw(0)  << clientCmdlines;
-        } else {
-            mOut << std::setw(0)  << clients;
-        }
-    }
-    mOut << std::endl;
+    mServicesTable.setDescription(
+            "All binderized services (registered services through hwservicemanager)");
+    mPassthroughRefTable.setDescription(
+            "All interfaces that getService() has ever return as a passthrough interface;\n"
+            "PIDs / processes shown below might be inaccurate because the process\n"
+            "might have relinquished the interface or might have died.\n"
+            "The Server / Server CMD column can be ignored.\n"
+            "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
+            "the library and successfully fetched the passthrough implementation.");
+    mImplementationsTable.setDescription(
+            "All available passthrough implementations (all -impl.so files)");
 }
 
 static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
@@ -254,9 +254,9 @@
     return false;
 }
 
-void ListCommand::dumpVintf() const {
+void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
     using vintf::operator|=;
-    mOut << "<!-- " << std::endl
+    out << "<!-- " << std::endl
          << "    This is a skeleton device manifest. Notes: " << std::endl
          << "    1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
          << "    2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
@@ -283,7 +283,7 @@
             auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
             FQName fqName(splittedFqInstanceName.first);
             if (!fqName.isValid()) {
-                mErr << "Warning: '" << splittedFqInstanceName.first
+                err() << "Warning: '" << splittedFqInstanceName.first
                      << "' is not a valid FQName." << std::endl;
                 continue;
             }
@@ -316,12 +316,12 @@
                         arch = vintf::Arch::ARCH_32_64; break;
                     case lshal::ARCH_UNKNOWN: // fallthrough
                     default:
-                        mErr << "Warning: '" << fqName.package()
+                        err() << "Warning: '" << fqName.package()
                              << "' doesn't have bitness info, assuming 32+64." << std::endl;
                         arch = vintf::Arch::ARCH_32_64;
                 }
             } else {
-                mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
+                err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
                 continue;
             }
 
@@ -329,7 +329,7 @@
             for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
                 if (hal->transport() != transport) {
                     if (transport != vintf::Transport::PASSTHROUGH) {
-                        mErr << "Fatal: should not reach here. Generated result may be wrong for '"
+                        err() << "Fatal: should not reach here. Generated result may be wrong for '"
                              << hal->name << "'."
                              << std::endl;
                     }
@@ -360,29 +360,11 @@
                     .versions = {version},
                     .transportArch = {transport, arch},
                     .interfaces = interfaces})) {
-                mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
+                err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
             }
         }
     });
-    mOut << vintf::gHalManifestConverter(manifest);
-}
-
-static const std::string &getArchString(Architecture arch) {
-    static const std::string sStr64 = "64";
-    static const std::string sStr32 = "32";
-    static const std::string sStrBoth = "32+64";
-    static const std::string sStrUnknown = "";
-    switch (arch) {
-        case ARCH64:
-            return sStr64;
-        case ARCH32:
-            return sStr32;
-        case ARCH_BOTH:
-            return sStrBoth;
-        case ARCH_UNKNOWN: // fall through
-        default:
-            return sStrUnknown;
-    }
+    out << vintf::gHalManifestConverter(manifest);
 }
 
 static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
@@ -397,68 +379,54 @@
     }
 }
 
-void ListCommand::dumpTable() {
-    mServicesTable.description =
-            "All binderized services (registered services through hwservicemanager)";
-    mPassthroughRefTable.description =
-            "All interfaces that getService() has ever return as a passthrough interface;\n"
-            "PIDs / processes shown below might be inaccurate because the process\n"
-            "might have relinquished the interface or might have died.\n"
-            "The Server / Server CMD column can be ignored.\n"
-            "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
-            "the library and successfully fetched the passthrough implementation.";
-    mImplementationsTable.description =
-            "All available passthrough implementations (all -impl.so files)";
-    forEachTable([this] (const Table &table) {
-        if (!mNeat) {
-            mOut << table.description << std::endl;
-        }
-        mOut << std::left;
-        if (!mNeat) {
-            printLine("Interface", "Transport", "Arch", "Thread Use", "Server",
-                      "Server CMD", "PTR", "Clients", "Clients CMD");
-        }
+void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
+    if (mNeat) {
+        MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
+            .createTextTable().dump(out.buf());
+        return;
+    }
 
-        for (const auto &entry : table) {
-            printLine(entry.interfaceName,
-                    entry.transport,
-                    getArchString(entry.arch),
-                    entry.getThreadUsage(),
-                    entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
-                    entry.serverCmdline,
-                    entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
-                    join(entry.clientPids, " "),
-                    join(entry.clientCmdlines, ";"));
+    forEachTable([this, &out](const Table &table) {
 
-            // We're only interested in dumping debug info for already
-            // instantiated services. There's little value in dumping the
-            // debug info for a service we create on the fly, so we only operate
-            // on the "mServicesTable".
-            if (mEmitDebugInfo && &table == &mServicesTable) {
-                auto pair = splitFirst(entry.interfaceName, '/');
-                mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(),
-                        NullableOStream<std::ostream>(nullptr));
-            }
+        // We're only interested in dumping debug info for already
+        // instantiated services. There's little value in dumping the
+        // debug info for a service we create on the fly, so we only operate
+        // on the "mServicesTable".
+        std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
+        if (mEmitDebugInfo && &table == &mServicesTable) {
+            emitDebugInfo = [this](const auto& iName) {
+                std::stringstream ss;
+                auto pair = splitFirst(iName, '/');
+                mLshal.emitDebugInfo(pair.first, pair.second, {}, ss,
+                                     NullableOStream<std::ostream>(nullptr));
+                return ss.str();
+            };
         }
-        if (!mNeat) {
-            mOut << std::endl;
-        }
+        table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
+        out << std::endl;
     });
-
 }
 
-void ListCommand::dump() {
-    if (mVintf) {
-        dumpVintf();
-        if (!!mFileOutput) {
-            mFileOutput.buf().close();
-            delete &mFileOutput.buf();
-            mFileOutput = nullptr;
-        }
-        mOut = std::cout;
-    } else {
-        dumpTable();
+Status ListCommand::dump() {
+    auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
+
+    if (mFileOutputPath.empty()) {
+        (*this.*dump)(out());
+        return OK;
     }
+
+    std::ofstream fileOutput(mFileOutputPath);
+    if (!fileOutput.is_open()) {
+        err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
+        return IO_ERROR;
+    }
+    chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
+
+    (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
+
+    fileOutput.flush();
+    fileOutput.close();
+    return OK;
 }
 
 void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
@@ -471,10 +439,10 @@
         case LIST_DLLIB :
             table = &mImplementationsTable; break;
         default:
-            mErr << "Error: Unknown source of entry " << source << std::endl;
+            err() << "Error: Unknown source of entry " << source << std::endl;
     }
     if (table) {
-        table->entries.push_back(std::forward<TableEntry>(entry));
+        table->add(std::forward<TableEntry>(entry));
     }
 }
 
@@ -491,10 +459,7 @@
             entries.emplace(interfaceName, TableEntry{
                 .interfaceName = interfaceName,
                 .transport = "passthrough",
-                .serverPid = NO_PID,
-                .serverObjectAddress = NO_PTR,
                 .clientPids = info.clientPids,
-                .arch = ARCH_UNKNOWN
             }).first->second.arch |= fromBaseArchitecture(info.arch);
         }
         for (auto &&pair : entries) {
@@ -502,7 +467,7 @@
         }
     });
     if (!ret.isOk()) {
-        mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
+        err() << "Error: Failed to call list on getPassthroughServiceManager(): "
              << ret.description() << std::endl;
         return DUMP_ALL_LIBS_ERROR;
     }
@@ -525,14 +490,13 @@
                         std::string{info.instanceName.c_str()},
                 .transport = "passthrough",
                 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
-                .serverObjectAddress = NO_PTR,
                 .clientPids = info.clientPids,
                 .arch = fromBaseArchitecture(info.arch)
             });
         }
     });
     if (!ret.isOk()) {
-        mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
+        err() << "Error: Failed to call debugDump on defaultServiceManager(): "
              << ret.description() << std::endl;
         return DUMP_PASSTHROUGH_ERROR;
     }
@@ -540,10 +504,6 @@
 }
 
 Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
-    using namespace ::std;
-    using namespace ::android::hardware;
-    using namespace ::android::hidl::manager::V1_0;
-    using namespace ::android::hidl::base::V1_0;
     const std::string mode = "hwbinder";
 
     hidl_vec<hidl_string> fqInstanceNames;
@@ -552,86 +512,123 @@
         fqInstanceNames = names;
     });
     if (!listRet.isOk()) {
-        mErr << "Error: Failed to list services for " << mode << ": "
+        err() << "Error: Failed to list services for " << mode << ": "
              << listRet.description() << std::endl;
         return DUMP_BINDERIZED_ERROR;
     }
 
     Status status = OK;
-    // server pid, .ptr value of binder object, child pids
-    std::map<std::string, DebugInfo> allDebugInfos;
-    std::map<pid_t, PidInfo> allPids;
+    std::map<std::string, TableEntry> allTableEntries;
     for (const auto &fqInstanceName : fqInstanceNames) {
-        const auto pair = splitFirst(fqInstanceName, '/');
-        const auto &serviceName = pair.first;
-        const auto &instanceName = pair.second;
-        auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
-        if (!getRet.isOk()) {
-            mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
-                 << "cannot be fetched from service manager:"
-                 << getRet.description() << std::endl;
-            status |= DUMP_BINDERIZED_ERROR;
-            continue;
-        }
-        sp<IBase> service = getRet;
-        if (service == nullptr) {
-            mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
-                 << "cannot be fetched from service manager (null)"
-                 << std::endl;
-            status |= DUMP_BINDERIZED_ERROR;
-            continue;
-        }
-        auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
-            allDebugInfos[fqInstanceName] = debugInfo;
-            if (debugInfo.pid >= 0) {
-                allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
-            }
+        // create entry and default assign all fields.
+        TableEntry& entry = allTableEntries[fqInstanceName];
+        entry.interfaceName = fqInstanceName;
+        entry.transport = mode;
+
+        status |= fetchBinderizedEntry(manager, &entry);
+    }
+
+    for (auto& pair : allTableEntries) {
+        putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second));
+    }
+    return status;
+}
+
+Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager,
+                                         TableEntry *entry) {
+    Status status = OK;
+    const auto handleError = [&](Status additionalError, const std::string& msg) {
+        err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl;
+        status |= DUMP_BINDERIZED_ERROR | additionalError;
+    };
+
+    const auto pair = splitFirst(entry->interfaceName, '/');
+    const auto &serviceName = pair.first;
+    const auto &instanceName = pair.second;
+    auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
+    if (!getRet.isOk()) {
+        handleError(TRANSACTION_ERROR,
+                    "cannot be fetched from service manager:" + getRet.description());
+        return status;
+    }
+    sp<IBase> service = getRet;
+    if (service == nullptr) {
+        handleError(NO_INTERFACE, "cannot be fetched from service manager (null)");
+        return status;
+    }
+
+    // getDebugInfo
+    do {
+        DebugInfo debugInfo;
+        auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
+            debugInfo = received;
         });
         if (!debugRet.isOk()) {
-            mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
-                 << "debugging information cannot be retrieved:"
-                 << debugRet.description() << std::endl;
-            status |= DUMP_BINDERIZED_ERROR;
+            handleError(TRANSACTION_ERROR,
+                        "debugging information cannot be retrieved: " + debugRet.description());
+            break; // skip getPidInfo
         }
-    }
 
-    for (auto &pair : allPids) {
-        pid_t serverPid = pair.first;
-        if (!getPidInfo(serverPid, &allPids[serverPid])) {
-            mErr << "Warning: no information for PID " << serverPid
-                      << ", are you root?" << std::endl;
-            status |= DUMP_BINDERIZED_ERROR;
-        }
-    }
-    for (const auto &fqInstanceName : fqInstanceNames) {
-        auto it = allDebugInfos.find(fqInstanceName);
-        if (it == allDebugInfos.end()) {
-            putEntry(HWSERVICEMANAGER_LIST, {
-                .interfaceName = fqInstanceName,
-                .transport = mode,
-                .serverPid = NO_PID,
-                .serverObjectAddress = NO_PTR,
-                .clientPids = {},
-                .threadUsage = 0,
-                .threadCount = 0,
-                .arch = ARCH_UNKNOWN
-            });
-            continue;
-        }
-        const DebugInfo &info = it->second;
-        bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;
+        entry->serverPid = debugInfo.pid;
+        entry->serverObjectAddress = debugInfo.ptr;
+        entry->arch = fromBaseArchitecture(debugInfo.arch);
 
-        putEntry(HWSERVICEMANAGER_LIST, {
-            .interfaceName = fqInstanceName,
-            .transport = mode,
-            .serverPid = info.pid,
-            .serverObjectAddress = info.ptr,
-            .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
-            .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
-            .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
-            .arch = fromBaseArchitecture(info.arch),
+        if (debugInfo.pid != NO_PID) {
+            const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
+            if (pidInfo == nullptr) {
+                handleError(IO_ERROR,
+                            "no information for PID " + std::to_string(debugInfo.pid) +
+                            ", are you root?");
+                break;
+            }
+            if (debugInfo.ptr != NO_PTR) {
+                auto it = pidInfo->refPids.find(debugInfo.ptr);
+                if (it != pidInfo->refPids.end()) {
+                    entry->clientPids = it->second;
+                }
+            }
+            entry->threadUsage = pidInfo->threadUsage;
+            entry->threadCount = pidInfo->threadCount;
+        }
+    } while (0);
+
+    // hash
+    do {
+        ssize_t hashIndex = -1;
+        auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
+            for (size_t i = 0; i < c.size(); ++i) {
+                if (serviceName == c[i]) {
+                    hashIndex = static_cast<ssize_t>(i);
+                    break;
+                }
+            }
         });
-    }
+        if (!ifaceChainRet.isOk()) {
+            handleError(TRANSACTION_ERROR,
+                        "interfaceChain fails: " + ifaceChainRet.description());
+            break; // skip getHashChain
+        }
+        if (hashIndex < 0) {
+            handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
+            break; // skip getHashChain
+        }
+        auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
+            if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
+                handleError(BAD_IMPL,
+                            "interfaceChain indicates position " + std::to_string(hashIndex) +
+                            " but getHashChain returns " + std::to_string(hashChain.size()) +
+                            " hashes");
+                return;
+            }
+
+            auto&& hashArray = hashChain[hashIndex];
+            std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
+            entry->hash = Hash::hexString(hashVec);
+        });
+        if (!hashRet.isOk()) {
+            handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
+        }
+    } while (0);
     return status;
 }
 
@@ -639,7 +636,7 @@
     Status status = OK;
     auto bManager = mLshal.serviceManager();
     if (bManager == nullptr) {
-        mErr << "Failed to get defaultServiceManager()!" << std::endl;
+        err() << "Failed to get defaultServiceManager()!" << std::endl;
         status |= NO_BINDERIZED_MANAGER;
     } else {
         status |= fetchBinderized(bManager);
@@ -649,7 +646,7 @@
 
     auto pManager = mLshal.passthroughManager();
     if (pManager == nullptr) {
-        mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
+        err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
         status |= NO_PASSTHROUGH_MANAGER;
     } else {
         status |= fetchAllLibraries(pManager);
@@ -657,139 +654,270 @@
     return status;
 }
 
-Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
-    static struct option longOptions[] = {
-        // long options with short alternatives
-        {"help",      no_argument,       0, 'h' },
-        {"interface", no_argument,       0, 'i' },
-        {"transport", no_argument,       0, 't' },
-        {"arch",      no_argument,       0, 'r' },
-        {"pid",       no_argument,       0, 'p' },
-        {"address",   no_argument,       0, 'a' },
-        {"clients",   no_argument,       0, 'c' },
-        {"threads",   no_argument,       0, 'e' },
-        {"cmdline",   no_argument,       0, 'm' },
-        {"debug",     optional_argument, 0, 'd' },
+void ListCommand::registerAllOptions() {
+    int v = mOptions.size();
+    // A list of acceptable command line options
+    // key: value returned by getopt_long
+    // long options with short alternatives
+    mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) {
+        return USAGE;
+    }, ""});
+    mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
+        return OK;
+    }, "print the instance name column"});
+    mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::RELEASED);
+        return OK;
+    }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"});
+    mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
+        return OK;
+    }, "print the transport mode column"});
+    mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::ARCH);
+        return OK;
+    }, "print the bitness column"});
+    mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::HASH);
+        return OK;
+    }, "print hash of the interface"});
+    mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID);
+        return OK;
+    }, "print the server PID, or server cmdline if -m is set"});
+    mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
+        return OK;
+    }, "print the server object address column"});
+    mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
+        return OK;
+    }, "print the client PIDs, or client cmdlines if -m is set"});
+    mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mSelectedColumns.push_back(TableColumnType::THREADS);
+        return OK;
+    }, "print currently used/available threads\n(note, available threads created lazily)"});
+    mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mEnableCmdlines = true;
+        return OK;
+    }, "print cmdline instead of PIDs"});
+    mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) {
+        thiz->mEmitDebugInfo = true;
+        if (arg) thiz->mFileOutputPath = arg;
+        return OK;
+    }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n"
+        "Writes to specified file if 'arg' is provided, otherwise stdout."});
 
-        // long options without short alternatives
-        {"sort",      required_argument, 0, 's' },
-        {"init-vintf",optional_argument, 0, 'v' },
-        {"neat",      no_argument,       0, 'n' },
-        { 0,          0,                 0,  0  }
-    };
+    // long options without short alternatives
+    mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
+        thiz->mVintf = true;
+        if (arg) thiz->mFileOutputPath = arg;
+        return OK;
+    }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."});
+    mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) {
+        if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) {
+            thiz->mSortColumn = TableEntry::sortByInterfaceName;
+        } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) {
+            thiz->mSortColumn = TableEntry::sortByServerPid;
+        } else {
+            thiz->err() << "Unrecognized sorting column: " << arg << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."});
+    mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) {
+        thiz->mNeat = true;
+        return OK;
+    }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
+}
+
+// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
+// the lifetime of "options" during the usage of the returned array.
+static std::unique_ptr<struct option[]> getLongOptions(
+        const ListCommand::RegisteredOptions& options,
+        int* longOptFlag) {
+    std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
+    int i = 0;
+    for (const auto& e : options) {
+        ret[i].name = e.longOption.c_str();
+        ret[i].has_arg = e.hasArg;
+        ret[i].flag = longOptFlag;
+        ret[i].val = e.val;
+
+        i++;
+    }
+    // getopt_long last option has all zeros
+    ret[i].name = NULL;
+    ret[i].has_arg = 0;
+    ret[i].flag = NULL;
+    ret[i].val = 0;
+
+    return ret;
+}
+
+// Create 'optstring' argument to getopt_long.
+static std::string getShortOptions(const ListCommand::RegisteredOptions& options) {
+    std::stringstream ss;
+    for (const auto& e : options) {
+        if (e.shortOption != '\0') {
+            ss << e.shortOption;
+        }
+    }
+    return ss.str();
+}
+
+Status ListCommand::parseArgs(const Arg &arg) {
+
+    if (mOptions.empty()) {
+        registerAllOptions();
+    }
+    int longOptFlag;
+    std::unique_ptr<struct option[]> longOptions = getLongOptions(mOptions, &longOptFlag);
+    std::string shortOptions = getShortOptions(mOptions);
+
+    // suppress output to std::err for unknown options
+    opterr = 0;
 
     int optionIndex;
     int c;
     // Lshal::parseArgs has set optind to the next option to parse
     for (;;) {
-        // using getopt_long in case we want to add other options in the future
         c = getopt_long(arg.argc, arg.argv,
-                "hitrpacmde", longOptions, &optionIndex);
+                shortOptions.c_str(), longOptions.get(), &optionIndex);
         if (c == -1) {
             break;
         }
-        switch (c) {
-        case 's': {
-            if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
-                mSortColumn = TableEntry::sortByInterfaceName;
-            } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
-                mSortColumn = TableEntry::sortByServerPid;
-            } else {
-                mErr << "Unrecognized sorting column: " << optarg << std::endl;
-                mLshal.usage(command);
-                return USAGE;
+        const RegisteredOption* found = nullptr;
+        if (c == 0) {
+            // see long option
+            for (const auto& e : mOptions) {
+                if (longOptFlag == e.val) found = &e;
             }
-            break;
-        }
-        case 'v': {
-            if (optarg) {
-                mFileOutput = new std::ofstream{optarg};
-                mOut = mFileOutput;
-                if (!mFileOutput.buf().is_open()) {
-                    mErr << "Could not open file '" << optarg << "'." << std::endl;
-                    return IO_ERROR;
-                }
+        } else {
+            // see short option
+            for (const auto& e : mOptions) {
+                if (c == e.shortOption) found = &e;
             }
-            mVintf = true;
         }
-        case 'i': {
-            mSelectedColumns |= ENABLE_INTERFACE_NAME;
-            break;
-        }
-        case 't': {
-            mSelectedColumns |= ENABLE_TRANSPORT;
-            break;
-        }
-        case 'r': {
-            mSelectedColumns |= ENABLE_ARCH;
-            break;
-        }
-        case 'p': {
-            mSelectedColumns |= ENABLE_SERVER_PID;
-            break;
-        }
-        case 'a': {
-            mSelectedColumns |= ENABLE_SERVER_ADDR;
-            break;
-        }
-        case 'c': {
-            mSelectedColumns |= ENABLE_CLIENT_PIDS;
-            break;
-        }
-        case 'e': {
-            mSelectedColumns |= ENABLE_THREADS;
-            break;
-        }
-        case 'm': {
-            mEnableCmdlines = true;
-            break;
-        }
-        case 'd': {
-            mEmitDebugInfo = true;
 
-            if (optarg) {
-                mFileOutput = new std::ofstream{optarg};
-                mOut = mFileOutput;
-                if (!mFileOutput.buf().is_open()) {
-                    mErr << "Could not open file '" << optarg << "'." << std::endl;
-                    return IO_ERROR;
-                }
-                chown(optarg, AID_SHELL, AID_SHELL);
-            }
-            break;
-        }
-        case 'n': {
-            mNeat = true;
-            break;
-        }
-        case 'h': // falls through
-        default: // see unrecognized options
-            mLshal.usage(command);
+        if (found == nullptr) {
+            // see unrecognized options
+            err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl;
             return USAGE;
         }
+
+        Status status = found->op(this, optarg);
+        if (status != OK) {
+            return status;
+        }
     }
     if (optind < arg.argc) {
         // see non option
-        mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
+        err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl;
+        return USAGE;
     }
 
-    if (mSelectedColumns == 0) {
-        mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS;
+    if (mNeat && mEmitDebugInfo) {
+        err() << "Error: --neat should not be used with --debug." << std::endl;
+        return USAGE;
     }
+
+    if (mSelectedColumns.empty()) {
+        mSelectedColumns = {TableColumnType::RELEASED,
+                            TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
+                            TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
+    }
+
+    if (mEnableCmdlines) {
+        for (size_t i = 0; i < mSelectedColumns.size(); ++i) {
+            if (mSelectedColumns[i] == TableColumnType::SERVER_PID) {
+                mSelectedColumns[i] = TableColumnType::SERVER_CMD;
+            }
+            if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) {
+                mSelectedColumns[i] = TableColumnType::CLIENT_CMDS;
+            }
+        }
+    }
+
+    forEachTable([this] (Table& table) {
+        table.setSelectedColumns(this->mSelectedColumns);
+    });
+
     return OK;
 }
 
-Status ListCommand::main(const std::string &command, const Arg &arg) {
-    Status status = parseArgs(command, arg);
+Status ListCommand::main(const Arg &arg) {
+    Status status = parseArgs(arg);
     if (status != OK) {
         return status;
     }
     status = fetch();
     postprocess();
-    dump();
+    status |= dump();
     return status;
 }
 
+static std::vector<std::string> splitString(const std::string &s, char c) {
+    std::vector<std::string> components;
+
+    size_t startPos = 0;
+    size_t matchPos;
+    while ((matchPos = s.find(c, startPos)) != std::string::npos) {
+        components.push_back(s.substr(startPos, matchPos - startPos));
+        startPos = matchPos + 1;
+    }
+
+    if (startPos <= s.length()) {
+        components.push_back(s.substr(startPos));
+    }
+    return components;
+}
+
+const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const {
+    static const std::string empty{};
+    static const std::string optional{"[=<arg>]"};
+    static const std::string required{"=<arg>"};
+
+    if (hasArg == optional_argument) {
+        return optional;
+    }
+    if (hasArg == required_argument) {
+        return required;
+    }
+    return empty;
+}
+
+void ListCommand::usage() const {
+
+    err() << "list:" << std::endl
+          << "    lshal" << std::endl
+          << "    lshal list" << std::endl
+          << "        List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl
+          << "    lshal list [-h|--help]" << std::endl
+          << "        -h, --help: Print help message for list (`lshal help list`)" << std::endl
+          << "    lshal [list] [OPTIONS...]" << std::endl;
+    for (const auto& e : mOptions) {
+        if (e.help.empty()) {
+            continue;
+        }
+        err() << "        ";
+        if (e.shortOption != '\0')
+            err() << "-" << e.shortOption << e.getHelpMessageForArgument();
+        if (e.shortOption != '\0' && !e.longOption.empty())
+            err() << ", ";
+        if (!e.longOption.empty())
+            err() << "--" << e.longOption << e.getHelpMessageForArgument();
+        err() << ": ";
+        std::vector<std::string> lines = splitString(e.help, '\n');
+        for (const auto& line : lines) {
+            if (&line != &lines.front())
+                err() << "            ";
+            err() << line << std::endl;
+        }
+    }
+}
+
 }  // namespace lshal
 }  // namespace android
 
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index a75db04..7e252fc 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -17,6 +17,7 @@
 #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
 #define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_
 
+#include <getopt.h>
 #include <stdint.h>
 
 #include <fstream>
@@ -26,8 +27,10 @@
 #include <android-base/macros.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 
+#include "Command.h"
 #include "NullableOStream.h"
 #include "TableEntry.h"
+#include "TextTable.h"
 #include "utils.h"
 
 namespace android {
@@ -35,39 +38,71 @@
 
 class Lshal;
 
-class ListCommand {
+struct PidInfo {
+    std::map<uint64_t, Pids> refPids; // pids that are referenced
+    uint32_t threadUsage; // number of threads in use
+    uint32_t threadCount; // number of threads total
+};
+
+class ListCommand : public Command {
 public:
-    ListCommand(Lshal &lshal);
-    Status main(const std::string &command, const Arg &arg);
-private:
-    Status parseArgs(const std::string &command, const Arg &arg);
+    ListCommand(Lshal &lshal) : Command(lshal) {}
+    virtual ~ListCommand() = default;
+    Status main(const Arg &arg) override;
+    void usage() const override;
+    std::string getSimpleDescription() const override;
+    std::string getName() const override { return GetName(); }
+
+    static std::string GetName();
+
+    struct RegisteredOption {
+        // short alternative, e.g. 'v'. If '\0', no short options is available.
+        char shortOption;
+        // long alternative, e.g. 'init-vintf'
+        std::string longOption;
+        // no_argument, required_argument or optional_argument
+        int hasArg;
+        // value written to 'flag' by getopt_long
+        int val;
+        // operation when the argument is present
+        std::function<Status(ListCommand* thiz, const char* arg)> op;
+        // help message
+        std::string help;
+
+        const std::string& getHelpMessageForArgument() const;
+    };
+    // A list of acceptable command line options
+    // key: value returned by getopt_long
+    using RegisteredOptions = std::vector<RegisteredOption>;
+
+protected:
+    Status parseArgs(const Arg &arg);
     Status fetch();
-    void postprocess();
-    void dump();
+    virtual void postprocess();
+    Status dump();
     void putEntry(TableEntrySource source, TableEntry &&entry);
     Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
     Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
     Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
 
-    struct PidInfo {
-        std::map<uint64_t, Pids> refPids; // pids that are referenced
-        uint32_t threadUsage; // number of threads in use
-        uint32_t threadCount; // number of threads total
-    };
-    bool getPidInfo(pid_t serverPid, PidInfo *info) const;
+    Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager,
+                                TableEntry *entry);
 
-    void dumpTable();
-    void dumpVintf() const;
-    void printLine(
-            const std::string &interfaceName,
-            const std::string &transport,
-            const std::string &arch,
-            const std::string &threadUsage,
-            const std::string &server,
-            const std::string &serverCmdline,
-            const std::string &address,
-            const std::string &clients,
-            const std::string &clientCmdlines) const;
+    // Get relevant information for a PID by parsing files under /d/binder.
+    // It is a virtual member function so that it can be mocked.
+    virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const;
+    // Retrieve from mCachedPidInfos and call getPidInfo if necessary.
+    const PidInfo* getPidInfoCached(pid_t serverPid);
+
+    void dumpTable(const NullableOStream<std::ostream>& out) const;
+    void dumpVintf(const NullableOStream<std::ostream>& out) const;
+    void addLine(TextTable *table, const std::string &interfaceName, const std::string &transport,
+                 const std::string &arch, const std::string &threadUsage, const std::string &server,
+                 const std::string &serverCmdline, const std::string &address,
+                 const std::string &clients, const std::string &clientCmdlines) const;
+    void addLine(TextTable *table, const TableEntry &entry);
+    // Read and return /proc/{pid}/cmdline.
+    virtual std::string parseCmdline(pid_t pid) const;
     // Return /proc/{pid}/cmdline if it exists, else empty string.
     const std::string &getCmdline(pid_t pid);
     // Call getCmdline on all pid in pids. If it returns empty string, the process might
@@ -76,21 +111,18 @@
     void forEachTable(const std::function<void(Table &)> &f);
     void forEachTable(const std::function<void(const Table &)> &f) const;
 
-    Lshal &mLshal;
+    NullableOStream<std::ostream> err() const;
+    NullableOStream<std::ostream> out() const;
+
+    void registerAllOptions();
 
     Table mServicesTable{};
     Table mPassthroughRefTable{};
     Table mImplementationsTable{};
 
-    NullableOStream<std::ostream> mErr;
-    NullableOStream<std::ostream> mOut;
-    NullableOStream<std::ofstream> mFileOutput = nullptr;
+    std::string mFileOutputPath;
     TableEntryCompare mSortColumn = nullptr;
-    TableEntrySelect mSelectedColumns = 0;
-    // If true, cmdlines will be printed instead of pid.
-    bool mEnableCmdlines = false;
 
-    // If true, calls IBase::debug(...) on each service.
     bool mEmitDebugInfo = false;
 
     // If true, output in VINTF format.
@@ -104,6 +136,16 @@
     // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
     std::map<pid_t, std::string> mCmdlines;
 
+    // Cache for getPidInfo.
+    std::map<pid_t, PidInfo> mCachedPidInfos;
+
+    RegisteredOptions mOptions;
+    // All selected columns
+    std::vector<TableColumnType> mSelectedColumns;
+    // If true, emit cmdlines instead of PIDs
+    bool mEnableCmdlines = false;
+
+private:
     DISALLOW_COPY_AND_ASSIGN(ListCommand);
 };
 
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index e2d5f6d..c6f28ac 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -34,9 +34,8 @@
 using ::android::hidl::manager::V1_0::IServiceManager;
 
 Lshal::Lshal()
-    : mOut(std::cout), mErr(std::cerr),
-      mServiceManager(::android::hardware::defaultServiceManager()),
-      mPassthroughManager(::android::hardware::getPassthroughServiceManager()) {
+    : Lshal(std::cout, std::cerr, ::android::hardware::defaultServiceManager(),
+            ::android::hardware::getPassthroughServiceManager()) {
 }
 
 Lshal::Lshal(std::ostream &out, std::ostream &err,
@@ -46,76 +45,39 @@
       mServiceManager(serviceManager),
       mPassthroughManager(passthroughManager) {
 
+    mRegisteredCommands.push_back({std::make_unique<ListCommand>(*this)});
+    mRegisteredCommands.push_back({std::make_unique<DebugCommand>(*this)});
+    mRegisteredCommands.push_back({std::make_unique<HelpCommand>(*this)});
 }
 
-void Lshal::usage(const std::string &command) const {
-    static const std::string helpSummary =
-            "lshal: List and debug HALs.\n"
-            "\n"
-            "commands:\n"
-            "    help            Print help message\n"
-            "    list            list HALs\n"
-            "    debug           debug a specified HAL\n"
-            "\n"
-            "If no command is specified, `list` is the default.\n";
+void Lshal::forEachCommand(const std::function<void(const Command* c)>& f) const {
+    for (const auto& e : mRegisteredCommands) f(e.get());
+}
 
-    static const std::string list =
-            "list:\n"
-            "    lshal\n"
-            "    lshal list\n"
-            "        List all hals with default ordering and columns (`lshal list -ipc`)\n"
-            "    lshal list [-h|--help]\n"
-            "        -h, --help: Print help message for list (`lshal help list`)\n"
-            "    lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n"
-            "            [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n"
-            "            [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n"
-            "            [--debug|-d[=<output file>]]\n"
-            "        -i, --interface: print the interface name column\n"
-            "        -n, --instance: print the instance name column\n"
-            "        -t, --transport: print the transport mode column\n"
-            "        -r, --arch: print if the HAL is in 64-bit or 32-bit\n"
-            "        -e, --threads: print currently used/available threads\n"
-            "                       (note, available threads created lazily)\n"
-            "        -p, --pid: print the server PID, or server cmdline if -m is set\n"
-            "        -a, --address: print the server object address column\n"
-            "        -c, --clients: print the client PIDs, or client cmdlines if -m is set\n"
-            "        -m, --cmdline: print cmdline instead of PIDs\n"
-            "        -d[=<output file>], --debug[=<output file>]: emit debug info from \n"
-            "                IBase::debug with empty options\n"
-            "        --sort=i, --sort=interface: sort by interface name\n"
-            "        --sort=p, --sort=pid: sort by server pid\n"
-            "        --init-vintf=<output file>: form a skeleton HAL manifest to specified\n"
-            "                      file, or stdout if no file specified.\n";
+void Lshal::usage() {
+    err() << "lshal: List and debug HALs." << std::endl << std::endl
+          << "commands:" << std::endl;
 
-    static const std::string debug =
-            "debug:\n"
-            "    lshal debug <interface> [options [options [...]]] \n"
-            "        Print debug information of a specified interface.\n"
-            "        <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n"
-            "            If instance name is missing `default` is used.\n"
-            "        options: space separated options to IBase::debug.\n";
+    size_t nameMaxLength = 0;
+    forEachCommand([&](const Command* e) {
+        nameMaxLength = std::max(nameMaxLength, e->getName().length());
+    });
+    bool first = true;
+    forEachCommand([&](const Command* e) {
+        if (!first) err() << std::endl;
+        first = false;
+        err() << "    " << std::left << std::setw(nameMaxLength + 8) << e->getName()
+              << e->getSimpleDescription();
+    });
+    err() << std::endl << "If no command is specified, `" << ListCommand::GetName()
+          << "` is the default." << std::endl << std::endl;
 
-    static const std::string help =
-            "help:\n"
-            "    lshal -h\n"
-            "    lshal --help\n"
-            "    lshal help\n"
-            "        Print this help message\n"
-            "    lshal help list\n"
-            "        Print help message for list\n"
-            "    lshal help debug\n"
-            "        Print help message for debug\n";
-
-    if (command == "list") {
-        mErr << list;
-        return;
-    }
-    if (command == "debug") {
-        mErr << debug;
-        return;
-    }
-
-    mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help;
+    first = true;
+    forEachCommand([&](const Command* e) {
+        if (!first) err() << std::endl;
+        first = false;
+        e->usage();
+    });
 }
 
 // A unique_ptr type using a custom deleter function.
@@ -186,26 +148,24 @@
 }
 
 Status Lshal::parseArgs(const Arg &arg) {
-    static std::set<std::string> sAllCommands{"list", "debug", "help"};
     optind = 1;
     if (optind >= arg.argc) {
         // no options at all.
         return OK;
     }
     mCommand = arg.argv[optind];
-    if (sAllCommands.find(mCommand) != sAllCommands.end()) {
+    if (selectCommand(mCommand) != nullptr) {
         ++optind;
         return OK; // mCommand is set correctly
     }
 
     if (mCommand.size() > 0 && mCommand[0] == '-') {
         // first argument is an option, set command to "" (which is recognized as "list")
-        mCommand = "";
+        mCommand.clear();
         return OK;
     }
 
-    mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl;
-    usage();
+    err() << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "'" << std::endl;
     return USAGE;
 }
 
@@ -216,27 +176,43 @@
     }
 }
 
+Command* Lshal::selectCommand(const std::string& command) const {
+    if (command.empty()) {
+        return selectCommand(ListCommand::GetName());
+    }
+    for (const auto& e : mRegisteredCommands) {
+        if (e->getName() == command) {
+            return e.get();
+        }
+    }
+    return nullptr;
+}
+
 Status Lshal::main(const Arg &arg) {
     // Allow SIGINT to terminate all threads.
     signal(SIGINT, signalHandler);
 
     Status status = parseArgs(arg);
     if (status != OK) {
+        usage();
         return status;
     }
-    if (mCommand == "help") {
-        usage(optind < arg.argc ? arg.argv[optind] : "");
+    auto c = selectCommand(mCommand);
+    if (c == nullptr) {
+        // unknown command, print global usage
+        usage();
         return USAGE;
     }
-    // Default command is list
-    if (mCommand == "list" || mCommand == "") {
-        return ListCommand{*this}.main(mCommand, arg);
+    status = c->main(arg);
+    if (status == USAGE) {
+        // bad options. Run `lshal help ${mCommand}` instead.
+        // For example, `lshal --unknown-option` becomes `lshal help` (prints global help)
+        // and `lshal list --unknown-option` becomes `lshal help list`
+        auto&& help = selectCommand(HelpCommand::GetName());
+        return static_cast<HelpCommand*>(help)->usageOfCommand(mCommand);
     }
-    if (mCommand == "debug") {
-        return DebugCommand{*this}.main(mCommand, arg);
-    }
-    usage();
-    return USAGE;
+
+    return status;
 }
 
 NullableOStream<std::ostream> Lshal::err() const {
diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h
index 00db5d0..9f8eeaa 100644
--- a/cmds/lshal/Lshal.h
+++ b/cmds/lshal/Lshal.h
@@ -24,6 +24,8 @@
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <utils/StrongPointer.h>
 
+#include "Command.h"
+#include "HelpCommand.h"
 #include "NullableOStream.h"
 #include "utils.h"
 
@@ -33,13 +35,15 @@
 class Lshal {
 public:
     Lshal();
+    virtual ~Lshal() {}
     Lshal(std::ostream &out, std::ostream &err,
             sp<hidl::manager::V1_0::IServiceManager> serviceManager,
             sp<hidl::manager::V1_0::IServiceManager> passthroughManager);
     Status main(const Arg &arg);
-    void usage(const std::string &command = "") const;
-    NullableOStream<std::ostream> err() const;
-    NullableOStream<std::ostream> out() const;
+    // global usage
+    void usage();
+    virtual NullableOStream<std::ostream> err() const;
+    virtual NullableOStream<std::ostream> out() const;
     const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const;
     const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const;
 
@@ -49,8 +53,14 @@
             const std::vector<std::string> &options,
             std::ostream &out,
             NullableOStream<std::ostream> err) const;
+
+    Command* selectCommand(const std::string& command) const;
+
+    void forEachCommand(const std::function<void(const Command* c)>& f) const;
+
 private:
     Status parseArgs(const Arg &arg);
+
     std::string mCommand;
     Arg mCmdArgs;
     NullableOStream<std::ostream> mOut;
@@ -59,6 +69,8 @@
     sp<hidl::manager::V1_0::IServiceManager> mServiceManager;
     sp<hidl::manager::V1_0::IServiceManager> mPassthroughManager;
 
+    std::vector<std::unique_ptr<Command>> mRegisteredCommands;
+
     DISALLOW_COPY_AND_ASSIGN(Lshal);
 };
 
diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp
new file mode 100644
index 0000000..e8792a4
--- /dev/null
+++ b/cmds/lshal/TableEntry.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "lshal"
+#include <android-base/logging.h>
+
+#include <hidl-hash/Hash.h>
+
+#include "TableEntry.h"
+
+#include "TextTable.h"
+#include "utils.h"
+
+namespace android {
+namespace lshal {
+
+static const std::string &getArchString(Architecture arch) {
+    static const std::string sStr64 = "64";
+    static const std::string sStr32 = "32";
+    static const std::string sStrBoth = "32+64";
+    static const std::string sStrUnknown = "";
+    switch (arch) {
+        case ARCH64:
+            return sStr64;
+        case ARCH32:
+            return sStr32;
+        case ARCH_BOTH:
+            return sStrBoth;
+        case ARCH_UNKNOWN: // fall through
+        default:
+            return sStrUnknown;
+    }
+}
+
+static std::string getTitle(TableColumnType type) {
+    switch (type) {
+        case TableColumnType::INTERFACE_NAME:   return "Interface";
+        case TableColumnType::TRANSPORT:        return "Transport";
+        case TableColumnType::SERVER_PID:       return "Server";
+        case TableColumnType::SERVER_CMD:       return "Server CMD";
+        case TableColumnType::SERVER_ADDR:      return "PTR";
+        case TableColumnType::CLIENT_PIDS:      return "Clients";
+        case TableColumnType::CLIENT_CMDS:      return "Clients CMD";
+        case TableColumnType::ARCH:             return "Arch";
+        case TableColumnType::THREADS:          return "Thread Use";
+        case TableColumnType::RELEASED:         return "R";
+        case TableColumnType::HASH:             return "Hash";
+        default:
+            LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
+            return "";
+    }
+}
+
+std::string TableEntry::getField(TableColumnType type) const {
+    switch (type) {
+        case TableColumnType::INTERFACE_NAME:
+            return interfaceName;
+        case TableColumnType::TRANSPORT:
+            return transport;
+        case TableColumnType::SERVER_PID:
+            return serverPid == NO_PID ? "N/A" : std::to_string(serverPid);
+        case TableColumnType::SERVER_CMD:
+            return serverCmdline;
+        case TableColumnType::SERVER_ADDR:
+            return serverObjectAddress == NO_PTR ? "N/A" : toHexString(serverObjectAddress);
+        case TableColumnType::CLIENT_PIDS:
+            return join(clientPids, " ");
+        case TableColumnType::CLIENT_CMDS:
+            return join(clientCmdlines, ";");
+        case TableColumnType::ARCH:
+            return getArchString(arch);
+        case TableColumnType::THREADS:
+            return getThreadUsage();
+        case TableColumnType::RELEASED:
+            return isReleased();
+        case TableColumnType::HASH:
+            return hash;
+        default:
+            LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
+            return "";
+    }
+}
+
+std::string TableEntry::isReleased() const {
+    static const std::string unreleased = Hash::hexString(Hash::kEmptyHash);
+
+    if (hash.empty() || hash == unreleased) {
+        return " "; // unknown or unreleased
+    }
+    return "Y"; // released
+}
+
+TextTable Table::createTextTable(bool neat,
+    const std::function<std::string(const std::string&)>& emitDebugInfo) const {
+
+    TextTable textTable;
+    std::vector<std::string> row;
+    if (!neat) {
+        textTable.add(mDescription);
+
+        row.clear();
+        for (TableColumnType type : mSelectedColumns) {
+            row.push_back(getTitle(type));
+        }
+        textTable.add(std::move(row));
+    }
+
+    for (const auto& entry : mEntries) {
+        row.clear();
+        for (TableColumnType type : mSelectedColumns) {
+            row.push_back(entry.getField(type));
+        }
+        textTable.add(std::move(row));
+
+        if (emitDebugInfo) {
+            std::string debugInfo = emitDebugInfo(entry.interfaceName);
+            if (!debugInfo.empty()) textTable.add(debugInfo);
+        }
+    }
+    return textTable;
+}
+
+TextTable MergedTable::createTextTable() {
+    TextTable textTable;
+    for (const Table* table : mTables) {
+        textTable.addAll(table->createTextTable());
+    }
+    return textTable;
+}
+
+bool TableEntry::operator==(const TableEntry& other) const {
+    if (this == &other) {
+        return true;
+    }
+    return interfaceName == other.interfaceName && transport == other.transport &&
+        serverPid == other.serverPid && threadUsage == other.threadUsage &&
+        threadCount == other.threadCount && serverCmdline == other.serverCmdline &&
+        serverObjectAddress == other.serverObjectAddress && clientPids == other.clientPids &&
+        clientCmdlines == other.clientCmdlines && arch == other.arch;
+}
+
+std::string TableEntry::to_string() const {
+    std::stringstream ss;
+    ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage()
+       << ";server=" << serverPid
+       << "(" << serverObjectAddress << ";" << serverCmdline << ");clients=["
+       << join(clientPids, ";") << "](" << join(clientCmdlines, ";") << ");arch="
+       << getArchString(arch);
+    return ss.str();
+
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index e04c3ca..69206cc 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -23,6 +23,8 @@
 #include <vector>
 #include <iostream>
 
+#include "TextTable.h"
+
 namespace android {
 namespace lshal {
 
@@ -43,17 +45,38 @@
 };
 using Architecture = unsigned int;
 
+enum class TableColumnType : unsigned int {
+    INTERFACE_NAME,
+    TRANSPORT,
+    SERVER_PID,
+    SERVER_CMD,
+    SERVER_ADDR,
+    CLIENT_PIDS,
+    CLIENT_CMDS,
+    ARCH,
+    THREADS,
+    RELEASED,
+    HASH,
+};
+
+enum {
+    NO_PID = -1,
+    NO_PTR = 0
+};
+
 struct TableEntry {
-    std::string interfaceName;
-    std::string transport;
-    int32_t serverPid;
-    uint32_t threadUsage;
-    uint32_t threadCount;
-    std::string serverCmdline;
-    uint64_t serverObjectAddress;
-    Pids clientPids;
-    std::vector<std::string> clientCmdlines;
-    Architecture arch;
+    std::string interfaceName{};
+    std::string transport{};
+    int32_t serverPid{NO_PID};
+    uint32_t threadUsage{0};
+    uint32_t threadCount{0};
+    std::string serverCmdline{};
+    uint64_t serverObjectAddress{NO_PTR};
+    Pids clientPids{};
+    std::vector<std::string> clientCmdlines{};
+    Architecture arch{ARCH_UNKNOWN};
+    // empty: unknown, all zeros: unreleased, otherwise: released
+    std::string hash{};
 
     static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
         return a.interfaceName < b.interfaceName;
@@ -69,36 +92,52 @@
 
         return std::to_string(threadUsage) + "/" + std::to_string(threadCount);
     }
+
+    std::string isReleased() const;
+
+    std::string getField(TableColumnType type) const;
+
+    bool operator==(const TableEntry& other) const;
+    std::string to_string() const;
 };
 
-struct Table {
-    using Entries = std::vector<TableEntry>;
-    std::string description;
-    Entries entries;
+using SelectedColumns = std::vector<TableColumnType>;
 
-    Entries::iterator begin() { return entries.begin(); }
-    Entries::const_iterator begin() const { return entries.begin(); }
-    Entries::iterator end() { return entries.end(); }
-    Entries::const_iterator end() const { return entries.end(); }
+class Table {
+public:
+    using Entries = std::vector<TableEntry>;
+
+    Entries::iterator begin() { return mEntries.begin(); }
+    Entries::const_iterator begin() const { return mEntries.begin(); }
+    Entries::iterator end() { return mEntries.end(); }
+    Entries::const_iterator end() const { return mEntries.end(); }
+    size_t size() const { return mEntries.size(); }
+
+    void add(TableEntry&& entry) { mEntries.push_back(std::move(entry)); }
+
+    void setSelectedColumns(const SelectedColumns& s) { mSelectedColumns = s; }
+    const SelectedColumns& getSelectedColumns() const { return mSelectedColumns; }
+
+    void setDescription(std::string&& d) { mDescription = std::move(d); }
+
+    // Write table content.
+    TextTable createTextTable(bool neat = true,
+        const std::function<std::string(const std::string&)>& emitDebugInfo = nullptr) const;
+
+private:
+    std::string mDescription;
+    Entries mEntries;
+    SelectedColumns mSelectedColumns;
 };
 
 using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>;
 
-enum : unsigned int {
-    ENABLE_INTERFACE_NAME = 1 << 0,
-    ENABLE_TRANSPORT      = 1 << 1,
-    ENABLE_SERVER_PID     = 1 << 2,
-    ENABLE_SERVER_ADDR    = 1 << 3,
-    ENABLE_CLIENT_PIDS    = 1 << 4,
-    ENABLE_ARCH           = 1 << 5,
-    ENABLE_THREADS        = 1 << 6,
-};
-
-using TableEntrySelect = unsigned int;
-
-enum {
-    NO_PID = -1,
-    NO_PTR = 0
+class MergedTable {
+public:
+    MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {}
+    TextTable createTextTable();
+private:
+    std::vector<const Table*> mTables;
 };
 
 }  // namespace lshal
diff --git a/cmds/lshal/TextTable.cpp b/cmds/lshal/TextTable.cpp
new file mode 100644
index 0000000..eca9061
--- /dev/null
+++ b/cmds/lshal/TextTable.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <iomanip>
+
+#include "TextTable.h"
+
+namespace android {
+namespace lshal {
+
+void TextTable::computeWidth(const std::vector<std::string>& v) {
+    if (mWidths.size() < v.size()) {
+        mWidths.resize(v.size());
+    }
+    for (size_t i = 0; i < v.size(); ++i) {
+        mWidths[i] = std::max(mWidths[i], v[i].length());
+    }
+}
+
+void TextTable::dump(std::ostream& out) const {
+    out << std::left;
+    for (const auto& row : mTable) {
+        if (!row.isRow()) {
+            out << row.line() << std::endl;
+            continue;
+        }
+
+        for (size_t i = 0; i < row.fields().size(); ++i) {
+            if (i != 0) {
+                out << " ";
+            }
+            // last column does not std::setw to avoid printing unnecessary spaces.
+            if (i < row.fields().size() - 1) {
+                out << std::setw(mWidths[i]);
+            }
+            out << row.fields()[i];
+        }
+        out << std::endl;
+    }
+}
+
+void TextTable::addAll(TextTable&& other) {
+    for (auto&& row : other.mTable) {
+        if (row.isRow()) {
+            computeWidth(row.fields());
+        }
+
+        mTable.emplace_back(std::move(row));
+    }
+}
+
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h
new file mode 100644
index 0000000..91d522a
--- /dev/null
+++ b/cmds/lshal/TextTable.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
+#define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace lshal {
+
+// An element in TextTable. This is either an actual row (an array of cells
+// in this row), or a string of explanatory text.
+// To see if this is an actual row, test fields().empty().
+class TextTableRow {
+public:
+    // An empty line.
+    TextTableRow() {}
+
+    // A row of cells.
+    TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {}
+
+    // A single comment string.
+    TextTableRow(std::string&& s) : mLine(std::move(s)) {}
+    TextTableRow(const std::string& s) : mLine(s) {}
+
+    // Whether this row is an actual row of cells.
+    bool isRow() const { return !fields().empty(); }
+
+    // Get all cells.
+    const std::vector<std::string>& fields() const { return mFields; }
+
+    // Get the single comment string.
+    const std::string& line() const { return mLine; }
+
+private:
+    std::vector<std::string> mFields;
+    std::string mLine;
+};
+
+// A TextTable is a 2D array of strings.
+class TextTable {
+public:
+
+    // Add a TextTableRow.
+    void add() { mTable.emplace_back(); }
+    void add(std::vector<std::string>&& v) {
+        computeWidth(v);
+        mTable.emplace_back(std::move(v));
+    }
+    void add(const std::string& s) { mTable.emplace_back(s); }
+    void add(std::string&& s) { mTable.emplace_back(std::move(s)); }
+
+    void addAll(TextTable&& other);
+
+    // Prints the table to out, with column widths adjusted appropriately according
+    // to the content.
+    void dump(std::ostream& out) const;
+
+private:
+    void computeWidth(const std::vector<std::string>& v);
+    std::vector<size_t> mWidths;
+    std::vector<TextTableRow> mTable;
+};
+
+} // namespace lshal
+} // namespace android
+
+#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 972d508..9220fc0 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -26,21 +26,29 @@
 #include <gmock/gmock.h>
 #include <android/hardware/tests/baz/1.0/IQuux.h>
 #include <hidl/HidlTransportSupport.h>
+#include <vintf/parse_xml.h>
 
+#include "ListCommand.h"
 #include "Lshal.h"
 
 #define NELEMS(array)   static_cast<int>(sizeof(array) / sizeof(array[0]))
 
 using namespace testing;
 
+using ::android::hidl::base::V1_0::DebugInfo;
 using ::android::hidl::base::V1_0::IBase;
 using ::android::hidl::manager::V1_0::IServiceManager;
 using ::android::hidl::manager::V1_0::IServiceNotification;
+using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_death_recipient;
 using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
 
+using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;
+
+using hidl_hash = hidl_array<uint8_t, 32>;
+
 namespace android {
 namespace hardware {
 namespace tests {
@@ -76,7 +84,6 @@
 
 namespace lshal {
 
-
 class MockServiceManager : public IServiceManager {
 public:
     template<typename T>
@@ -107,7 +114,7 @@
 
 };
 
-class LshalTest : public ::testing::Test {
+class DebugTest : public ::testing::Test {
 public:
     void SetUp() override {
         using ::android::hardware::tests::baz::V1_0::IQuux;
@@ -122,43 +129,545 @@
                     return new Quux();
                 return nullptr;
             }));
+
+        lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager);
     }
     void TearDown() override {}
 
     std::stringstream err;
     std::stringstream out;
     sp<MockServiceManager> serviceManager;
+
+    std::unique_ptr<Lshal> lshal;
 };
 
-TEST_F(LshalTest, Debug) {
-    const char *args[] = {
+static Arg createArg(const std::vector<const char*>& args) {
+    return Arg{static_cast<int>(args.size()), const_cast<char**>(args.data())};
+}
+
+template<typename T>
+static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) {
+    return lshal->main(createArg(args));
+}
+
+TEST_F(DebugTest, Debug) {
+    EXPECT_EQ(0u, callMain(lshal, {
         "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar"
-    };
-    EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
-            .main({NELEMS(args), const_cast<char **>(args)}));
+    }));
     EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar"));
     EXPECT_THAT(err.str(), IsEmpty());
 }
 
-TEST_F(LshalTest, Debug2) {
-    const char *args[] = {
+TEST_F(DebugTest, Debug2) {
+    EXPECT_EQ(0u, callMain(lshal, {
         "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux"
-    };
-    EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager)
-            .main({NELEMS(args), const_cast<char **>(args)}));
+    }));
     EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux"));
     EXPECT_THAT(err.str(), IsEmpty());
 }
 
-TEST_F(LshalTest, Debug3) {
-    const char *args[] = {
+TEST_F(DebugTest, Debug3) {
+    EXPECT_NE(0u, callMain(lshal, {
         "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist",
-    };
-    EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager)
-            .main({NELEMS(args), const_cast<char **>(args)}));
+    }));
     EXPECT_THAT(err.str(), HasSubstr("does not exist"));
 }
 
+class MockLshal : public Lshal {
+public:
+    MockLshal() {}
+    ~MockLshal() = default;
+    MOCK_CONST_METHOD0(out, NullableOStream<std::ostream>());
+    MOCK_CONST_METHOD0(err, NullableOStream<std::ostream>());
+};
+
+// expose protected fields and methods for ListCommand
+class MockListCommand : public ListCommand {
+public:
+    MockListCommand(Lshal* lshal) : ListCommand(*lshal) {}
+
+    Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); }
+    Status main(const Arg& arg) { return ListCommand::main(arg); }
+    void forEachTable(const std::function<void(Table &)> &f) {
+        return ListCommand::forEachTable(f);
+    }
+    void forEachTable(const std::function<void(const Table &)> &f) const {
+        return ListCommand::forEachTable(f);
+    }
+    Status fetch() { return ListCommand::fetch(); }
+    void dumpVintf(const NullableOStream<std::ostream>& out) {
+        return ListCommand::dumpVintf(out);
+    }
+    void internalPostprocess() { ListCommand::postprocess(); }
+    const PidInfo* getPidInfoCached(pid_t serverPid) {
+        return ListCommand::getPidInfoCached(serverPid);
+    }
+
+    MOCK_METHOD0(postprocess, void());
+    MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
+    MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
+};
+
+class ListParseArgsTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        mockLshal = std::make_unique<NiceMock<MockLshal>>();
+        mockList = std::make_unique<MockListCommand>(mockLshal.get());
+        // ListCommand::parseArgs should parse arguments from the second element
+        optind = 1;
+    }
+    std::unique_ptr<MockLshal> mockLshal;
+    std::unique_ptr<MockListCommand> mockList;
+    std::stringstream output;
+};
+
+TEST_F(ListParseArgsTest, Args) {
+    EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"})));
+    mockList->forEachTable([](const Table& table) {
+        EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME,
+                                   TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}),
+                  table.getSelectedColumns());
+    });
+}
+
+TEST_F(ListParseArgsTest, Cmds) {
+    EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"})));
+    mockList->forEachTable([](const Table& table) {
+        EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID)))
+                << "should not print server PID with -m";
+        EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS)))
+                << "should not print client PIDs with -m";
+        EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD))
+                << "should print server cmd with -m";
+        EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS))
+                << "should print client cmds with -m";
+    });
+}
+
+TEST_F(ListParseArgsTest, DebugAndNeat) {
+    ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output)));
+    EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"})));
+    EXPECT_THAT(output.str(), StrNe(""));
+}
+
+/// Fetch Test
+
+// A set of deterministic functions to generate fake debug infos.
+static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; }
+static std::vector<pid_t> getClients(pid_t serverId) {
+    return {serverId + 1, serverId + 3};
+}
+static PidInfo getPidInfoFromId(pid_t serverId) {
+    PidInfo info;
+    info.refPids[getPtr(serverId)] = getClients(serverId);
+    info.threadUsage = 10 + serverId;
+    info.threadCount = 20 + serverId;
+    return info;
+}
+static std::string getInterfaceName(pid_t serverId) {
+    return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo";
+}
+static std::string getInstanceName(pid_t serverId) {
+    return std::to_string(serverId);
+}
+static pid_t getIdFromInstanceName(const hidl_string& instance) {
+    return atoi(instance.c_str());
+}
+static std::string getFqInstanceName(pid_t serverId) {
+    return getInterfaceName(serverId) + "/" + getInstanceName(serverId);
+}
+static std::string getCmdlineFromId(pid_t serverId) {
+    if (serverId == NO_PID) return "";
+    return "command_line_" + std::to_string(serverId);
+}
+static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; }
+static hidl_hash getHashFromId(pid_t serverId) {
+    hidl_hash hash;
+    bool isReleased = getIsReleasedFromId(serverId);
+    for (size_t i = 0; i < hash.size(); ++i) {
+        hash[i] = isReleased ? static_cast<uint8_t>(serverId) : 0u;
+    }
+    return hash;
+}
+
+// Fake service returned by mocked IServiceManager::get.
+class TestService : public IBase {
+public:
+    TestService(pid_t id) : mId(id) {}
+    hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
+        cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT });
+        return hardware::Void();
+    }
+    hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+        cb({getInterfaceName(mId), IBase::descriptor});
+        return hardware::Void();
+    }
+    hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+        cb({getHashFromId(mId), getHashFromId(0xff)});
+        return hardware::Void();
+    }
+private:
+    pid_t mId;
+};
+
+class ListTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        initMockServiceManager();
+        lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager);
+        initMockList();
+    }
+
+    void initMockList() {
+        mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get());
+        ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke(
+            [](pid_t serverPid, PidInfo* info) {
+                *info = getPidInfoFromId(serverPid);
+                return true;
+            }));
+        ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId));
+        ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() {
+            mockList->internalPostprocess();
+            size_t i = 0;
+            mockList->forEachTable([&](Table& table) {
+                table.setDescription("[fake description " + std::to_string(i++) + "]");
+            });
+        }));
+    }
+
+    void initMockServiceManager() {
+        serviceManager = new testing::NiceMock<MockServiceManager>();
+        passthruManager = new testing::NiceMock<MockServiceManager>();
+        using A = DebugInfo::Architecture;
+        ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke(
+            [] (IServiceManager::list_cb cb) {
+                cb({ getFqInstanceName(1), getFqInstanceName(2) });
+                return hardware::Void();
+            }));
+
+        ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
+            [&](const hidl_string&, const hidl_string& instance) {
+                int id = getIdFromInstanceName(instance);
+                return sp<IBase>(new TestService(id));
+            }));
+
+        ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke(
+            [] (IServiceManager::debugDump_cb cb) {
+                cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3,
+                                      getClients(3), A::IS_32BIT},
+                    InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4,
+                                      getClients(4), A::IS_32BIT}});
+                return hardware::Void();
+            }));
+
+        ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke(
+            [] (IServiceManager::debugDump_cb cb) {
+                cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5,
+                                      getClients(5), A::IS_32BIT},
+                    InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6,
+                                      getClients(6), A::IS_32BIT}});
+                return hardware::Void();
+            }));
+    }
+
+    std::stringstream err;
+    std::stringstream out;
+    std::unique_ptr<Lshal> lshal;
+    std::unique_ptr<MockListCommand> mockList;
+    sp<MockServiceManager> serviceManager;
+    sp<MockServiceManager> passthruManager;
+};
+
+TEST_F(ListTest, GetPidInfoCached) {
+    EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1);
+
+    EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
+    EXPECT_NE(nullptr, mockList->getPidInfoCached(5));
+}
+
+TEST_F(ListTest, Fetch) {
+    EXPECT_EQ(0u, mockList->fetch());
+    std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough",
+                                          "passthrough", "passthrough", "passthrough"}};
+    std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}};
+    int id = 1;
+    mockList->forEachTable([&](const Table& table) {
+        ASSERT_EQ(2u, table.size());
+        for (const auto& entry : table) {
+            const auto& transport = transports[id - 1];
+            TableEntry expected{
+                .interfaceName = getFqInstanceName(id),
+                .transport = transport,
+                .serverPid = transport == "hwbinder" ? id : NO_PID,
+                .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0,
+                .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0,
+                .serverCmdline = {},
+                .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR,
+                .clientPids = getClients(id),
+                .clientCmdlines = {},
+                .arch = archs[id - 1],
+            };
+            EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string();
+
+            ++id;
+        }
+    });
+
+}
+
+TEST_F(ListTest, DumpVintf) {
+    const std::string expected =
+        "<!-- \n"
+        "    This is a skeleton device manifest. Notes: \n"
+        "    1. android.hidl.*, android.frameworks.*, android.system.* are not included.\n"
+        "    2. If a HAL is supported in both hwbinder and passthrough transport, \n"
+        "       only hwbinder is shown.\n"
+        "    3. It is likely that HALs in passthrough transport does not have\n"
+        "       <interface> declared; users will have to write them by hand.\n"
+        "    4. A HAL with lower minor version can be overridden by a HAL with\n"
+        "       higher minor version if they have the same name and major version.\n"
+        "    5. sepolicy version is set to 0.0. It is recommended that the entry\n"
+        "       is removed from the manifest file and written by assemble_vintf\n"
+        "       at build time.\n"
+        "-->\n"
+        "<manifest version=\"1.0\" type=\"device\">\n"
+        "    <hal format=\"hidl\">\n"
+        "        <name>a.h.foo1</name>\n"
+        "        <transport>hwbinder</transport>\n"
+        "        <version>1.0</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <instance>1</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "    <hal format=\"hidl\">\n"
+        "        <name>a.h.foo2</name>\n"
+        "        <transport>hwbinder</transport>\n"
+        "        <version>2.0</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <instance>2</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "    <hal format=\"hidl\">\n"
+        "        <name>a.h.foo3</name>\n"
+        "        <transport arch=\"32\">passthrough</transport>\n"
+        "        <version>3.0</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <instance>3</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "    <hal format=\"hidl\">\n"
+        "        <name>a.h.foo4</name>\n"
+        "        <transport arch=\"32\">passthrough</transport>\n"
+        "        <version>4.0</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <instance>4</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "    <hal format=\"hidl\">\n"
+        "        <name>a.h.foo5</name>\n"
+        "        <transport arch=\"32\">passthrough</transport>\n"
+        "        <version>5.0</version>\n"
+        "    </hal>\n"
+        "    <hal format=\"hidl\">\n"
+        "        <name>a.h.foo6</name>\n"
+        "        <transport arch=\"32\">passthrough</transport>\n"
+        "        <version>6.0</version>\n"
+        "    </hal>\n"
+        "    <sepolicy>\n"
+        "        <version>0.0</version>\n"
+        "    </sepolicy>\n"
+        "</manifest>\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+
+    vintf::HalManifest m;
+    EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str()))
+        << "--init-vintf does not emit valid HAL manifest: "
+        << vintf::gHalManifestConverter.lastError();
+}
+
+// test default columns
+TEST_F(ListTest, DumpDefault) {
+    const std::string expected =
+        "[fake description 0]\n"
+        "R Interface            Thread Use Server Clients\n"
+        "  a.h.foo1@1.0::IFoo/1 11/21      1      2 4\n"
+        "Y a.h.foo2@2.0::IFoo/2 12/22      2      3 5\n"
+        "\n"
+        "[fake description 1]\n"
+        "R Interface            Thread Use Server Clients\n"
+        "  a.h.foo3@3.0::IFoo/3 N/A        N/A    4 6\n"
+        "  a.h.foo4@4.0::IFoo/4 N/A        N/A    5 7\n"
+        "\n"
+        "[fake description 2]\n"
+        "R Interface            Thread Use Server Clients\n"
+        "  a.h.foo5@5.0::IFoo/5 N/A        N/A    6 8\n"
+        "  a.h.foo6@6.0::IFoo/6 N/A        N/A    7 9\n"
+        "\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpHash) {
+    const std::string expected =
+        "[fake description 0]\n"
+        "Interface            R Hash\n"
+        "a.h.foo1@1.0::IFoo/1   0000000000000000000000000000000000000000000000000000000000000000\n"
+        "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n"
+        "\n"
+        "[fake description 1]\n"
+        "Interface            R Hash\n"
+        "a.h.foo3@3.0::IFoo/3   \n"
+        "a.h.foo4@4.0::IFoo/4   \n"
+        "\n"
+        "[fake description 2]\n"
+        "Interface            R Hash\n"
+        "a.h.foo5@5.0::IFoo/5   \n"
+        "a.h.foo6@6.0::IFoo/6   \n"
+        "\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, Dump) {
+    const std::string expected =
+        "[fake description 0]\n"
+        "Interface            Transport Arch Thread Use Server PTR              Clients\n"
+        "a.h.foo1@1.0::IFoo/1 hwbinder  64   11/21      1      0000000000002711 2 4\n"
+        "a.h.foo2@2.0::IFoo/2 hwbinder  64   12/22      2      0000000000002712 3 5\n"
+        "\n"
+        "[fake description 1]\n"
+        "Interface            Transport   Arch Thread Use Server PTR Clients\n"
+        "a.h.foo3@3.0::IFoo/3 passthrough 32   N/A        N/A    N/A 4 6\n"
+        "a.h.foo4@4.0::IFoo/4 passthrough 32   N/A        N/A    N/A 5 7\n"
+        "\n"
+        "[fake description 2]\n"
+        "Interface            Transport   Arch Thread Use Server PTR Clients\n"
+        "a.h.foo5@5.0::IFoo/5 passthrough 32   N/A        N/A    N/A 6 8\n"
+        "a.h.foo6@6.0::IFoo/6 passthrough 32   N/A        N/A    N/A 7 9\n"
+        "\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpCmdline) {
+    const std::string expected =
+        "[fake description 0]\n"
+        "Interface            Transport Arch Thread Use Server CMD     PTR              Clients CMD\n"
+        "a.h.foo1@1.0::IFoo/1 hwbinder  64   11/21      command_line_1 0000000000002711 command_line_2;command_line_4\n"
+        "a.h.foo2@2.0::IFoo/2 hwbinder  64   12/22      command_line_2 0000000000002712 command_line_3;command_line_5\n"
+        "\n"
+        "[fake description 1]\n"
+        "Interface            Transport   Arch Thread Use Server CMD PTR Clients CMD\n"
+        "a.h.foo3@3.0::IFoo/3 passthrough 32   N/A                   N/A command_line_4;command_line_6\n"
+        "a.h.foo4@4.0::IFoo/4 passthrough 32   N/A                   N/A command_line_5;command_line_7\n"
+        "\n"
+        "[fake description 2]\n"
+        "Interface            Transport   Arch Thread Use Server CMD PTR Clients CMD\n"
+        "a.h.foo5@5.0::IFoo/5 passthrough 32   N/A                   N/A command_line_6;command_line_8\n"
+        "a.h.foo6@6.0::IFoo/6 passthrough 32   N/A                   N/A command_line_7;command_line_9\n"
+        "\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpNeat) {
+    const std::string expected =
+        "a.h.foo1@1.0::IFoo/1 11/21 1   2 4\n"
+        "a.h.foo2@2.0::IFoo/2 12/22 2   3 5\n"
+        "a.h.foo3@3.0::IFoo/3 N/A   N/A 4 6\n"
+        "a.h.foo4@4.0::IFoo/4 N/A   N/A 5 7\n"
+        "a.h.foo5@5.0::IFoo/5 N/A   N/A 6 8\n"
+        "a.h.foo6@6.0::IFoo/6 N/A   N/A 7 9\n";
+
+    optind = 1; // mimic Lshal::parseArg()
+    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"})));
+    EXPECT_EQ(expected, out.str());
+    EXPECT_EQ("", err.str());
+}
+
+class HelpTest : public ::testing::Test {
+public:
+    void SetUp() override {
+        lshal = std::make_unique<Lshal>(out, err, new MockServiceManager() /* serviceManager */,
+                                        new MockServiceManager() /* passthruManager */);
+    }
+
+    std::stringstream err;
+    std::stringstream out;
+    std::unique_ptr<Lshal> lshal;
+};
+
+TEST_F(HelpTest, GlobalUsage) {
+    (void)callMain(lshal, {"lshal", "--help"}); // ignore return
+    std::string errStr = err.str();
+    EXPECT_THAT(errStr, ContainsRegex("(^|\n)commands:($|\n)"))
+        << "`lshal --help` does not contain global usage";
+    EXPECT_THAT(errStr, ContainsRegex("(^|\n)list:($|\n)"))
+        << "`lshal --help` does not contain usage for 'list' command";
+    EXPECT_THAT(errStr, ContainsRegex("(^|\n)debug:($|\n)"))
+        << "`lshal --help` does not contain usage for 'debug' command";
+    EXPECT_THAT(errStr, ContainsRegex("(^|\n)help:($|\n)"))
+        << "`lshal --help` does not contain usage for 'help' command";
+
+    err.str("");
+    (void)callMain(lshal, {"lshal", "help"}); // ignore return
+    EXPECT_EQ(errStr, err.str()) << "`lshal help` should have the same output as `lshal --help`";
+
+    err.str("");
+    EXPECT_NE(0u, callMain(lshal, {"lshal", "--unknown-option"}));
+    EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
+    EXPECT_THAT(err.str(), EndsWith(errStr))
+            << "`lshal --unknown-option` should have the same output as `lshal --help`";
+    EXPECT_EQ("", out.str());
+}
+
+TEST_F(HelpTest, UnknownOptionList1) {
+    (void)callMain(lshal, {"lshal", "help", "list"});
+    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
+        << "`lshal help list` does not contain usage for 'list' command";
+}
+
+TEST_F(HelpTest, UnknownOptionList2) {
+    EXPECT_NE(0u, callMain(lshal, {"lshal", "list", "--unknown-option"}));
+    EXPECT_THAT(err.str(), ContainsRegex("unrecognized option"));
+    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)"))
+        << "`lshal list --unknown-option` does not contain usage for 'list' command";
+    EXPECT_EQ("", out.str());
+}
+
+TEST_F(HelpTest, UnknownOptionHelp1) {
+    (void)callMain(lshal, {"lshal", "help", "help"});
+    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
+        << "`lshal help help` does not contain usage for 'help' command";
+}
+
+TEST_F(HelpTest, UnknownOptionHelp2) {
+    (void)callMain(lshal, {"lshal", "help", "--unknown-option"});
+    EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)"))
+        << "`lshal help --unknown-option` does not contain usage for 'help' command";
+    EXPECT_EQ("", out.str());
+}
+
 } // namespace lshal
 } // namespace android
 
diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h
index 45b922c..c09e8b1 100644
--- a/cmds/lshal/utils.h
+++ b/cmds/lshal/utils.h
@@ -29,15 +29,23 @@
 
 enum : unsigned int {
     OK                                      = 0,
+    // Return to Lshal::main to print help info.
     USAGE                                   = 1 << 0,
+    // no service managers
     NO_BINDERIZED_MANAGER                   = 1 << 1,
     NO_PASSTHROUGH_MANAGER                  = 1 << 2,
+    // general error in getting information from the three sources
     DUMP_BINDERIZED_ERROR                   = 1 << 3,
     DUMP_PASSTHROUGH_ERROR                  = 1 << 4,
     DUMP_ALL_LIBS_ERROR                     = 1 << 5,
+    // I/O error in reading files
     IO_ERROR                                = 1 << 6,
+    // Interface does not exist (IServiceManager::get fails)
     NO_INTERFACE                            = 1 << 7,
+    // Transaction error from hwbinder transactions
     TRANSACTION_ERROR                       = 1 << 8,
+    // No transaction error, but return value is unexpected.
+    BAD_IMPL                                = 1 << 9,
 };
 using Status = unsigned int;
 
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 93a18fc..fade8cf 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -514,7 +514,7 @@
         return;
 
     obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
-    obj->type = BINDER_TYPE_BINDER;
+    obj->hdr.type = BINDER_TYPE_BINDER;
     obj->binder = (uintptr_t)ptr;
     obj->cookie = 0;
 }
@@ -532,7 +532,7 @@
         return;
 
     obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
-    obj->type = BINDER_TYPE_HANDLE;
+    obj->hdr.type = BINDER_TYPE_HANDLE;
     obj->handle = handle;
     obj->cookie = 0;
 }
@@ -649,7 +649,7 @@
     if (!obj)
         return 0;
 
-    if (obj->type == BINDER_TYPE_HANDLE)
+    if (obj->hdr.type == BINDER_TYPE_HANDLE)
         return obj->handle;
 
     return 0;
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 35b63ec..2b5389b 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -505,7 +505,7 @@
     ALOGV("Setting Transparent Region Hint");
     Region re = Region();
 
-    for (auto r : trhc.region()) {
+    for (const auto& r : trhc.region()) {
         Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
         re.merge(rect);
     }
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 7f46087..a88733c 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -388,13 +388,13 @@
 #endif
 
 #if __ANDROID_API__ >= __ANDROID_API_O__
-/*
+/**
  * Get a reference to the sensor manager. ASensorManager is a singleton
  * per package as different packages may have access to different sensors.
  *
  * Example:
  *
- *    ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
+ *     ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz");
  *
  */
 ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName);
@@ -503,14 +503,12 @@
  * {@link ASensor_isDirectChannelTypeSupported}, respectively.
  *
  * Example:
- * \code{.cpp}
- *      ASensorManager *manager = ...;
- *      ASensor *sensor = ...;
- *      int channelId = ...;
  *
- *      ASensorManager_configureDirectReport(
- *              manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
- * \endcode
+ *     ASensorManager *manager = ...;
+ *     ASensor *sensor = ...;
+ *     int channelId = ...;
+ *
+ *     ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST);
  *
  * \param manager   the {@link ASensorManager} instance obtained from
  *                  {@link ASensorManager_getInstanceForPackage}.
@@ -530,50 +528,86 @@
 /*****************************************************************************/
 
 /**
- * Enable the selected sensor with a specified sampling period and max batch report latency.
- * Returns a negative error code on failure.
- * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
+ * Enable the selected sensor with sampling and report parameters
+ *
+ * Enable the selected sensor at a specified sampling period and max batch report latency.
+ * To disable  sensor, use {@link ASensorEventQueue_disableSensor}.
+ *
+ * \param queue {@link ASensorEventQueue} for sensor event to be report to.
+ * \param sensor {@link ASensor} to be enabled.
+ * \param samplingPeriodUs sampling period of sensor in microseconds.
+ * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are
+ *                                delievered in microseconds. For sensor streaming, set to 0.
+ * \return 0 on success or a negative error code on failure.
  */
 int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
         int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs);
 
 /**
- * Enable the selected sensor. Returns a negative error code on failure.
+ * Enable the selected sensor at default sampling rate.
+ *
+ * Start event reports of a sensor to specified sensor event queue at a default rate.
+ *
+ * \param queue {@link ASensorEventQueue} for sensor event to be report to.
+ * \param sensor {@link ASensor} to be enabled.
+ *
+ * \return 0 on success or a negative error code on failure.
  */
 int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor);
 
 /**
- * Disable the selected sensor. Returns a negative error code on failure.
+ * Disable the selected sensor.
+ *
+ * Stop event reports from the sensor to specified sensor event queue.
+ *
+ * \param queue {@link ASensorEventQueue} to be changed
+ * \param sensor {@link ASensor} to be disabled
+ * \return 0 on success or a negative error code on failure.
  */
 int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor);
 
 /**
  * Sets the delivery rate of events in microseconds for the given sensor.
+ *
+ * This function has to be called after {@link ASensorEventQueue_enableSensor}.
  * Note that this is a hint only, generally event will arrive at a higher
  * rate. It is an error to set a rate inferior to the value returned by
  * ASensor_getMinDelay().
- * Returns a negative error code on failure.
+ *
+ * \param queue {@link ASensorEventQueue} to which sensor event is delivered.
+ * \param sensor {@link ASensor} of which sampling rate to be updated.
+ * \param usec sensor sampling period (1/sampling rate) in microseconds
+ * \return 0 on sucess or a negative error code on failure.
  */
 int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec);
 
 /**
- * Returns true if there are one or more events available in the
- * sensor queue.  Returns 1 if the queue has events; 0 if
- * it does not have events; and a negative value if there is an error.
+ * Determine if a sensor event queue has pending event to be processed.
+ *
+ * \param queue {@link ASensorEventQueue} to be queried
+ * \return 1 if the queue has events; 0 if it does not have events;
+ *         or a negative value if there is an error.
  */
 int ASensorEventQueue_hasEvents(ASensorEventQueue* queue);
 
 /**
- * Returns the next available events from the queue.  Returns a negative
- * value if no events are available or an error has occurred, otherwise
- * the number of events returned.
+ * Retrieve pending events in sensor event queue
+ *
+ * Retrieve next available events from the queue to a specified event array.
+ *
+ * \param queue {@link ASensorEventQueue} to get events from
+ * \param events pointer to an array of {@link ASensorEvents}.
+ * \param count max number of event that can be filled into array event.
+ * \return number of events returned on success; negative error code when
+ *         no events are pending or an error has occurred.
  *
  * Examples:
- *   ASensorEvent event;
- *   ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
  *
- *   ASensorEvent eventBuffer[8];
- *   ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
+ *     ASensorEvent event;
+ *     ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1);
+ *
+ *     ASensorEvent eventBuffer[8];
+ *     ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8);
  *
  */
 ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index e832961..1c3fab4 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -470,22 +470,33 @@
 void IPCThreadState::processPendingDerefs()
 {
     if (mIn.dataPosition() >= mIn.dataSize()) {
-        size_t numPending = mPendingWeakDerefs.size();
-        if (numPending > 0) {
-            for (size_t i = 0; i < numPending; i++) {
-                RefBase::weakref_type* refs = mPendingWeakDerefs[i];
+        /*
+         * The decWeak()/decStrong() calls may cause a destructor to run,
+         * which in turn could have initiated an outgoing transaction,
+         * which in turn could cause us to add to the pending refs
+         * vectors; so instead of simply iterating, loop until they're empty.
+         *
+         * We do this in an outer loop, because calling decStrong()
+         * may result in something being added to mPendingWeakDerefs,
+         * which could be delayed until the next incoming command
+         * from the driver if we don't process it now.
+         */
+        while (mPendingWeakDerefs.size() > 0 || mPendingStrongDerefs.size() > 0) {
+            while (mPendingWeakDerefs.size() > 0) {
+                RefBase::weakref_type* refs = mPendingWeakDerefs[0];
+                mPendingWeakDerefs.removeAt(0);
                 refs->decWeak(mProcess.get());
             }
-            mPendingWeakDerefs.clear();
-        }
 
-        numPending = mPendingStrongDerefs.size();
-        if (numPending > 0) {
-            for (size_t i = 0; i < numPending; i++) {
-                BBinder* obj = mPendingStrongDerefs[i];
+            if (mPendingStrongDerefs.size() > 0) {
+                // We don't use while() here because we don't want to re-order
+                // strong and weak decs at all; if this decStrong() causes both a
+                // decWeak() and a decStrong() to be queued, we want to process
+                // the decWeak() first.
+                BBinder* obj = mPendingStrongDerefs[0];
+                mPendingStrongDerefs.removeAt(0);
                 obj->decStrong(mProcess.get());
             }
-            mPendingStrongDerefs.clear();
         }
     }
 }
@@ -675,7 +686,7 @@
 #if LOG_REFCOUNTS
     ALOGV("IPCThreadState::expungeHandle(%ld)\n", handle);
 #endif
-    self()->mProcess->expungeHandle(handle, binder);
+    self()->mProcess->expungeHandle(handle, binder); // NOLINT
 }
 
 status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e22179b..84ca3c0 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -104,7 +104,7 @@
 void acquire_object(const sp<ProcessState>& proc,
     const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
 {
-    switch (obj.type) {
+    switch (obj.hdr.type) {
         case BINDER_TYPE_BINDER:
             if (obj.binder) {
                 LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
@@ -140,7 +140,7 @@
         }
     }
 
-    ALOGD("Invalid object type 0x%08x", obj.type);
+    ALOGD("Invalid object type 0x%08x", obj.hdr.type);
 }
 
 void acquire_object(const sp<ProcessState>& proc,
@@ -152,7 +152,7 @@
 static void release_object(const sp<ProcessState>& proc,
     const flat_binder_object& obj, const void* who, size_t* outAshmemSize)
 {
-    switch (obj.type) {
+    switch (obj.hdr.type) {
         case BINDER_TYPE_BINDER:
             if (obj.binder) {
                 LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie);
@@ -191,7 +191,7 @@
         }
     }
 
-    ALOGE("Invalid object type 0x%08x", obj.type);
+    ALOGE("Invalid object type 0x%08x", obj.hdr.type);
 }
 
 void release_object(const sp<ProcessState>& proc,
@@ -227,17 +227,17 @@
                 ALOGE("null proxy");
             }
             const int32_t handle = proxy ? proxy->handle() : 0;
-            obj.type = BINDER_TYPE_HANDLE;
+            obj.hdr.type = BINDER_TYPE_HANDLE;
             obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
             obj.handle = handle;
             obj.cookie = 0;
         } else {
-            obj.type = BINDER_TYPE_BINDER;
+            obj.hdr.type = BINDER_TYPE_BINDER;
             obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
             obj.cookie = reinterpret_cast<uintptr_t>(local);
         }
     } else {
-        obj.type = BINDER_TYPE_BINDER;
+        obj.hdr.type = BINDER_TYPE_BINDER;
         obj.binder = 0;
         obj.cookie = 0;
     }
@@ -261,12 +261,12 @@
                     ALOGE("null proxy");
                 }
                 const int32_t handle = proxy ? proxy->handle() : 0;
-                obj.type = BINDER_TYPE_WEAK_HANDLE;
+                obj.hdr.type = BINDER_TYPE_WEAK_HANDLE;
                 obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
                 obj.handle = handle;
                 obj.cookie = 0;
             } else {
-                obj.type = BINDER_TYPE_WEAK_BINDER;
+                obj.hdr.type = BINDER_TYPE_WEAK_BINDER;
                 obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs());
                 obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get());
             }
@@ -281,13 +281,13 @@
         // but we can't do that with the different reference counting
         // implementation we are using.
         ALOGE("Unable to unflatten Binder weak reference!");
-        obj.type = BINDER_TYPE_BINDER;
+        obj.hdr.type = BINDER_TYPE_BINDER;
         obj.binder = 0;
         obj.cookie = 0;
         return finish_flatten_binder(NULL, obj, out);
 
     } else {
-        obj.type = BINDER_TYPE_BINDER;
+        obj.hdr.type = BINDER_TYPE_BINDER;
         obj.binder = 0;
         obj.cookie = 0;
         return finish_flatten_binder(NULL, obj, out);
@@ -307,7 +307,7 @@
     const flat_binder_object* flat = in.readObject(false);
 
     if (flat) {
-        switch (flat->type) {
+        switch (flat->hdr.type) {
             case BINDER_TYPE_BINDER:
                 *out = reinterpret_cast<IBinder*>(flat->cookie);
                 return finish_unflatten_binder(NULL, *flat, in);
@@ -326,7 +326,7 @@
     const flat_binder_object* flat = in.readObject(false);
 
     if (flat) {
-        switch (flat->type) {
+        switch (flat->hdr.type) {
             case BINDER_TYPE_BINDER:
                 *out = reinterpret_cast<IBinder*>(flat->cookie);
                 return finish_unflatten_binder(NULL, *flat, in);
@@ -543,7 +543,7 @@
                 = reinterpret_cast<flat_binder_object*>(mData + off);
             acquire_object(proc, *flat, this, &mOpenAshmemSize);
 
-            if (flat->type == BINDER_TYPE_FD) {
+            if (flat->hdr.type == BINDER_TYPE_FD) {
                 // If this is a file descriptor, we need to dup it so the
                 // new Parcel now owns its own fd, and can declare that we
                 // officially know we have fds.
@@ -1152,7 +1152,7 @@
 status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership)
 {
     flat_binder_object obj;
-    obj.type = BINDER_TYPE_FD;
+    obj.hdr.type = BINDER_TYPE_FD;
     obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
     obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
     obj.handle = fd;
@@ -1310,7 +1310,7 @@
         *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
 
         // remember if it's a file descriptor
-        if (val.type == BINDER_TYPE_FD) {
+        if (val.hdr.type == BINDER_TYPE_FD) {
             if (!mAllowFds) {
                 // fail before modifying our object index
                 return FDS_NOT_ALLOWED;
@@ -2132,7 +2132,7 @@
 {
     const flat_binder_object* flat = readObject(true);
 
-    if (flat && flat->type == BINDER_TYPE_FD) {
+    if (flat && flat->hdr.type == BINDER_TYPE_FD) {
         return flat->handle;
     }
 
@@ -2325,7 +2325,7 @@
         i--;
         const flat_binder_object* flat
             = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
-        if (flat->type == BINDER_TYPE_FD) {
+        if (flat->hdr.type == BINDER_TYPE_FD) {
             //ALOGI("Closing fd: %ld", flat->handle);
             close(flat->handle);
         }
@@ -2397,7 +2397,7 @@
             const flat_binder_object* flat
                 = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);
             to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
-                << TypeCode(flat->type & 0x7f7f7f00)
+                << TypeCode(flat->hdr.type & 0x7f7f7f00)
                 << " = " << flat->binder;
         }
     } else {
@@ -2618,7 +2618,7 @@
             for (size_t i=objectsSize; i<mObjectsSize; i++) {
                 const flat_binder_object* flat
                     = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
-                if (flat->type == BINDER_TYPE_FD) {
+                if (flat->hdr.type == BINDER_TYPE_FD) {
                     // will need to rescan because we may have lopped off the only FDs
                     mFdsKnown = false;
                 }
@@ -2728,7 +2728,7 @@
     for (size_t i=0; i<mObjectsSize; i++) {
         const flat_binder_object* flat
             = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
-        if (flat->type == BINDER_TYPE_FD) {
+        if (flat->hdr.type == BINDER_TYPE_FD) {
             hasFds = true;
             break;
         }
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp
index fd1dfd5..85cd739 100644
--- a/libs/binder/Value.cpp
+++ b/libs/binder/Value.cpp
@@ -182,10 +182,12 @@
 
 Value& Value::operator=(const Value& rhs)
 {
-    delete mContent;
-    mContent = rhs.mContent
-        ? rhs.mContent->clone()
-        : NULL;
+    if (this != &rhs) {
+        delete mContent;
+        mContent = rhs.mContent
+            ? rhs.mContent->clone()
+            : NULL;
+    }
     return *this;
 }
 
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index ff5912f..b14631d 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -139,6 +139,12 @@
     ASSERT_EQ(BINDER_CURRENT_PROTOCOL_VERSION, version.protocol_version);
 }
 
+TEST_F(BinderDriverInterfaceTest, OpenNoMmap) {
+    int binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+    ASSERT_GE(binderFd, 0);
+    close(binderFd);
+}
+
 TEST_F(BinderDriverInterfaceTest, WriteReadNull) {
     binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT);
 }
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index a04869a..1611e11 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -28,10 +28,19 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
+#include <sys/epoll.h>
+
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
 using namespace android;
 
+static ::testing::AssertionResult IsPageAligned(void *buf) {
+    if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0)
+        return ::testing::AssertionSuccess();
+    else
+        return ::testing::AssertionFailure() << buf << " is not page aligned";
+}
+
 static testing::Environment* binder_env;
 static char *binderservername;
 static char *binderserversuffix;
@@ -43,7 +52,10 @@
     BINDER_LIB_TEST_NOP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
     BINDER_LIB_TEST_REGISTER_SERVER,
     BINDER_LIB_TEST_ADD_SERVER,
+    BINDER_LIB_TEST_ADD_POLL_SERVER,
     BINDER_LIB_TEST_CALL_BACK,
+    BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF,
+    BINDER_LIB_TEST_DELAYED_CALL_BACK,
     BINDER_LIB_TEST_NOP_CALL_BACK,
     BINDER_LIB_TEST_GET_SELF_TRANSACTION,
     BINDER_LIB_TEST_GET_ID_TRANSACTION,
@@ -60,7 +72,7 @@
     BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
 };
 
-pid_t start_server_process(int arg2)
+pid_t start_server_process(int arg2, bool usePoll = false)
 {
     int ret;
     pid_t pid;
@@ -68,11 +80,13 @@
     int pipefd[2];
     char stri[16];
     char strpipefd1[16];
+    char usepoll[2];
     char *childargv[] = {
         binderservername,
         binderserverarg,
         stri,
         strpipefd1,
+        usepoll,
         binderserversuffix,
         NULL
     };
@@ -83,6 +97,7 @@
 
     snprintf(stri, sizeof(stri), "%d", arg2);
     snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]);
+    snprintf(usepoll, sizeof(usepoll), "%d", usePoll ? 1 : 0);
 
     pid = fork();
     if (pid == -1)
@@ -167,14 +182,14 @@
         virtual void TearDown() {
         }
     protected:
-        sp<IBinder> addServer(int32_t *idPtr = NULL)
+        sp<IBinder> addServerEtc(int32_t *idPtr, int code)
         {
             int ret;
             int32_t id;
             Parcel data, reply;
             sp<IBinder> binder;
 
-            ret = m_server->transact(BINDER_LIB_TEST_ADD_SERVER, data, &reply);
+            ret = m_server->transact(code, data, &reply);
             EXPECT_EQ(NO_ERROR, ret);
 
             EXPECT_FALSE(binder != NULL);
@@ -186,6 +201,17 @@
                 *idPtr = id;
             return binder;
         }
+
+        sp<IBinder> addServer(int32_t *idPtr = NULL)
+        {
+            return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_SERVER);
+        }
+
+        sp<IBinder> addPollServer(int32_t *idPtr = NULL)
+        {
+            return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_POLL_SERVER);
+        }
+
         void waitForReadData(int fd, int timeout_ms) {
             int ret;
             pollfd pfd = pollfd();
@@ -265,17 +291,23 @@
             pthread_mutex_unlock(&m_waitMutex);
             return ret;
         }
+        pthread_t getTriggeringThread()
+        {
+            return m_triggeringThread;
+        }
     protected:
         void triggerEvent(void) {
             pthread_mutex_lock(&m_waitMutex);
             pthread_cond_signal(&m_waitCond);
             m_eventTriggered = true;
+            m_triggeringThread = pthread_self();
             pthread_mutex_unlock(&m_waitMutex);
         };
     private:
         pthread_mutex_t m_waitMutex;
         pthread_cond_t m_waitCond;
         bool m_eventTriggered;
+        pthread_t m_triggeringThread;
 };
 
 class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent
@@ -283,6 +315,7 @@
     public:
         BinderLibTestCallBack()
             : m_result(NOT_ENOUGH_DATA)
+            , m_prev_end(NULL)
         {
         }
         status_t getResult(void)
@@ -298,16 +331,43 @@
             (void)reply;
             (void)flags;
             switch(code) {
-            case BINDER_LIB_TEST_CALL_BACK:
-                m_result = data.readInt32();
+            case BINDER_LIB_TEST_CALL_BACK: {
+                status_t status = data.readInt32(&m_result);
+                if (status != NO_ERROR) {
+                    m_result = status;
+                }
                 triggerEvent();
                 return NO_ERROR;
+            }
+            case BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF: {
+                sp<IBinder> server;
+                int ret;
+                const uint8_t *buf = data.data();
+                size_t size = data.dataSize();
+                if (m_prev_end) {
+                    /* 64-bit kernel needs at most 8 bytes to align buffer end */
+                    EXPECT_LE((size_t)(buf - m_prev_end), (size_t)8);
+                } else {
+                    EXPECT_TRUE(IsPageAligned((void *)buf));
+                }
+
+                m_prev_end = buf + size + data.objectsCount() * sizeof(binder_size_t);
+
+                if (size > 0) {
+                    server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+                    ret = server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION,
+                                           data, reply);
+                    EXPECT_EQ(NO_ERROR, ret);
+                }
+                return NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
             }
         }
 
         status_t m_result;
+        const uint8_t *m_prev_end;
 };
 
 class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent
@@ -606,6 +666,65 @@
     }
 }
 
+TEST_F(BinderLibTest, DeathNotificationThread)
+{
+    status_t ret;
+    sp<BinderLibTestCallBack> callback;
+    sp<IBinder> target = addServer();
+    ASSERT_TRUE(target != NULL);
+    sp<IBinder> client = addServer();
+    ASSERT_TRUE(client != NULL);
+
+    sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient();
+
+    ret = target->linkToDeath(testDeathRecipient);
+    EXPECT_EQ(NO_ERROR, ret);
+
+    {
+        Parcel data, reply;
+        ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY);
+        EXPECT_EQ(0, ret);
+    }
+
+    /* Make sure it's dead */
+    testDeathRecipient->waitEvent(5);
+
+    /* Now, pass the ref to another process and ask that process to
+     * call linkToDeath() on it, and wait for a response. This tests
+     * two things:
+     * 1) You still get death notifications when calling linkToDeath()
+     *    on a ref that is already dead when it was passed to you.
+     * 2) That death notifications are not directly pushed to the thread
+     *    registering them, but to the threadpool (proc workqueue) instead.
+     *
+     * 2) is tested because the thread handling BINDER_LIB_TEST_DEATH_TRANSACTION
+     * is blocked on a condition variable waiting for the death notification to be
+     * called; therefore, that thread is not available for handling proc work.
+     * So, if the death notification was pushed to the thread workqueue, the callback
+     * would never be called, and the test would timeout and fail.
+     *
+     * Note that we can't do this part of the test from this thread itself, because
+     * the binder driver would only push death notifications to the thread if
+     * it is a looper thread, which this thread is not.
+     *
+     * See b/23525545 for details.
+     */
+    {
+        Parcel data, reply;
+
+        callback = new BinderLibTestCallBack();
+        data.writeStrongBinder(target);
+        data.writeStrongBinder(callback);
+        ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY);
+        EXPECT_EQ(NO_ERROR, ret);
+    }
+
+    ret = callback->waitEvent(5);
+    EXPECT_EQ(NO_ERROR, ret);
+    ret = callback->getResult();
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
 TEST_F(BinderLibTest, PassFile) {
     int ret;
     int pipefd[2];
@@ -681,7 +800,7 @@
 
     const flat_binder_object *fb = reply.readObject(false);
     ASSERT_TRUE(fb != NULL);
-    EXPECT_EQ(BINDER_TYPE_HANDLE, fb->type);
+    EXPECT_EQ(BINDER_TYPE_HANDLE, fb->hdr.type);
     EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle));
     EXPECT_EQ((binder_uintptr_t)0, fb->cookie);
     EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32);
@@ -728,6 +847,61 @@
     }
 }
 
+TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) {
+    status_t ret;
+    Parcel data, reply;
+    sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
+    for (int i = 0; i < 2; i++) {
+        BinderLibTestBundle datai;
+        datai.appendFrom(&data, 0, data.dataSize());
+
+        data.freeData();
+        data.writeInt32(1);
+        data.writeStrongBinder(callBack);
+        data.writeInt32(BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF);
+
+        datai.appendTo(&data);
+    }
+    ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply);
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, OnewayQueueing)
+{
+    status_t ret;
+    Parcel data, data2;
+
+    sp<IBinder> pollServer = addPollServer();
+
+    sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
+    data.writeStrongBinder(callBack);
+    data.writeInt32(500000); // delay in us before calling back
+
+    sp<BinderLibTestCallBack> callBack2 = new BinderLibTestCallBack();
+    data2.writeStrongBinder(callBack2);
+    data2.writeInt32(0); // delay in us
+
+    ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, NULL, TF_ONE_WAY);
+    EXPECT_EQ(NO_ERROR, ret);
+
+    // The delay ensures that this second transaction will end up on the async_todo list
+    // (for a single-threaded server)
+    ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, NULL, TF_ONE_WAY);
+    EXPECT_EQ(NO_ERROR, ret);
+
+    // The server will ensure that the two transactions are handled in the expected order;
+    // If the ordering is not as expected, an error will be returned through the callbacks.
+    ret = callBack->waitEvent(2);
+    EXPECT_EQ(NO_ERROR, ret);
+    ret = callBack->getResult();
+    EXPECT_EQ(NO_ERROR, ret);
+
+    ret = callBack2->waitEvent(2);
+    EXPECT_EQ(NO_ERROR, ret);
+    ret = callBack2->getResult();
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
@@ -735,6 +909,7 @@
             : m_id(id)
             , m_nextServerId(id + 1)
             , m_serverStartRequested(false)
+            , m_callback(NULL)
         {
             pthread_mutex_init(&m_serverWaitMutex, NULL);
             pthread_cond_init(&m_serverWaitCond, NULL);
@@ -743,6 +918,16 @@
         {
             exit(EXIT_SUCCESS);
         }
+
+        void processPendingCall() {
+            if (m_callback != NULL) {
+                Parcel data;
+                data.writeInt32(NO_ERROR);
+                m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY);
+                m_callback = NULL;
+            }
+        }
+
         virtual status_t onTransact(uint32_t code,
                                     const Parcel& data, Parcel* reply,
                                     uint32_t flags = 0) {
@@ -774,6 +959,7 @@
                 pthread_mutex_unlock(&m_serverWaitMutex);
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_ADD_POLL_SERVER:
             case BINDER_LIB_TEST_ADD_SERVER: {
                 int ret;
                 uint8_t buf[1] = { 0 };
@@ -788,9 +974,10 @@
                 } else {
                     serverid = m_nextServerId++;
                     m_serverStartRequested = true;
+                    bool usePoll = code == BINDER_LIB_TEST_ADD_POLL_SERVER;
 
                     pthread_mutex_unlock(&m_serverWaitMutex);
-                    ret = start_server_process(serverid);
+                    ret = start_server_process(serverid, usePoll);
                     pthread_mutex_lock(&m_serverWaitMutex);
                 }
                 if (ret > 0) {
@@ -818,6 +1005,42 @@
             }
             case BINDER_LIB_TEST_NOP_TRANSACTION:
                 return NO_ERROR;
+            case BINDER_LIB_TEST_DELAYED_CALL_BACK: {
+                // Note: this transaction is only designed for use with a
+                // poll() server. See comments around epoll_wait().
+                if (m_callback != NULL) {
+                    // A callback was already pending; this means that
+                    // we received a second call while still processing
+                    // the first one. Fail the test.
+                    sp<IBinder> callback = data.readStrongBinder();
+                    Parcel data2;
+                    data2.writeInt32(UNKNOWN_ERROR);
+
+                    callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, NULL, TF_ONE_WAY);
+                } else {
+                    m_callback = data.readStrongBinder();
+                    int32_t delayUs = data.readInt32();
+                    /*
+                     * It's necessary that we sleep here, so the next
+                     * transaction the caller makes will be queued to
+                     * the async queue.
+                     */
+                    usleep(delayUs);
+
+                    /*
+                     * Now when we return, libbinder will tell the kernel
+                     * we are done with this transaction, and the kernel
+                     * can move the queued transaction to either the
+                     * thread todo worklist (for kernels without the fix),
+                     * or the proc todo worklist. In case of the former,
+                     * the next outbound call will pick up the pending
+                     * transaction, which leads to undesired reentrant
+                     * behavior. This is caught in the if() branch above.
+                     */
+                }
+
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_NOP_CALL_BACK: {
                 Parcel data2, reply2;
                 sp<IBinder> binder;
@@ -825,7 +1048,7 @@
                 if (binder == NULL) {
                     return BAD_VALUE;
                 }
-                reply2.writeInt32(NO_ERROR);
+                data2.writeInt32(NO_ERROR);
                 binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2);
                 return NO_ERROR;
             }
@@ -967,16 +1190,26 @@
         bool m_serverStartRequested;
         sp<IBinder> m_serverStarted;
         sp<IBinder> m_strongRef;
+        bool m_callbackPending;
+        sp<IBinder> m_callback;
 };
 
-int run_server(int index, int readypipefd)
+int run_server(int index, int readypipefd, bool usePoll)
 {
     binderLibTestServiceName += String16(binderserversuffix);
 
     status_t ret;
     sp<IServiceManager> sm = defaultServiceManager();
+    BinderLibTestService* testServicePtr;
     {
         sp<BinderLibTestService> testService = new BinderLibTestService(index);
+        /*
+         * We need this below, but can't hold a sp<> because it prevents the
+         * node from being cleaned up automatically. It's safe in this case
+         * because of how the tests are written.
+         */
+        testServicePtr = testService.get();
+
         if (index == 0) {
             ret = sm->addService(binderLibTestServiceName, testService);
         } else {
@@ -994,8 +1227,53 @@
     if (ret)
         return 1;
     //printf("%s: joinThreadPool\n", __func__);
-    ProcessState::self()->startThreadPool();
-    IPCThreadState::self()->joinThreadPool();
+    if (usePoll) {
+        int fd;
+        struct epoll_event ev;
+        int epoll_fd;
+        IPCThreadState::self()->setupPolling(&fd);
+        if (fd < 0) {
+            return 1;
+        }
+        IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER
+
+        epoll_fd = epoll_create1(0);
+        if (epoll_fd == -1) {
+            return 1;
+        }
+
+        ev.events = EPOLLIN;
+        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+            return 1;
+        }
+
+        while (1) {
+             /*
+              * We simulate a single-threaded process using the binder poll
+              * interface; besides handling binder commands, it can also
+              * issue outgoing transactions, by storing a callback in
+              * m_callback and setting m_callbackPending.
+              *
+              * processPendingCall() will then issue that transaction.
+              */
+             struct epoll_event events[1];
+             int numEvents = epoll_wait(epoll_fd, events, 1, 1000);
+             if (numEvents < 0) {
+                 if (errno == EINTR) {
+                     continue;
+                 }
+                 return 1;
+             }
+             if (numEvents > 0) {
+                 IPCThreadState::self()->handlePolledCommands();
+                 IPCThreadState::self()->flushCommands(); // flush BC_FREE_BUFFER
+                 testServicePtr->processPendingCall();
+             }
+        }
+    } else {
+        ProcessState::self()->startThreadPool();
+        IPCThreadState::self()->joinThreadPool();
+    }
     //printf("%s: joinThreadPool returned\n", __func__);
     return 1; /* joinThreadPool should not return */
 }
@@ -1009,9 +1287,9 @@
         binderservername = argv[0];
     }
 
-    if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
-        binderserversuffix = argv[4];
-        return run_server(atoi(argv[2]), atoi(argv[3]));
+    if (argc == 6 && !strcmp(argv[1], binderserverarg)) {
+        binderserversuffix = argv[5];
+        return run_server(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) == 1);
     }
     binderserversuffix = new char[16];
     snprintf(binderserversuffix, 16, "%d", getpid());
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 3996305..21debc1 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -96,6 +96,7 @@
         "IProducerListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
+        "LayerDebugInfo.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 625dc5b..c5cab2d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -764,7 +764,7 @@
     input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
             &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
             &getFrameTimestamps);
-    Region surfaceDamage = input.getSurfaceDamage();
+    const Region& surfaceDamage = input.getSurfaceDamage();
 
     if (acquireFence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0a0d112..8e7f814 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -28,6 +28,7 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
+#include <gui/LayerDebugInfo.h>
 
 #include <private/gui/LayerState.h>
 
@@ -469,6 +470,36 @@
         return result;
     }
 
+    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
+    {
+        if (!outLayers) {
+            return UNEXPECTED_NULL;
+        }
+
+        Parcel data, reply;
+
+        status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        int32_t result = 0;
+        err = reply.readInt32(&result);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        outLayers->clear();
+        return reply.readParcelableVector(outLayers);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -763,6 +794,17 @@
             }
             return injectVSync(when);
         }
+        case GET_LAYER_DEBUG_INFO: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            std::vector<LayerDebugInfo> outLayers;
+            status_t result = getLayerDebugInfo(&outLayers);
+            reply->writeInt32(result);
+            if (result == NO_ERROR)
+            {
+                result = reply->writeParcelableVector(outLayers);
+            }
+            return result;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
new file mode 100644
index 0000000..57ddde0
--- /dev/null
+++ b/libs/gui/LayerDebugInfo.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/LayerDebugInfo.h>
+
+#include <ui/DebugUtils.h>
+
+#include <binder/Parcel.h>
+
+#include <utils/String8.h>
+
+using namespace android;
+
+#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)
+
+namespace android {
+
+status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const {
+    RETURN_ON_ERROR(parcel->writeCString(mName.c_str()));
+    RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str()));
+    RETURN_ON_ERROR(parcel->writeCString(mType.c_str()));
+    RETURN_ON_ERROR(parcel->write(mTransparentRegion));
+    RETURN_ON_ERROR(parcel->write(mVisibleRegion));
+    RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion));
+    RETURN_ON_ERROR(parcel->writeUint32(mLayerStack));
+    RETURN_ON_ERROR(parcel->writeFloat(mX));
+    RETURN_ON_ERROR(parcel->writeFloat(mY));
+    RETURN_ON_ERROR(parcel->writeUint32(mZ));
+    RETURN_ON_ERROR(parcel->writeInt32(mWidth));
+    RETURN_ON_ERROR(parcel->writeInt32(mHeight));
+    RETURN_ON_ERROR(parcel->write(mCrop));
+    RETURN_ON_ERROR(parcel->write(mFinalCrop));
+    RETURN_ON_ERROR(parcel->writeFloat(mAlpha));
+    RETURN_ON_ERROR(parcel->writeUint32(mFlags));
+    RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat));
+    RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace)));
+    for (size_t index = 0; index < 4; index++) {
+        RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2]));
+    }
+    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth));
+    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight));
+    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride));
+    RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat));
+    RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames));
+    RETURN_ON_ERROR(parcel->writeBool(mRefreshPending));
+    RETURN_ON_ERROR(parcel->writeBool(mIsOpaque));
+    RETURN_ON_ERROR(parcel->writeBool(mContentDirty));
+    return NO_ERROR;
+}
+
+status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) {
+    mName = parcel->readCString();
+    RETURN_ON_ERROR(parcel->errorCheck());
+    mParentName = parcel->readCString();
+    RETURN_ON_ERROR(parcel->errorCheck());
+    mType = parcel->readCString();
+    RETURN_ON_ERROR(parcel->errorCheck());
+    RETURN_ON_ERROR(parcel->read(mTransparentRegion));
+    RETURN_ON_ERROR(parcel->read(mVisibleRegion));
+    RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion));
+    RETURN_ON_ERROR(parcel->readUint32(&mLayerStack));
+    RETURN_ON_ERROR(parcel->readFloat(&mX));
+    RETURN_ON_ERROR(parcel->readFloat(&mY));
+    RETURN_ON_ERROR(parcel->readUint32(&mZ));
+    RETURN_ON_ERROR(parcel->readInt32(&mWidth));
+    RETURN_ON_ERROR(parcel->readInt32(&mHeight));
+    RETURN_ON_ERROR(parcel->read(mCrop));
+    RETURN_ON_ERROR(parcel->read(mFinalCrop));
+    RETURN_ON_ERROR(parcel->readFloat(&mAlpha));
+    RETURN_ON_ERROR(parcel->readUint32(&mFlags));
+    RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat));
+    // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways?
+    mDataSpace = static_cast<android_dataspace>(parcel->readUint32());
+    RETURN_ON_ERROR(parcel->errorCheck());
+    for (size_t index = 0; index < 4; index++) {
+        RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2]));
+    }
+    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth));
+    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight));
+    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride));
+    RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat));
+    RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames));
+    RETURN_ON_ERROR(parcel->readBool(&mRefreshPending));
+    RETURN_ON_ERROR(parcel->readBool(&mIsOpaque));
+    RETURN_ON_ERROR(parcel->readBool(&mContentDirty));
+    return NO_ERROR;
+}
+
+std::string to_string(const LayerDebugInfo& info) {
+    String8 result;
+
+    result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
+    info.mTransparentRegion.dump(result, "TransparentRegion");
+    info.mVisibleRegion.dump(result, "VisibleRegion");
+    info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
+
+    result.appendFormat("      layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+            info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY),
+            info.mWidth, info.mHeight);
+
+    result.appendFormat("crop=%s, finalCrop=%s, ",
+            to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str());
+    result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
+    result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
+    result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
+    result.appendFormat("alpha=%.3f, flags=0x%08x, ",
+            static_cast<double>(info.mAlpha), info.mFlags);
+    result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]",
+            static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]),
+            static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1]));
+    result.append("\n");
+    result.appendFormat("      parent=%s\n", info.mParentName.c_str());
+    result.appendFormat("      activeBuffer=[%4ux%4u:%4u,%s],",
+            info.mActiveBufferWidth, info.mActiveBufferHeight,
+            info.mActiveBufferStride,
+            decodePixelFormat(info.mActiveBufferFormat).c_str());
+    result.appendFormat(" queued-frames=%d, mRefreshPending=%d",
+            info.mNumQueuedFrames, info.mRefreshPending);
+    result.append("\n");
+    return std::string(result.c_str());
+}
+
+} // android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index f80ba00..b226742 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -39,6 +39,7 @@
 struct DisplayState;
 struct DisplayInfo;
 struct DisplayStatInfo;
+class LayerDebugInfo;
 class HdrCapabilities;
 class IDisplayEventConnection;
 class IGraphicBufferProducer;
@@ -195,6 +196,12 @@
     virtual status_t enableVSyncInjections(bool enable) = 0;
 
     virtual status_t injectVSync(nsecs_t when) = 0;
+
+    /* Gets the list of active layers in Z order for debugging purposes
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -229,6 +236,7 @@
         SET_ACTIVE_COLOR_MODE,
         ENABLE_VSYNC_INJECTIONS,
         INJECT_VSYNC,
+        GET_LAYER_DEBUG_INFO,
         CREATE_SCOPED_CONNECTION
     };
 
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
new file mode 100644
index 0000000..8453e04
--- /dev/null
+++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcelable.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/Region.h>
+
+#include <string>
+
+namespace android {
+
+/* Class for transporting debug info from SurfaceFlinger to authorized
+ * recipients.  The class is intended to be a data container. There are
+ * no getters or setters.
+ */
+class LayerDebugInfo : public Parcelable {
+public:
+    LayerDebugInfo() = default;
+    LayerDebugInfo(const LayerDebugInfo&) = default;
+    virtual ~LayerDebugInfo() = default;
+
+    virtual status_t writeToParcel(Parcel* parcel) const;
+    virtual status_t readFromParcel(const Parcel* parcel);
+
+    std::string mName = std::string("NOT FILLED");
+    std::string mParentName = std::string("NOT FILLED");
+    std::string mType = std::string("NOT FILLED");
+    Region mTransparentRegion = Region::INVALID_REGION;
+    Region mVisibleRegion = Region::INVALID_REGION;
+    Region mSurfaceDamageRegion = Region::INVALID_REGION;
+    uint32_t mLayerStack = 0;
+    float mX = 0.f;
+    float mY = 0.f;
+    uint32_t mZ = 0 ;
+    int32_t mWidth = -1;
+    int32_t mHeight = -1;
+    Rect mCrop = Rect::INVALID_RECT;
+    Rect mFinalCrop = Rect::INVALID_RECT;
+    float mAlpha = 0.f;
+    uint32_t mFlags = 0;
+    PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
+    android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN;
+    // Row-major transform matrix (SurfaceControl::setMatrix())
+    float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}};
+    int32_t mActiveBufferWidth = -1;
+    int32_t mActiveBufferHeight = -1;
+    int32_t mActiveBufferStride = 0;
+    PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE;
+    int32_t mNumQueuedFrames = -1;
+    bool mRefreshPending = false;
+    bool mIsOpaque = false;
+    bool mContentDirty = false;
+};
+
+std::string to_string(const LayerDebugInfo& info);
+
+} // namespace android
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 8bb705c..c15209d 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -90,6 +90,16 @@
     status_t    setFlags(uint32_t flags, uint32_t mask);
     status_t    setTransparentRegionHint(const Region& transparent);
     status_t    setAlpha(float alpha=1.0f);
+
+    // Experimentarily it appears that the matrix transforms the
+    // on-screen rectangle and it's contents before the position is
+    // applied.
+    //
+    // TODO: Test with other combinations to find approximate transformation rules.
+    //
+    // For example:
+    // Layer sized (W,H) set to position (x,y) with matrix M=[-1, 0, 0, 1] (Horizontal flip) gives
+    // [((0, 0), (W, H)) x M] + (x,y) = ((-W, 0), (0, H)) + (x,y) = ((-W + x, y), (x, H+y))
     status_t    setMatrix(float dsdx, float dtdx, float dtdy, float dsdy);
     status_t    setCrop(const Rect& crop);
     status_t    setFinalCrop(const Rect& crop);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index e18af17..45e95a5 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -539,6 +539,9 @@
         return NO_ERROR;
     }
     status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; }
+    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override {
+        return NO_ERROR;
+    }
 
 protected:
     IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d8dc957..9abd04c 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -450,7 +450,7 @@
             break;
         }
 
-        case AINPUT_EVENT_TYPE_MOTION: {
+        case InputMessage::TYPE_MOTION: {
             ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
             if (batchIndex >= 0) {
                 Batch& batch = mBatches.editItemAt(batchIndex);
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index b174fa8..62acea3 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -556,6 +556,46 @@
     return true;
 }
 
+/*
+ * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to
+ * the default implementation
+ */
+static float solveUnweightedLeastSquaresDeg2(const float* x, const float* y, size_t count) {
+    float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
+
+    for (size_t i = 0; i < count; i++) {
+        float xi = x[i];
+        float yi = y[i];
+        float xi2 = xi*xi;
+        float xi3 = xi2*xi;
+        float xi4 = xi3*xi;
+        float xi2yi = xi2*yi;
+        float xiyi = xi*yi;
+
+        sxi += xi;
+        sxi2 += xi2;
+        sxiyi += xiyi;
+        sxi2yi += xi2yi;
+        syi += yi;
+        sxi3 += xi3;
+        sxi4 += xi4;
+    }
+
+    float Sxx = sxi2 - sxi*sxi / count;
+    float Sxy = sxiyi - sxi*syi / count;
+    float Sxx2 = sxi3 - sxi*sxi2 / count;
+    float Sx2y = sxi2yi - sxi2*syi / count;
+    float Sx2x2 = sxi4 - sxi2*sxi2 / count;
+
+    float numerator = Sxy*Sx2x2 - Sx2y*Sxx2;
+    float denominator = Sxx*Sx2x2 - Sxx2*Sxx2;
+    if (denominator == 0) {
+        ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2);
+        return 0;
+    }
+    return numerator/denominator;
+}
+
 bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
         VelocityTracker::Estimator* outEstimator) const {
     outEstimator->clear();
@@ -597,6 +637,19 @@
         degree = m - 1;
     }
     if (degree >= 1) {
+        if (degree == 2 && mWeighting == WEIGHTING_NONE) { // optimize unweighted, degree=2 fit
+            outEstimator->time = newestMovement.eventTime;
+            outEstimator->degree = 2;
+            outEstimator->confidence = 1;
+            outEstimator->xCoeff[0] = 0; // only slope is calculated, set rest of coefficients = 0
+            outEstimator->yCoeff[0] = 0;
+            outEstimator->xCoeff[1] = solveUnweightedLeastSquaresDeg2(time, x, m);
+            outEstimator->yCoeff[1] = solveUnweightedLeastSquaresDeg2(time, y, m);
+            outEstimator->xCoeff[2] = 0;
+            outEstimator->yCoeff[2] = 0;
+            return true;
+        }
+
         float xdet, ydet;
         uint32_t n = degree + 1;
         if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet)
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 171a627..940ff5a 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -16,9 +16,12 @@
     name: "libsensor",
 
     clang: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     cppflags: [
         "-Weverything",
-        "-Werror",
 
         // The static constructors and destructors in this library have not been noted to
         // introduce significant overheads
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp
index 9d530fc..9fd84bc 100644
--- a/libs/sensor/tests/Android.bp
+++ b/libs/sensor/tests/Android.bp
@@ -17,6 +17,8 @@
 
     clang: true,
 
+    cflags: ["-Wall", "-Werror"],
+
     srcs: [
         "Sensor_test.cpp",
     ],
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index d5676cc..2d72944 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -16,10 +16,13 @@
 
 #include <ui/DebugUtils.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 
 #include <android-base/stringprintf.h>
 #include <string>
 
+using android::base::StringPrintf;
+
 std::string decodeStandard(android_dataspace dataspace) {
     const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
     switch (dataspaceSelect) {
@@ -187,7 +190,7 @@
 
 std::string dataspaceDetails(android_dataspace dataspace) {
     if (dataspace == 0) {
-        return "Default (0)";
+        return "Default";
     }
     return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(),
                                        decodeTransfer(dataspace).c_str(),
@@ -262,3 +265,7 @@
             return android::base::StringPrintf("Unknown %#08x", format);
     }
 }
+
+std::string to_string(const android::Rect& rect) {
+    return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom);
+}
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d52c508..1634328 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -99,7 +99,7 @@
 {
     int32_t fenceFd = -1;
     status_t error = unlockAsync(handle, &fenceFd);
-    if (error == NO_ERROR) {
+    if (error == NO_ERROR && fenceFd >= 0) {
         sync_wait(fenceFd, -1);
         close(fenceFd);
     }
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 30f4a59..dad9446 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -21,9 +21,14 @@
 
 #include <string>
 
+namespace android {
+class Rect;
+}
+
 std::string decodeStandard(android_dataspace dataspace);
 std::string decodeTransfer(android_dataspace dataspace);
 std::string decodeRange(android_dataspace dataspace);
 std::string dataspaceDetails(android_dataspace dataspace);
 std::string decodeColorMode(android_color_mode colormode);
 std::string decodePixelFormat(android::PixelFormat format);
+std::string to_string(const android::Rect& rect);
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index b3da120..fb69d5c 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -230,7 +230,7 @@
   // Standalones only create the composer client once and then use SetPowerMode
   // to control the screen on pause/resume.
   if (!is_standalone_device_ || !composer_) {
-    composer_.reset(new Hwc2::Composer(false));
+    composer_.reset(new Hwc2::Composer("default"));
     composer_callback_ = new ComposerCallback;
     composer_->registerCallback(composer_callback_);
     Layer::SetComposer(composer_.get());
diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h
index 36acff9..25033f2 100644
--- a/opengl/include/GLES/gl.h
+++ b/opengl/include/GLES/gl.h
@@ -51,6 +51,7 @@
 #ifndef GL_VERSION_ES_CM_1_0
 #define GL_VERSION_ES_CM_1_0 1
 typedef void GLvoid;
+typedef char GLchar;
 typedef unsigned int GLenum;
 #include <KHR/khrplatform.h>
 typedef khronos_float_t GLfloat;
diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h
index b6fe620..1a150e3 100644
--- a/opengl/include/GLES/glext.h
+++ b/opengl/include/GLES/glext.h
@@ -104,7 +104,6 @@
 
 #ifndef GL_OES_byte_coordinates
 #define GL_OES_byte_coordinates 1
-typedef khronos_int8_t GLbyte;
 #endif /* GL_OES_byte_coordinates */
 
 #ifndef GL_OES_compressed_ETC1_RGB8_sub_texture
@@ -128,7 +127,6 @@
 
 #ifndef GL_OES_draw_texture
 #define GL_OES_draw_texture 1
-typedef short GLshort;
 #define GL_TEXTURE_CROP_RECT_OES          0x8B9D
 typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height);
 typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height);
@@ -409,7 +407,6 @@
 
 #ifndef GL_OES_single_precision
 #define GL_OES_single_precision 1
-typedef khronos_float_t GLclampf;
 typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth);
 typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation);
 typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f);
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 371239d..399affc 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -389,7 +389,7 @@
         static bool find(std::string& result,
                 const std::string& pattern, const char* const search, bool exact) {
             if (exact) {
-                std::string absolutePath = std::string(search) + "/" + pattern;
+                std::string absolutePath = std::string(search) + "/" + pattern + ".so";
                 if (!access(absolutePath.c_str(), R_OK)) {
                     result = absolutePath;
                     return true;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 837cfa9..72b4823 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -116,17 +116,23 @@
     if (gl_extensions.empty()) {
         // call the implementation's glGetString(GL_EXTENSIONS)
         const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS);
-        gl_extensions = exts;
-        if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
-            gl_extensions.insert(0, "GL_EXT_debug_marker ");
-        }
 
-        // tokenize the supported extensions for the glGetStringi() wrapper
-        std::stringstream ss;
-        std::string str;
-        ss << gl_extensions;
-        while (ss >> str) {
-            tokenized_gl_extensions.push_back(str);
+        // If this context is sharing with another context, and the other context was reset
+        // e.g. due to robustness failure, this context might also be reset and glGetString can
+        // return NULL.
+        if (exts) {
+            gl_extensions = exts;
+            if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) {
+                gl_extensions.insert(0, "GL_EXT_debug_marker ");
+            }
+
+            // tokenize the supported extensions for the glGetStringi() wrapper
+            std::stringstream ss;
+            std::string str;
+            ss << gl_extensions;
+            while (ss >> str) {
+                tokenized_gl_extensions.push_back(str);
+            }
         }
     }
 }
diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp
index 69e56ff..5956366 100644
--- a/opengl/tests/hwc/hwcRects.cpp
+++ b/opengl/tests/hwc/hwcRects.cpp
@@ -170,7 +170,7 @@
 static EGLint width, height;
 
 // Function prototypes
-static Rectangle parseRect(string rectStr);
+static Rectangle parseRect(const string& rectStr);
 void init(void);
 void printSyntax(const char *cmd);
 
@@ -358,7 +358,7 @@
 
 // Parse string description of rectangle and add it to list of rectangles
 // to be rendered.
-static Rectangle parseRect(string rectStr)
+static Rectangle parseRect(const string& rectStr)
 {
     int rv;
     string str;
diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp
index ca91b8e..0227164 100644
--- a/services/schedulerservice/Android.bp
+++ b/services/schedulerservice/Android.bp
@@ -3,6 +3,7 @@
     srcs: [
         "SchedulingPolicyService.cpp",
     ],
+    cflags: ["-Wall", "-Werror"],
     shared_libs: [
         "libhidlbase",
         "libhidltransport",
diff --git a/services/schedulerservice/SchedulingPolicyService.cpp b/services/schedulerservice/SchedulingPolicyService.cpp
index 1f6ed57..a1106cf 100644
--- a/services/schedulerservice/SchedulingPolicyService.cpp
+++ b/services/schedulerservice/SchedulingPolicyService.cpp
@@ -17,6 +17,8 @@
 
 #include "SchedulingPolicyService.h"
 
+#include <private/android_filesystem_config.h> // for AID_CAMERASERVER
+
 #include <log/log.h>
 #include <hwbinder/IPCThreadState.h>
 #include <mediautils/SchedulingPolicyService.h>
@@ -28,8 +30,9 @@
 namespace implementation {
 
 bool SchedulingPolicyService::isAllowed() {
-    // TODO(b/37291237)
-    return true;
+    using ::android::hardware::IPCThreadState;
+
+    return IPCThreadState::self()->getCallingUid() == AID_CAMERASERVER;
 }
 
 Return<bool> SchedulingPolicyService::requestPriority(int32_t pid, int32_t tid, int32_t priority) {
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 8d381b1..a7f3a52 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -14,6 +14,7 @@
         "RecentEventLogger.cpp",
         "RotationVectorSensor.cpp",
         "SensorDevice.cpp",
+        "SensorDeviceUtils.cpp",
         "SensorDirectConnection.cpp",
         "SensorEventConnection.cpp",
         "SensorFusion.cpp",
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 7d9b0b7..535d0db 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -26,11 +26,10 @@
 #include <cinttypes>
 #include <thread>
 
-using android::hardware::hidl_vec;
-
 using namespace android::hardware::sensors::V1_0;
 using namespace android::hardware::sensors::V1_0::implementation;
-
+using android::hardware::hidl_vec;
+using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -52,7 +51,8 @@
     }
 }
 
-SensorDevice::SensorDevice() : mHidlTransportErrors(20) {
+SensorDevice::SensorDevice()
+        : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) {
     if (!connectHidlService()) {
         return;
     }
@@ -87,35 +87,29 @@
 }
 
 bool SensorDevice::connectHidlService() {
-    // SensorDevice may wait upto 100ms * 10 = 1s for hidl service.
-    constexpr auto RETRY_DELAY = std::chrono::milliseconds(100);
+    // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
     size_t retry = 10;
 
-    while (true) {
-        int initStep = 0;
+    while (retry-- > 0) {
         mSensors = ISensors::getService();
-        if (mSensors != nullptr) {
-            ++initStep;
-            // Poke ISensor service. If it has lingering connection from previous generation of
-            // system server, it will kill itself. There is no intention to handle the poll result,
-            // which will be done since the size is 0.
-            if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
-                // ok to continue
-                break;
-            }
-            // hidl service is restarting, pointer is invalid.
-            mSensors = nullptr;
-        }
-
-        if (--retry <= 0) {
-            ALOGE("Cannot connect to ISensors hidl service!");
+        if (mSensors == nullptr) {
+            // no sensor hidl service found
             break;
         }
-        // Delay 100ms before retry, hidl service is expected to come up in short time after
-        // crash.
-        ALOGI("%s unsuccessful, try again soon (remaining retry %zu).",
-                (initStep == 0) ? "getService()" : "poll() check", retry);
-        std::this_thread::sleep_for(RETRY_DELAY);
+
+        mRestartWaiter->reset();
+        // Poke ISensor service. If it has lingering connection from previous generation of
+        // system server, it will kill itself. There is no intention to handle the poll result,
+        // which will be done since the size is 0.
+        if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
+            // ok to continue
+            break;
+        }
+
+        // hidl service is restarting, pointer is invalid.
+        mSensors = nullptr;
+        ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
+        mRestartWaiter->wait();
     }
     return (mSensors != nullptr);
 }
@@ -173,7 +167,7 @@
 }
 
 status_t SensorDevice::initCheck() const {
-    return mSensors != NULL ? NO_ERROR : NO_INIT;
+    return mSensors != nullptr ? NO_ERROR : NO_INIT;
 }
 
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index fd6cee6..6d75051 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SENSOR_DEVICE_H
 #define ANDROID_SENSOR_DEVICE_H
 
+#include "SensorDeviceUtils.h"
 #include "SensorServiceUtils.h"
 
 #include <sensor/Sensor.h>
@@ -39,12 +40,9 @@
 namespace android {
 
 // ---------------------------------------------------------------------------
-using SensorServiceUtil::Dumpable;
-using hardware::Return;
 
-class SensorDevice : public Singleton<SensorDevice>, public Dumpable {
+class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable {
 public:
-
     class HidlTransportErrorLog {
      public:
 
@@ -105,7 +103,7 @@
 private:
     friend class Singleton<SensorDevice>;
 
-    sp<android::hardware::sensors::V1_0::ISensors> mSensors;
+    sp<hardware::sensors::V1_0::ISensors> mSensors;
     Vector<sensor_t> mSensorList;
     std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
 
@@ -174,6 +172,8 @@
         }
         return std::move(ret);
     }
+    //TODO(b/67425500): remove waiter after bug is resolved.
+    sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
 
     bool isClientDisabled(void* ident);
     bool isClientDisabledLocked(void* ident);
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
new file mode 100644
index 0000000..b1344be
--- /dev/null
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SensorDeviceUtils.h"
+
+#include <android/hardware/sensors/1.0/ISensors.h>
+#include <utils/Log.h>
+
+#include <chrono>
+#include <thread>
+
+using ::android::hardware::Void;
+using namespace android::hardware::sensors::V1_0;
+
+namespace android {
+namespace SensorDeviceUtils {
+
+HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter()
+        : mRegistered(ISensors::registerForNotifications("default", this)) {
+}
+
+Return<void> HidlServiceRegistrationWaiter::onRegistration(
+        const hidl_string &fqName, const hidl_string &name, bool preexisting) {
+    ALOGV("onRegistration fqName %s, name %s, preexisting %d",
+          fqName.c_str(), name.c_str(), preexisting);
+
+    {
+        std::lock_guard<std::mutex> lk(mLock);
+        mRestartObserved = true;
+    }
+    mCondition.notify_all();
+    return Void();
+}
+
+void HidlServiceRegistrationWaiter::reset() {
+    std::lock_guard<std::mutex> lk(mLock);
+    mRestartObserved = false;
+}
+
+bool HidlServiceRegistrationWaiter::wait() {
+    constexpr int DEFAULT_WAIT_MS = 100;
+    constexpr int TIMEOUT_MS = 1000;
+
+    if (!mRegistered) {
+        ALOGW("Cannot register service notification, use default wait(%d ms)", DEFAULT_WAIT_MS);
+        std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_WAIT_MS));
+        // not sure if service is actually restarted
+        return false;
+    }
+
+    std::unique_lock<std::mutex> lk(mLock);
+    return mCondition.wait_for(lk, std::chrono::milliseconds(TIMEOUT_MS),
+            [this]{return mRestartObserved;});
+}
+
+} // namespace SensorDeviceUtils
+} // namespace android
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
new file mode 100644
index 0000000..da36928
--- /dev/null
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SENSOR_DEVICE_UTIL
+#define ANDROID_SENSOR_DEVICE_UTIL
+
+#include <android/hidl/manager/1.0/IServiceNotification.h>
+
+#include <condition_variable>
+#include <thread>
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hidl::manager::V1_0::IServiceNotification;
+
+namespace android {
+namespace SensorDeviceUtils {
+
+class HidlServiceRegistrationWaiter : public IServiceNotification {
+public:
+
+    HidlServiceRegistrationWaiter();
+
+    Return<void> onRegistration(const hidl_string &fqName,
+                                const hidl_string &name,
+                                bool preexisting) override;
+
+    void reset();
+
+    /**
+     * Wait for service restart
+     *
+     * @return true if service is restart since last reset(); false otherwise.
+     */
+    bool wait();
+private:
+    const bool mRegistered;
+
+    std::mutex mLock;
+    std::condition_variable mCondition;
+    bool mRestartObserved;
+};
+
+} // namespace SensorDeviceUtils
+} // namespace android;
+
+#endif // ANDROID_SENSOR_SERVICE_UTIL
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cc93105..4775e4e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -2,3 +2,5 @@
     name: "libsurfaceflingerincludes",
     export_include_dirs: ["."],
 }
+
+subdirs = ["tests/fakehwc"]
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b28c9ba..248ef53 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -65,7 +65,7 @@
 using namespace android::hardware::configstore::V1_0;
 
 static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs,
-        &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) == 3;
+        &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) >= 3;
 
 #if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle)
 // Dummy implementation in case it is missing.
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index cf01ad0..7d6d988 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -157,15 +157,11 @@
     write64(metadata.usage);
 }
 
-Composer::Composer(bool useVrComposer)
+Composer::Composer(const std::string& serviceName)
     : mWriter(kWriterInitialSize),
-      mIsUsingVrComposer(useVrComposer)
+      mIsUsingVrComposer(serviceName == std::string("vr"))
 {
-    if (mIsUsingVrComposer) {
-        mComposer = IComposer::getService("vr");
-    } else {
-        mComposer = IComposer::getService(); // use default name
-    }
+    mComposer = IComposer::getService(serviceName);
 
     if (mComposer == nullptr) {
         LOG_ALWAYS_FATAL("failed to get hwcomposer service");
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 533509b..31a3c1d 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -136,7 +136,7 @@
 // Composer is a wrapper to IComposer, a proxy to server-side composer.
 class Composer {
 public:
-    Composer(bool useVrComposer);
+    Composer(const std::string& serviceName);
 
     std::vector<IComposer::Capability> getCapabilities();
     std::string dumpDebugInfo();
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 1ac21c6..93c6d54 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -34,6 +34,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/Surface.h>
 
+#include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 
@@ -103,6 +104,7 @@
     sp<Fence> acquireFence(Fence::NO_FENCE);
     android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN;
     status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
+    mDataSpace = dataspace;
     if (result != NO_ERROR) {
         ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
                 strerror(-result), result);
@@ -249,7 +251,10 @@
 #endif
 
 void FramebufferSurface::dumpAsString(String8& result) const {
-    ConsumerBase::dumpState(result);
+    Mutex::Autolock lock(mMutex);
+    result.appendFormat("FramebufferSurface: dataspace: %s(%d)\n",
+                        dataspaceDetails(mDataSpace).c_str(), mDataSpace);
+    ConsumerBase::dumpLocked(result, "");
 }
 
 void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 69a72d7..a1756ca 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -83,6 +83,13 @@
     // or the buffer is not associated with a slot.
     int mCurrentBufferSlot;
 
+    // mDataSpace is the dataspace of the current composition buffer for
+    // this FramebufferSurface. It will be 0 when HWC is doing the
+    // compositing. Otherwise it will display the dataspace of the buffer
+    // use for compositing which can change as wide-color content is
+    // on/off.
+    android_dataspace mDataSpace;
+
     // mCurrentBuffer is the current buffer or NULL to indicate that there is
     // no current buffer.
     sp<GraphicBuffer> mCurrentBuffer;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ae44ae0..78c0c85 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -98,8 +98,8 @@
 
 // Device methods
 
-Device::Device(bool useVrComposer)
-  : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
+Device::Device(const std::string& serviceName)
+  : mComposer(std::make_unique<Hwc2::Composer>(serviceName)),
     mCapabilities(),
     mDisplays(),
     mRegisteredCallback(false)
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 949f0e3..fbe4c7e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -79,10 +79,9 @@
 class Device
 {
 public:
-    // useVrComposer is passed to the composer HAL. When true, the composer HAL
-    // will use the vr composer service, otherwise it uses the real hardware
-    // composer.
-    Device(bool useVrComposer);
+    // Service name is expected to be 'default' or 'vr' for normal use.
+    // 'vr' will slightly modify the behavior of the mComposer.
+    Device(const std::string& serviceName);
 
     void registerCallback(ComposerCallback* callback, int32_t sequenceId);
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 3f3c67b..b096a3a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -59,7 +59,7 @@
 
 // ---------------------------------------------------------------------------
 
-HWComposer::HWComposer(bool useVrComposer)
+HWComposer::HWComposer(const std::string& serviceName)
     : mHwcDevice(),
       mDisplayData(2),
       mFreeDisplaySlots(),
@@ -73,7 +73,7 @@
         mVSyncCounts[i] = 0;
     }
 
-    mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
+    mHwcDevice = std::make_unique<HWC2::Device>(serviceName);
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e25dee1..3640bb5 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -65,10 +65,9 @@
 class HWComposer
 {
 public:
-    // useVrComposer is passed to the composer HAL. When true, the composer HAL
-    // will use the vr composer service, otherwise it uses the real hardware
-    // composer.
-    HWComposer(bool useVrComposer);
+    // Uses the named composer service. Valid choices for normal use
+    // are 'default' and 'vr'.
+    HWComposer(const std::string& serviceName);
 
     ~HWComposer();
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 038ece2..e92565f 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -40,6 +40,7 @@
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 
 #include "clz.h"
@@ -2420,69 +2421,51 @@
 // debugging
 // ----------------------------------------------------------------------------
 
-void Layer::dump(String8& result, Colorizer& colorizer) const
-{
-    const Layer::State& s(getDrawingState());
-
-    colorizer.colorize(result, Colorizer::GREEN);
-    result.appendFormat(
-            "+ %s %p (%s)\n",
-            getTypeId(), this, getName().string());
-    colorizer.reset(result);
-
-    s.activeTransparentRegion.dump(result, "transparentRegion");
-    visibleRegion.dump(result, "visibleRegion");
-    surfaceDamageRegion.dump(result, "surfaceDamageRegion");
-    sp<Client> client(mClientRef.promote());
-    PixelFormat pf = PIXEL_FORMAT_UNKNOWN;
-    const sp<GraphicBuffer>& buffer(getActiveBuffer());
-    if (buffer != NULL) {
-        pf = buffer->getPixelFormat();
+LayerDebugInfo Layer::getLayerDebugInfo() const {
+    LayerDebugInfo info;
+    const Layer::State& ds = getDrawingState();
+    info.mName = getName();
+    sp<Layer> parent = getParent();
+    info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
+    info.mType = String8(getTypeId());
+    info.mTransparentRegion = ds.activeTransparentRegion;
+    info.mVisibleRegion = visibleRegion;
+    info.mSurfaceDamageRegion = surfaceDamageRegion;
+    info.mLayerStack = getLayerStack();
+    info.mX = ds.active.transform.tx();
+    info.mY = ds.active.transform.ty();
+    info.mZ = ds.z;
+    info.mWidth = ds.active.w;
+    info.mHeight = ds.active.h;
+    info.mCrop = ds.crop;
+    info.mFinalCrop = ds.finalCrop;
+    info.mAlpha = ds.alpha;
+    info.mFlags = ds.flags;
+    info.mPixelFormat = getPixelFormat();
+    info.mDataSpace = getDataSpace();
+    info.mMatrix[0][0] = ds.active.transform[0][0];
+    info.mMatrix[0][1] = ds.active.transform[0][1];
+    info.mMatrix[1][0] = ds.active.transform[1][0];
+    info.mMatrix[1][1] = ds.active.transform[1][1];
+    {
+        sp<const GraphicBuffer> activeBuffer = getActiveBuffer();
+        if (activeBuffer != 0) {
+            info.mActiveBufferWidth = activeBuffer->getWidth();
+            info.mActiveBufferHeight = activeBuffer->getHeight();
+            info.mActiveBufferStride = activeBuffer->getStride();
+            info.mActiveBufferFormat = activeBuffer->format;
+        } else {
+            info.mActiveBufferWidth = 0;
+            info.mActiveBufferHeight = 0;
+            info.mActiveBufferStride = 0;
+            info.mActiveBufferFormat = 0;
+        }
     }
-
-    result.appendFormat(            "      "
-            "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), "
-            "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), "
-            "isOpaque=%1d, invalidate=%1d, "
-            "dataspace=%s, pixelformat=%s "
-#ifdef USE_HWC2
-            "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#else
-            "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#endif
-            "      client=%p\n",
-            getLayerStack(), s.z,
-            s.active.transform.tx(), s.active.transform.ty(),
-            s.active.w, s.active.h,
-            s.crop.left, s.crop.top,
-            s.crop.right, s.crop.bottom,
-            s.finalCrop.left, s.finalCrop.top,
-            s.finalCrop.right, s.finalCrop.bottom,
-            isOpaque(s), contentDirty,
-            dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(),
-            s.alpha, s.flags,
-            s.active.transform[0][0], s.active.transform[0][1],
-            s.active.transform[1][0], s.active.transform[1][1],
-            client.get());
-
-    sp<const GraphicBuffer> buf0(mActiveBuffer);
-    uint32_t w0=0, h0=0, s0=0, f0=0;
-    if (buf0 != 0) {
-        w0 = buf0->getWidth();
-        h0 = buf0->getHeight();
-        s0 = buf0->getStride();
-        f0 = buf0->format;
-    }
-    result.appendFormat(
-            "      "
-            "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
-            " queued-frames=%d, mRefreshPending=%d\n",
-            mFormat, w0, h0, s0,f0,
-            mQueuedFrames, mRefreshPending);
-
-    if (mSurfaceFlingerConsumer != 0) {
-        mSurfaceFlingerConsumer->dumpState(result, "            ");
-    }
+    info.mNumQueuedFrames = getQueuedFrameCount();
+    info.mRefreshPending = isBufferLatched();
+    info.mIsOpaque = isOpaque(ds);
+    info.mContentDirty = contentDirty;
+    return info;
 }
 
 #ifdef USE_HWC2
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c34d8a0..f7b82e4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -60,6 +60,7 @@
 class DisplayDevice;
 class GraphicBuffer;
 class SurfaceFlinger;
+class LayerDebugInfo;
 
 // ---------------------------------------------------------------------------
 
@@ -447,6 +448,8 @@
     bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
             mSidebandStreamChanged || mAutoRefresh; }
 
+    int32_t getQueuedFrameCount() const { return mQueuedFrames; }
+
 #ifdef USE_HWC2
     // -----------------------------------------------------------------------
 
@@ -479,9 +482,9 @@
     inline  const State&    getCurrentState() const { return mCurrentState; }
     inline  State&          getCurrentState()       { return mCurrentState; }
 
+    LayerDebugInfo getLayerDebugInfo() const;
 
     /* always call base class first */
-    void dump(String8& result, Colorizer& colorizer) const;
 #ifdef USE_HWC2
     static void miniDumpHeader(String8& result);
     void miniDump(String8& result, int32_t hwcId) const;
@@ -679,6 +682,9 @@
     sp<IGraphicBufferProducer> getProducer() const;
     const String8& getName() const;
     void notifyAvailableFrames();
+
+    PixelFormat getPixelFormat() const { return mFormat; }
+
 private:
 
     // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c05ac8a..78ec4fd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -46,6 +46,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 
 #include <ui/GraphicBufferAllocator.h>
@@ -138,6 +139,21 @@
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 bool SurfaceFlinger::hasWideColorDisplay;
 
+
+std::string getHwcServiceName() {
+    char value[PROPERTY_VALUE_MAX] = {};
+    property_get("debug.sf.hwc_service_name", value, "default");
+    ALOGI("Using HWComposer service: '%s'", value);
+    return std::string(value);
+}
+
+bool useTrebleTestingOverride() {
+    char value[PROPERTY_VALUE_MAX] = {};
+    property_get("debug.sf.treble_testing_override", value, "false");
+    ALOGI("Treble testing override: '%s'", value);
+    return std::string(value) == "true";
+}
+
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
         mTransactionFlags(0),
@@ -146,6 +162,7 @@
         mLayersRemoved(false),
         mLayersAdded(false),
         mRepaintEverything(0),
+        mHwcServiceName(getHwcServiceName()),
         mRenderEngine(nullptr),
         mBootTime(systemTime()),
         mBuiltinDisplays(),
@@ -247,6 +264,15 @@
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
     // instead read after the boot animation
+
+    if (useTrebleTestingOverride()) {
+        // Without the override SurfaceFlinger cannot connect to HIDL
+        // services that are not listed in the manifests.  Considered
+        // deriving the setting from the set service name, but it
+        // would be brittle if the name that's not 'default' is used
+        // for production purposes later on.
+        setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+    }
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -599,7 +625,7 @@
 
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
             "Starting with vr flinger active is not currently supported.");
-    mHwc.reset(new HWComposer(false));
+    mHwc.reset(new HWComposer(mHwcServiceName));
     mHwc->registerCallback(this, mComposerSequenceId);
 
     if (useVrFlinger) {
@@ -1065,6 +1091,33 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if ((uid != AID_SHELL) &&
+            !PermissionCache::checkPermission(sDump, pid, uid)) {
+        ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+
+    // Try to acquire a lock for 1s, fail gracefully
+    const status_t err = mStateLock.timedLock(s2ns(1));
+    const bool locked = (err == NO_ERROR);
+    if (!locked) {
+        ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+        return TIMED_OUT;
+    }
+
+    outLayers->clear();
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+        outLayers->push_back(layer->getLayerDebugInfo());
+    });
+
+    mStateLock.unlock();
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1354,7 +1407,8 @@
 
     resetDisplayState();
     mHwc.reset();  // Delete the current instance before creating the new one
-    mHwc.reset(new HWComposer(vrFlingerRequestsDisplay));
+    mHwc.reset(new HWComposer(
+            vrFlingerRequestsDisplay ? "vr" : mHwcServiceName));
     mHwc->registerCallback(this, ++mComposerSequenceId);
 
     LOG_ALWAYS_FATAL_IF(!mHwc->getComposer()->isRemote(),
@@ -2223,7 +2277,7 @@
                     sp<const DisplayDevice> hw(mDisplays[dpy]);
                     if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) {
                         if (disp == NULL) {
-                            disp = hw;
+                            disp = std::move(hw);
                         } else {
                             disp = NULL;
                             break;
@@ -2995,7 +3049,10 @@
             }
         }
         if (what & layer_state_t::eRelativeLayerChanged) {
+            ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
             if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
+                mCurrentState.layersSortedByZ.removeAt(idx);
+                mCurrentState.layersSortedByZ.add(layer);
                 flags |= eTransactionNeeded|eTraversalNeeded;
             }
         }
@@ -3720,7 +3777,7 @@
     result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
     mCurrentState.traverseInZOrder([&](Layer* layer) {
-        layer->dump(result, colorizer);
+        result.append(to_string(layer->getLayerDebugInfo()).c_str());
     });
 
     /*
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 13a057f..1b77aaf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -313,6 +313,7 @@
             HdrCapabilities* outCapabilities) const;
     virtual status_t enableVSyncInjections(bool enable);
     virtual status_t injectVSync(nsecs_t when);
+    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
 
 
     /* ------------------------------------------------------------------------
@@ -669,6 +670,10 @@
     // acquiring mStateLock.
     std::unique_ptr<HWComposer> mHwc;
 
+#ifdef USE_HWC2
+    const std::string mHwcServiceName; // "default" for real use, something else for testing.
+#endif
+
     // constant members (no synchronization needed for access)
     RenderEngine* mRenderEngine;
     nsecs_t mBootTime;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b28fe68..a92e1f9 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -41,6 +41,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 
 #include <ui/GraphicBufferAllocator.h>
@@ -931,6 +932,34 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if ((uid != AID_SHELL) &&
+            !PermissionCache::checkPermission(sDump, pid, uid)) {
+        ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+
+    // Try to acquire a lock for 1s, fail gracefully
+    status_t err = mStateLock.timedLock(s2ns(1));
+    bool locked = (err == NO_ERROR);
+    if (!locked) {
+        ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+        return TIMED_OUT;
+    }
+
+    outLayers->clear();
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+            outLayers->push_back(layer->getLayerDebugInfo());
+        });
+
+    mStateLock.unlock();
+
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -2577,6 +2606,14 @@
                 }
             }
         }
+        if (what & layer_state_t::eRelativeLayerChanged) {
+            ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+            if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) {
+                mCurrentState.layersSortedByZ.removeAt(idx);
+                mCurrentState.layersSortedByZ.add(layer);
+                flags |= eTransactionNeeded|eTraversalNeeded;
+            }
+        }
         if (what & layer_state_t::eSizeChanged) {
             if (layer->setSize(s.w, s.h)) {
                 flags |= eTraversalNeeded;
@@ -3254,7 +3291,7 @@
     result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
     mCurrentState.traverseInZOrder([&](Layer* layer) {
-        layer->dump(result, colorizer);
+        result.append(to_string(layer->getLayerDebugInfo()).c_str());
     });
 
     /*
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
new file mode 100644
index 0000000..94f3f25
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -0,0 +1,35 @@
+cc_test {
+    name: "sffakehwc_test",
+    srcs: [
+         "FakeComposerClient.cpp",
+         "FakeComposerService.cpp",
+         "FakeComposerUtils.cpp",
+         "SFFakeHwc_test.cpp"
+    ],
+    shared_libs: [
+        "libcutils",
+        "libutils",
+        "libbinder",
+        "libui",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.mapper@2.0",
+        "libhwbinder",
+        "libhardware",
+        "libhidlbase",
+        "libsync",
+        "libfmq",
+        "libbase",
+        "libhidltransport"
+    ],
+    static_libs: [
+        "libhwcomposer-client",
+        "libsurfaceflingerincludes",
+        "libtrace_proto",
+        "libgmock"
+    ],
+    tags: ["tests"],
+    test_suites: ["device-tests"]
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
new file mode 100644
index 0000000..60916f3
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeComposer"
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+#include <inttypes.h>
+#include <time.h>
+#include <algorithm>
+#include <condition_variable>
+#include <iostream>
+#include <mutex>
+#include <set>
+#include <thread>
+
+constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
+constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1);
+
+using namespace sftest;
+
+using android::Condition;
+using android::Mutex;
+
+using Clock = std::chrono::steady_clock;
+using TimePoint = std::chrono::time_point<Clock>;
+
+namespace {
+
+// Internal state of a layer in the HWC API.
+class LayerImpl {
+public:
+    LayerImpl() = default;
+
+    bool mValid = true;
+    RenderState mRenderState;
+    uint32_t mZ = 0;
+};
+
+// Struct for storing per frame rectangle state. Contains the render
+// state shared to the test case. Basically a snapshot and a subset of
+// LayerImpl sufficient to re-create the pixels of a layer for the
+// frame.
+struct FrameRect {
+public:
+    FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
+          : layer(layer_), renderState(state), z(z_) {}
+
+    const Layer layer;
+    const RenderState renderState;
+    const uint32_t z;
+};
+
+// Collection of FrameRects forming one rendered frame. Could store
+// related fences and other data in the future.
+class Frame {
+public:
+    Frame() = default;
+    std::vector<std::unique_ptr<FrameRect>> rectangles;
+};
+
+class DelayedEventGenerator {
+public:
+    DelayedEventGenerator(std::function<void()> onTimerExpired)
+          : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {}
+
+    ~DelayedEventGenerator() {
+        ALOGI("DelayedEventGenerator exiting.");
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            mRunning = false;
+            mWakeups.clear();
+            mCondition.notify_one();
+        }
+        mThread.join();
+        ALOGI("DelayedEventGenerator exited.");
+    }
+
+    void wakeAfter(std::chrono::nanoseconds waitTime) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mWakeups.insert(Clock::now() + waitTime);
+        mCondition.notify_one();
+    }
+
+private:
+    void loop() {
+        while (true) {
+            // Lock scope
+            {
+                std::unique_lock<std::mutex> lock(mMutex);
+                mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
+                if (!mRunning && mWakeups.empty()) {
+                    // This thread should only exit once the destructor has been called and all
+                    // wakeups have been processed
+                    return;
+                }
+
+                // At this point, mWakeups will not be empty
+
+                TimePoint target = *(mWakeups.begin());
+                auto status = mCondition.wait_until(lock, target);
+                while (status == std::cv_status::no_timeout) {
+                    // This was either a spurious wakeup or another wakeup was added, so grab the
+                    // oldest point and wait again
+                    target = *(mWakeups.begin());
+                    status = mCondition.wait_until(lock, target);
+                }
+
+                // status must have been timeout, so we can finally clear this point
+                mWakeups.erase(target);
+            }
+            // Callback *without* locks!
+            mOnTimerExpired();
+        }
+    }
+
+    std::function<void()> mOnTimerExpired;
+    std::thread mThread;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mRunning = true;
+    std::set<TimePoint> mWakeups;
+};
+
+} // namespace
+
+FakeComposerClient::FakeComposerClient()
+      : mCallbacksOn(false),
+        mClient(nullptr),
+        mCurrentConfig(NULL_DISPLAY_CONFIG),
+        mVsyncEnabled(false),
+        mLayers(),
+        mDelayedEventGenerator(
+                std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
+        mSurfaceComposer(nullptr) {}
+
+FakeComposerClient::~FakeComposerClient() {}
+
+void FakeComposerClient::removeClient() {
+    ALOGV("removeClient");
+    // TODO: Ahooga! Only thing current lifetime management choices in
+    // APIs make possible. Sad.
+    delete this;
+}
+
+void FakeComposerClient::enableCallback(bool enable) {
+    ALOGV("enableCallback");
+    mCallbacksOn = enable;
+    if (mCallbacksOn) {
+        mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED);
+    }
+}
+
+void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
+    if (mCallbacksOn) {
+        mClient->onHotplug(display, state);
+    }
+}
+
+uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
+    ALOGV("getMaxVirtualDisplayCount");
+    return 1;
+}
+
+Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+                                               PixelFormat* /*format*/, Display* /*outDisplay*/) {
+    ALOGV("createVirtualDisplay");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+    ALOGV("destroyVirtualDisplay");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+    ALOGV("createLayer");
+    *outLayer = mLayers.size();
+    auto newLayer = std::make_unique<LayerImpl>();
+    mLayers.push_back(std::move(newLayer));
+    return Error::NONE;
+}
+
+Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+    ALOGV("destroyLayer");
+    mLayers[layer]->mValid = false;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+    ALOGV("getActiveConfig");
+
+    // TODO Assert outConfig != nullptr
+
+    // TODO This is my reading of the
+    // IComposerClient::getActiveConfig, but returning BAD_CONFIG
+    // seems to not fit SurfaceFlinger plans. See version 2 below.
+    // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
+    //     return Error::BAD_CONFIG;
+    // }
+    //*outConfig = mCurrentConfig;
+    *outConfig = 1; // Very special config for you my friend
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+                                                 uint32_t /*height*/, PixelFormat /*format*/,
+                                                 Dataspace /*dataspace*/) {
+    ALOGV("getClientTargetSupport");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+    ALOGV("getColorModes");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+                                              IComposerClient::Attribute attribute,
+                                              int32_t* outValue) {
+    ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
+          static_cast<int>(config), static_cast<int>(attribute), outValue);
+
+    // TODO: SOOO much fun to be had with these alone
+    switch (attribute) {
+        case IComposerClient::Attribute::WIDTH:
+            *outValue = 1920;
+            break;
+        case IComposerClient::Attribute::HEIGHT:
+            *outValue = 1080;
+            break;
+        case IComposerClient::Attribute::VSYNC_PERIOD:
+            *outValue = 1666666666;
+            break; // TOOD: Tests break down if lowered to 16ms?
+        case IComposerClient::Attribute::DPI_X:
+            *outValue = 240;
+            break;
+        case IComposerClient::Attribute::DPI_Y:
+            *outValue = 240;
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Say what!?! New attribute");
+    }
+
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
+    ALOGV("getDisplayConfigs");
+    // TODO assert display == 1, outConfigs != nullptr
+
+    outConfigs->resize(1);
+    (*outConfigs)[0] = 1;
+
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+    ALOGV("getDisplayName");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayType(Display /*display*/,
+                                         IComposerClient::DisplayType* outType) {
+    ALOGV("getDisplayType");
+    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+    // assumed to be physical?
+    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+    ALOGV("getDozeSupport");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
+                                             float* /*outMaxLuminance*/,
+                                             float* /*outMaxAverageLuminance*/,
+                                             float* /*outMinLuminance*/) {
+    ALOGV("getHdrCapabilities");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
+    ALOGV("setActiveConfig");
+    mCurrentConfig = config;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
+    ALOGV("setColorMode");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
+    ALOGV("setPowerMode");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
+    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+                                            int32_t /*hint*/) {
+    ALOGV("setColorTransform");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+                                          int32_t /*acquireFence*/, int32_t /*dataspace*/,
+                                          const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setClientTarget");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+                                          int32_t /*releaseFence*/) {
+    ALOGV("setOutputBuffer");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::validateDisplay(
+        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+        std::vector<uint32_t>* /*outRequestMasks*/) {
+    ALOGV("validateDisplay");
+    // TODO: Assume touching nothing means All Korrekt!
+    return Error::NONE;
+}
+
+Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+    ALOGV("acceptDisplayChanges");
+    // Didn't ask for changes because software is omnipotent.
+    return Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+    return a->z <= b->z;
+}
+
+Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+                                         std::vector<Layer>* /*outLayers*/,
+                                         std::vector<int32_t>* /*outReleaseFences*/) {
+    ALOGV("presentDisplay");
+    // TODO Leaving layers and their fences out for now. Doing so
+    // means that we've already processed everything. Important to
+    // test that the fences are respected, though. (How?)
+
+    std::unique_ptr<Frame> newFrame(new Frame);
+    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+        const LayerImpl& layerImpl = *mLayers[layer];
+
+        if (!layerImpl.mValid) continue;
+
+        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+        newFrame->rectangles.push_back(std::move(rect));
+    }
+    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+    {
+        Mutex::Autolock _l(mStateMutex);
+        mFrames.push_back(std::move(newFrame));
+        mFramesAvailable.broadcast();
+    }
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+                                                 int32_t /*x*/, int32_t /*y*/) {
+    ALOGV("setLayerCursorPosition");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
+                                         int32_t acquireFence) {
+    ALOGV("setLayerBuffer");
+    LayerImpl& l = getLayerImpl(layer);
+    if (buffer != l.mRenderState.mBuffer) {
+        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+    }
+    l.mRenderState.mBuffer = buffer;
+    l.mRenderState.mAcquireFence = acquireFence;
+
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+                                                const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setLayerSurfaceDamage");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+    ALOGV("setLayerBlendMode");
+    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+                                        IComposerClient::Color color) {
+    ALOGV("setLayerColor");
+    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+                                                  int32_t /*type*/) {
+    ALOGV("setLayerCompositionType");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+                                            int32_t /*dataspace*/) {
+    ALOGV("setLayerDataspace");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+                                               const hwc_rect_t& frame) {
+    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+          frame.bottom);
+    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+    ALOGV("setLayerPlaneAlpha");
+    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+                                                 buffer_handle_t /*stream*/) {
+    ALOGV("setLayerSidebandStream");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+                                             const hwc_frect_t& crop) {
+    ALOGV("setLayerSourceCrop");
+    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
+    ALOGV("setLayerTransform");
+    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+                                                const std::vector<hwc_rect_t>& visible) {
+    ALOGV("setLayerVisibleRegion");
+    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+    ALOGV("setLayerZOrder");
+    getLayerImpl(layer).mZ = z;
+    return Error::NONE;
+}
+
+//////////////////////////////////////////////////////////////////
+
+void FakeComposerClient::setClient(ComposerClient* client) {
+    mClient = client;
+}
+
+void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
+    if (mCallbacksOn) {
+        uint64_t timestamp = vsyncTime;
+        ALOGV("Vsync");
+        if (timestamp == 0) {
+            struct timespec ts;
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+            timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+        }
+        if (mSurfaceComposer != nullptr) {
+            mSurfaceComposer->injectVSync(timestamp);
+        } else {
+            mClient->onVsync(DEFAULT_DISPLAY, timestamp);
+        }
+    }
+}
+
+void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
+    mDelayedEventGenerator->wakeAfter(wait);
+}
+
+LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
+    // TODO Change these to an internal state check that can be
+    // invoked from the gtest? GTest macros do not seem all that safe
+    // when used outside the test class
+    EXPECT_GE(handle, static_cast<Layer>(0));
+    EXPECT_LT(handle, mLayers.size());
+    return *(mLayers[handle]);
+}
+
+int FakeComposerClient::getFrameCount() const {
+    return mFrames.size();
+}
+
+static std::vector<RenderState> extractRenderState(
+        const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
+    std::vector<RenderState> result;
+    result.reserve(internalRects.size());
+    for (const std::unique_ptr<FrameRect>& rect : internalRects) {
+        result.push_back(rect->renderState);
+    }
+    return result;
+}
+
+std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
+    Mutex::Autolock _l(mStateMutex);
+    return extractRenderState(mFrames[frame]->rectangles);
+}
+
+std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
+    Mutex::Autolock _l(mStateMutex);
+    return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
+}
+
+void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
+    int currentFrame = 0;
+    {
+        Mutex::Autolock _l(mStateMutex); // I hope this is ok...
+        currentFrame = static_cast<int>(mFrames.size());
+        requestVSync();
+    }
+    waitUntilFrame(currentFrame + 1, maxWait);
+}
+
+void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
+    Mutex::Autolock _l(mStateMutex);
+    while (mFrames.size() < static_cast<size_t>(targetFrame)) {
+        android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
+        if (result == android::TIMED_OUT) {
+            ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
+                  mFrames.size(), maxWait.count());
+            return;
+        }
+    }
+}
+
+void FakeComposerClient::clearFrames() {
+    Mutex::Autolock _l(mStateMutex);
+    mFrames.clear();
+    for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
+        if (layer->mValid) {
+            layer->mRenderState.mSwapCount = 0;
+        }
+    }
+}
+
+void FakeComposerClient::onSurfaceFlingerStart() {
+    mSurfaceComposer == nullptr;
+    do {
+        mSurfaceComposer = new android::SurfaceComposerClient;
+        android::status_t initResult = mSurfaceComposer->initCheck();
+        if (initResult != android::NO_ERROR) {
+            ALOGD("Init result: %d", initResult);
+            mSurfaceComposer = nullptr;
+            std::this_thread::sleep_for(10ms);
+        }
+    } while (mSurfaceComposer == nullptr);
+    ALOGD("SurfaceComposerClient created");
+    mSurfaceComposer->enableVSyncInjections(true);
+}
+
+void FakeComposerClient::onSurfaceFlingerStop() {
+    mSurfaceComposer->dispose();
+    mSurfaceComposer.clear();
+}
+
+// Includes destroyed layers, stored in order of creation.
+int FakeComposerClient::getLayerCount() const {
+    return mLayers.size();
+}
+
+Layer FakeComposerClient::getLayer(size_t index) const {
+    // NOTE: If/when passing calls through to actual implementation,
+    // this might get more involving.
+    return static_cast<Layer>(index);
+}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
new file mode 100644
index 0000000..294abb2
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ComposerClient.h"
+#include "RenderState.h"
+
+#include <utils/Condition.h>
+
+#include <chrono>
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace {
+class LayerImpl;
+class Frame;
+class DelayedEventGenerator;
+} // namespace
+
+namespace android {
+class SurfaceComposerClient;
+} // namespace android
+
+namespace sftest {
+
+class FakeComposerClient : public ComposerBase {
+public:
+    FakeComposerClient();
+    virtual ~FakeComposerClient();
+
+    void removeClient() override;
+    void enableCallback(bool enable) override;
+    uint32_t getMaxVirtualDisplayCount() override;
+    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+                               Display* outDisplay) override;
+    Error destroyVirtualDisplay(Display display) override;
+    Error createLayer(Display display, Layer* outLayer) override;
+    Error destroyLayer(Display display, Layer layer) override;
+
+    Error getActiveConfig(Display display, Config* outConfig) override;
+    Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+                                 PixelFormat format, Dataspace dataspace) override;
+    Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
+    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+                              int32_t* outValue) override;
+    Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+    Error getDisplayName(Display display, hidl_string* outName) override;
+    Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+    Error getDozeSupport(Display display, bool* outSupport) override;
+    Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
+                             float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+    Error setActiveConfig(Display display, Config config) override;
+    Error setColorMode(Display display, ColorMode mode) override;
+    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+    Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+    Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+                          int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+    Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
+    Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+                          std::vector<IComposerClient::Composition>* outCompositionTypes,
+                          uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+                          std::vector<uint32_t>* outRequestMasks) override;
+    Error acceptDisplayChanges(Display display) override;
+    Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
+                         std::vector<int32_t>* outReleaseFences) override;
+
+    Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+    Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+                         int32_t acquireFence) override;
+    Error setLayerSurfaceDamage(Display display, Layer layer,
+                                const std::vector<hwc_rect_t>& damage) override;
+    Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+    Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+    Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+    Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+    Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
+    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+    Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
+    Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+    Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+    Error setLayerVisibleRegion(Display display, Layer layer,
+                                const std::vector<hwc_rect_t>& visible) override;
+    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+    void setClient(ComposerClient* client);
+
+    void requestVSync(uint64_t vsyncTime = 0);
+    // We don't want tests hanging, so always use a timeout. Remember
+    // to always check the number of frames with test ASSERT_!
+    // Wait until next frame is rendered after requesting vsync.
+    void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
+    void runVSyncAfter(std::chrono::nanoseconds wait);
+
+    int getFrameCount() const;
+    // We don't want tests hanging, so always use a timeout. Remember
+    // to always check the number of frames with test ASSERT_!
+    void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
+    std::vector<RenderState> getFrameRects(int frame) const;
+    std::vector<RenderState> getLatestFrame() const;
+    void clearFrames();
+
+    void onSurfaceFlingerStart();
+    void onSurfaceFlingerStop();
+
+    int getLayerCount() const;
+    Layer getLayer(size_t index) const;
+
+    void hotplugDisplay(Display display, IComposerCallback::Connection state);
+
+private:
+    LayerImpl& getLayerImpl(Layer handle);
+
+    bool mCallbacksOn;
+    ComposerClient* mClient;
+    Config mCurrentConfig;
+    bool mVsyncEnabled;
+    std::vector<std::unique_ptr<LayerImpl>> mLayers;
+    std::vector<std::unique_ptr<Frame>> mFrames;
+    // Using a pointer to hide the implementation into the CPP file.
+    std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
+    android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
+    mutable android::Mutex mStateMutex;
+    mutable android::Condition mFramesAvailable;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
new file mode 100644
index 0000000..c411604
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcService"
+#include <log/log.h>
+
+#include "FakeComposerService.h"
+
+using namespace android::hardware;
+
+namespace sftest {
+
+FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+
+FakeComposerService::~FakeComposerService() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+    ALOGI("FakeComposerService::createClient %p", mClient.get());
+    mClient->initialize();
+    hidl_cb(Error::NONE, mClient);
+    return Void();
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
new file mode 100644
index 0000000..5204084
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "ComposerClient.h"
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using android::hardware::Return;
+
+namespace sftest {
+
+class FakeComposerService : public IComposer {
+public:
+    FakeComposerService(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
new file mode 100644
index 0000000..51956ec
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcUtil"
+#include <log/log.h>
+
+#include "FakeComposerUtils.h"
+#include "RenderState.h"
+
+#include "SurfaceFlinger.h" // Get the name of the service...
+
+#include <binder/IServiceManager.h>
+
+#include <cutils/properties.h>
+
+#include <iomanip>
+#include <thread>
+
+using android::String16;
+using android::sp;
+using namespace std::chrono_literals;
+using namespace sftest;
+using std::setw;
+
+namespace sftest {
+
+// clang-format off
+inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
+    os << std::fixed << std::setprecision(1) << "("
+       << setw(align) << sourceRect.left << setw(0) << ","
+       << setw(align) << sourceRect.top << setw(0) << ","
+       << setw(align) << sourceRect.right << setw(0) << ","
+       << setw(align) << sourceRect.bottom << setw(0) << ")";
+}
+
+inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
+    os << "("
+       << setw(align) << displayRect.left << setw(0) << ","
+       << setw(align) << displayRect.top << setw(0) << ","
+       << setw(align) << displayRect.right << setw(0) << ","
+       << setw(align) << displayRect.bottom << setw(0) << ")";
+}
+// clang-format on
+
+inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
+    printSourceRectAligned(os, state.mSourceCrop, 7);
+    os << "->";
+    printDisplayRectAligned(os, state.mDisplayFrame, 5);
+    return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
+              << state.mPlaneAlpha << " Xform:" << state.mTransform;
+}
+
+// Helper for verifying the parts of the RenderState
+template <typename T>
+bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
+                 const char* name) {
+    if (ref != val) {
+        message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
+        return false;
+    }
+    return true;
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
+    // TODO: Message could start as success and be assigned as failure.
+    // Only problem is that utility assumes it to be failure and just adds stuff. Would
+    // need still special case the initial failure in the utility?
+    // TODO: ... or would it be possible to break this back to gtest primitives?
+    ::testing::AssertionResult message = ::testing::AssertionFailure();
+    bool passes = true;
+
+    // The work here is mostly about providing good log strings for differences
+    passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
+    passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
+    passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
+    passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
+    // ... add more
+    if (passes) {
+        return ::testing::AssertionSuccess();
+    }
+    return message;
+}
+
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+                                         const std::vector<RenderState>& val) {
+    ::testing::AssertionResult message = ::testing::AssertionFailure();
+    bool passed = true;
+    if (ref.size() != val.size()) {
+        message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
+        passed = false;
+    }
+    for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
+        ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
+        if (rectResult == false) {
+            message << "First different rect at " << rectIndex << ": " << rectResult.message();
+            passed = false;
+            break;
+        }
+    }
+
+    if (passed) {
+        return ::testing::AssertionSuccess();
+    } else {
+        message << "\nReference:";
+        for (auto state = ref.begin(); state != ref.end(); ++state) {
+            message << "\n" << *state;
+        }
+        message << "\nActual:";
+        for (auto state = val.begin(); state != val.end(); ++state) {
+            message << "\n" << *state;
+        }
+    }
+    return message;
+}
+
+void startSurfaceFlinger() {
+    ALOGI("Start SurfaceFlinger");
+    system("start surfaceflinger");
+
+    sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<android::IBinder> sf;
+    while (sf == nullptr) {
+        std::this_thread::sleep_for(10ms);
+        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+    }
+    ALOGV("SurfaceFlinger running");
+}
+
+void stopSurfaceFlinger() {
+    ALOGI("Stop SurfaceFlinger");
+    system("stop surfaceflinger");
+    sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<android::IBinder> sf;
+    while (sf != nullptr) {
+        std::this_thread::sleep_for(10ms);
+        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+    }
+    ALOGV("SurfaceFlinger stopped");
+}
+
+////////////////////////////////////////////////
+
+void FakeHwcEnvironment::SetUp() {
+    ALOGI("Test env setup");
+    system("setenforce 0");
+    system("stop");
+    property_set("debug.sf.nobootanimation", "1");
+    {
+        char value[PROPERTY_VALUE_MAX];
+        property_get("debug.sf.nobootanimation", value, "0");
+        LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
+    }
+    // TODO: Try registering the mock as the default service instead.
+    property_set("debug.sf.hwc_service_name", "mock");
+    // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+    property_set("debug.sf.treble_testing_override", "true");
+}
+
+void FakeHwcEnvironment::TearDown() {
+    ALOGI("Test env tear down");
+    system("stop");
+    // Wait for mock call signaling teardown?
+    property_set("debug.sf.nobootanimation", "0");
+    property_set("debug.sf.hwc_service_name", "default");
+    ALOGI("Test env tear down - done");
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
new file mode 100644
index 0000000..74dc0e5
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <hardware/hwcomposer_defs.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+// clang-format off
+// Note: This needs to reside in the global namespace for the GTest to use it
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
+    return os << "(" << rect.left << ","
+              << rect.top << ","
+              << rect.right << ","
+              << rect.bottom << ")";
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
+    return os << "(" << rect.left << ","
+              << rect.top << ","
+              << rect.right << ","
+              << rect.bottom << ")";
+}
+// clang-format on
+
+namespace sftest {
+
+class RenderState;
+
+// clang-format off
+inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
+    return a.top == b.top &&
+            a.left == b.left &&
+            a.bottom == b.bottom &&
+            a.right == b.right;
+}
+
+inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
+    return a.top == b.top &&
+            a.left == b.left &&
+            a.bottom == b.bottom &&
+            a.right == b.right;
+}
+// clang-format on
+
+inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
+    return !(a == b);
+}
+
+inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
+    return !(a == b);
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+                                         const std::vector<RenderState>& val);
+
+void startSurfaceFlinger();
+void stopSurfaceFlinger();
+
+class FakeHwcEnvironment : public ::testing::Environment {
+public:
+    virtual ~FakeHwcEnvironment() {}
+    void SetUp() override;
+    void TearDown() override;
+};
+
+/*
+ * All surface state changes are supposed to happen inside a global
+ * transaction. GlobalTransactionScope object at the beginning of
+ * scope automates the process. The resulting scope gives a visual cue
+ * on the span of the transaction as well.
+ *
+ * Closing the transaction is synchronous, i.e., it waits for
+ * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
+ * is built to explicitly request vsyncs one at the time. A delayed
+ * request must be made before closing the transaction or the test
+ * thread stalls until SurfaceFlinger does an emergency vsync by
+ * itself. GlobalTransactionScope encapsulates this vsync magic.
+ */
+class GlobalTransactionScope {
+public:
+    GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) {
+        android::SurfaceComposerClient::openGlobalTransaction();
+    }
+    ~GlobalTransactionScope() {
+        int frameCount = mComposer.getFrameCount();
+        mComposer.runVSyncAfter(1ms);
+        android::SurfaceComposerClient::closeGlobalTransaction(true);
+        // Make sure that exactly one frame has been rendered.
+        mComposer.waitUntilFrame(frameCount + 1);
+        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+                            "Unexpected frame advance. Delta: %d",
+                            mComposer.getFrameCount() - frameCount);
+    }
+    FakeComposerClient& mComposer;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
new file mode 100644
index 0000000..0059289
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/RenderState.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hardware/hwcomposer2.h>
+
+#include <vector>
+
+namespace sftest {
+// Description of a rendered rectangle.  Should only contain
+// instructions necessary to rasterize the rectangle. The full scene
+// is given as a sorted list of rectangles, bottom layer at index 0.
+class RenderState {
+public:
+    RenderState() = default;
+    // Default copy-ctor
+
+    hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
+    hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
+    std::vector<hwc_rect_t> mVisibleRegion;
+    hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
+    buffer_handle_t mBuffer = 0;
+    uint32_t mSwapCount = 0;   // How many set buffer calls to the layer.
+    int32_t mAcquireFence = 0; // Probably should not be here.
+    float mPlaneAlpha = 0.f;
+    hwc_color_t mLayerColor = {0, 0, 0, 0};
+    hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
new file mode 100644
index 0000000..8902ede
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -0,0 +1,1306 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "FakeHwcTest"
+
+#include "FakeComposerClient.h"
+#include "FakeComposerService.h"
+#include "FakeComposerUtils.h"
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+
+#include <android/native_window.h>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include <hwbinder/ProcessState.h>
+
+#include <binder/ProcessState.h>
+
+#include <log/log.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <limits>
+
+using namespace std::chrono_literals;
+
+using namespace android;
+using namespace android::hardware;
+
+using namespace sftest;
+
+namespace {
+
+// Mock test helpers
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::_;
+
+///////////////////////////////////////////////
+
+struct TestColor {
+public:
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+};
+
+constexpr static TestColor RED = {195, 63, 63, 255};
+constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
+constexpr static TestColor GREEN = {63, 195, 63, 255};
+constexpr static TestColor BLUE = {63, 63, 195, 255};
+constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
+constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
+                             bool unlock = true) {
+    ANativeWindow_Buffer outBuffer;
+    sp<Surface> s = sc->getSurface();
+    ASSERT_TRUE(s != nullptr);
+    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+    for (int y = 0; y < outBuffer.height; y++) {
+        for (int x = 0; x < outBuffer.width; x++) {
+            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+            pixel[0] = color.r;
+            pixel[1] = color.g;
+            pixel[2] = color.b;
+            pixel[3] = color.a;
+        }
+    }
+    if (unlock) {
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+    }
+}
+
+inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
+    RenderState res;
+    res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
+    res.mPlaneAlpha = 1.0f;
+    res.mSwapCount = 0;
+    res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
+                                  static_cast<float>(bottom - top)};
+    return res;
+}
+
+inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
+                                  unsigned int bottom) {
+    EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
+    EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
+    EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
+    EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
+    return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
+                          static_cast<int>(bottom));
+}
+
+////////////////////////////////////////////////
+
+class DisplayTest : public ::testing::Test {
+public:
+    class MockComposerClient : public FakeComposerClient {
+    public:
+        MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
+        MOCK_METHOD4(getDisplayAttribute,
+                     Error(Display display, Config config, IComposerClient::Attribute attribute,
+                           int32_t* outValue));
+
+        // Re-routing to basic fake implementation
+        Error getDisplayAttributeFake(Display display, Config config,
+                                      IComposerClient::Attribute attribute, int32_t* outValue) {
+            return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
+        }
+    };
+
+protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    sp<IComposer> mFakeService;
+    sp<SurfaceComposerClient> mComposerClient;
+
+    MockComposerClient* mMockComposer;
+};
+
+void DisplayTest::SetUp() {
+    // TODO: The mMockComposer should be a unique_ptr, but it needs to
+    // outlive the test class.  Currently ComposerClient only dies
+    // when the service is replaced. The Mock deletes itself when
+    // removeClient is called on it, which is ugly.  This can be
+    // changed if HIDL ServiceManager allows removing services or
+    // ComposerClient starts taking the ownership of the contained
+    // implementation class. Moving the fake class to the HWC2
+    // interface instead of the current Composer interface might also
+    // change the situation.
+    mMockComposer = new MockComposerClient;
+    sp<ComposerClient> client = new ComposerClient(*mMockComposer);
+    mMockComposer->setClient(client.get());
+    mFakeService = new FakeComposerService(client);
+    mFakeService->registerAsService("mock");
+
+    android::hardware::ProcessState::self()->startThreadPool();
+    android::ProcessState::self()->startThreadPool();
+
+    EXPECT_CALL(*mMockComposer, getDisplayType(1, _))
+            .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                            Return(Error::NONE)));
+    // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy
+    // but encoding that here exactly.
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _))
+            .Times(5)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+    // TODO: Find out what code is generating the ID 0.
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _))
+            .Times(5)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+
+    startSurfaceFlinger();
+
+    // Fake composer wants to enable VSync injection
+    mMockComposer->onSurfaceFlingerStart();
+
+    mComposerClient = new SurfaceComposerClient;
+    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+}
+
+void DisplayTest::TearDown() {
+    mComposerClient->dispose();
+    mComposerClient = nullptr;
+
+    // Fake composer needs to release SurfaceComposerClient before the stop.
+    mMockComposer->onSurfaceFlingerStop();
+    stopSurfaceFlinger();
+
+    mFakeService = nullptr;
+    // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+    // management.
+    mMockComposer = nullptr;
+}
+
+TEST_F(DisplayTest, Hotplug) {
+    ALOGD("DisplayTest::Hotplug");
+
+    EXPECT_CALL(*mMockComposer, getDisplayType(2, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                                  Return(Error::NONE)));
+    // The attribute queries will get done twice. This is for defaults
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _))
+            .Times(2 * 3)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+    // ... and then special handling for dimensions. Specifying this
+    // rules later means that gmock will try them first, i.e.,
+    // ordering of width/height vs. the default implementation for
+    // other queries is significant.
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+    // TODO: Width and height queries are not actually called. Display
+    // info returns dimensions 0x0 in display info. Why?
+
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::CONNECTED);
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+        DisplayInfo info;
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+        ASSERT_EQ(400u, info.w);
+        ASSERT_EQ(200u, info.h);
+
+        auto surfaceControl =
+                mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
+                                               PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(surfaceControl != nullptr);
+        ASSERT_TRUE(surfaceControl->isValid());
+        fillSurfaceRGBA8(surfaceControl, BLUE);
+
+        {
+            GlobalTransactionScope gts(*mMockComposer);
+            mComposerClient->setDisplayLayerStack(display, 0);
+
+            ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+            ASSERT_EQ(NO_ERROR, surfaceControl->show());
+        }
+    }
+
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::DISCONNECTED);
+
+    mMockComposer->clearFrames();
+
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::CONNECTED);
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+        DisplayInfo info;
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+        ASSERT_EQ(400u, info.w);
+        ASSERT_EQ(200u, info.h);
+
+        auto surfaceControl =
+                mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
+                                               PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(surfaceControl != nullptr);
+        ASSERT_TRUE(surfaceControl->isValid());
+        fillSurfaceRGBA8(surfaceControl, BLUE);
+
+        {
+            GlobalTransactionScope gts(*mMockComposer);
+            mComposerClient->setDisplayLayerStack(display, 0);
+
+            ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+            ASSERT_EQ(NO_ERROR, surfaceControl->show());
+        }
+    }
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::DISCONNECTED);
+}
+
+////////////////////////////////////////////////
+
+class TransactionTest : public ::testing::Test {
+protected:
+    // Layer array indexing constants.
+    constexpr static int BG_LAYER = 0;
+    constexpr static int FG_LAYER = 1;
+
+    static void SetUpTestCase();
+    static void TearDownTestCase();
+
+    void SetUp() override;
+    void TearDown() override;
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
+    std::vector<RenderState> mBaseFrame;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+
+    static FakeComposerClient* sFakeComposer;
+};
+
+FakeComposerClient* TransactionTest::sFakeComposer;
+
+void TransactionTest::SetUpTestCase() {
+    // TODO: See TODO comment at DisplayTest::SetUp for background on
+    // the lifetime of the FakeComposerClient.
+    sFakeComposer = new FakeComposerClient;
+    sp<ComposerClient> client = new ComposerClient(*sFakeComposer);
+    sFakeComposer->setClient(client.get());
+    sp<IComposer> fakeService = new FakeComposerService(client);
+    fakeService->registerAsService("mock");
+
+    android::hardware::ProcessState::self()->startThreadPool();
+    android::ProcessState::self()->startThreadPool();
+
+    startSurfaceFlinger();
+
+    // Fake composer wants to enable VSync injection
+    sFakeComposer->onSurfaceFlingerStart();
+}
+
+void TransactionTest::TearDownTestCase() {
+    // Fake composer needs to release SurfaceComposerClient before the stop.
+    sFakeComposer->onSurfaceFlingerStop();
+    stopSurfaceFlinger();
+    // TODO: This is deleted when the ComposerClient calls
+    // removeClient. Devise better lifetime control.
+    sFakeComposer = nullptr;
+}
+
+void TransactionTest::SetUp() {
+    ALOGI("TransactionTest::SetUp");
+    mComposerClient = new SurfaceComposerClient;
+    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+    ALOGI("TransactionTest::SetUp - display");
+    sp<android::IBinder> display(
+            SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    DisplayInfo info;
+    SurfaceComposerClient::getDisplayInfo(display, &info);
+
+    mDisplayWidth = info.w;
+    mDisplayHeight = info.h;
+
+    // Background surface
+    mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+                                                       mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mBGSurfaceControl != nullptr);
+    ASSERT_TRUE(mBGSurfaceControl->isValid());
+    fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+    // Foreground surface
+    mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+                                                       PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mFGSurfaceControl != nullptr);
+    ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+    fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+    SurfaceComposerClient::openGlobalTransaction();
+
+    mComposerClient->setDisplayLayerStack(display, 0);
+
+    ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2));
+    ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1));
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+
+    // Synchronous transaction will stop this thread, so we set up a
+    // delayed, off-thread vsync request before closing the
+    // transaction. In the test code this is usually done with
+    // GlobalTransactionScope. Leaving here in the 'vanilla' form for
+    // reference.
+    ASSERT_EQ(0, sFakeComposer->getFrameCount());
+    sFakeComposer->runVSyncAfter(1ms);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+    sFakeComposer->waitUntilFrame(1);
+
+    // Reference data. This is what the HWC should see.
+    static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+    mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+    mBaseFrame[BG_LAYER].mSwapCount = 1;
+    mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+    mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+    auto frame = sFakeComposer->getFrameRects(0);
+    ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+}
+
+void TransactionTest::TearDown() {
+    ALOGD("TransactionTest::TearDown");
+
+    mComposerClient->dispose();
+    mBGSurfaceControl = 0;
+    mFGSurfaceControl = 0;
+    mComposerClient = 0;
+
+    sFakeComposer->runVSyncAndWait();
+    mBaseFrame.clear();
+    sFakeComposer->clearFrames();
+    ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    std::vector<LayerDebugInfo> layers;
+    status_t result = sf->getLayerDebugInfo(&layers);
+    if (result != NO_ERROR) {
+        ALOGE("Failed to get layers %s %d", strerror(-result), result);
+    } else {
+        // If this fails, the test being torn down leaked layers.
+        EXPECT_EQ(0u, layers.size());
+        if (layers.size() > 0) {
+            for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+                std::cout << to_string(*layer).c_str();
+            }
+            // To ensure the next test has clean slate, will run the class
+            // tear down and setup here.
+            TearDownTestCase();
+            SetUpTestCase();
+        }
+    }
+    ALOGD("TransactionTest::TearDown - complete");
+}
+
+TEST_F(TransactionTest, LayerMove) {
+    ALOGD("TransactionTest::LayerMove");
+
+    // The scope opens and closes a global transaction and, at the
+    // same time, makes sure the SurfaceFlinger progresses one frame
+    // after the transaction closes. The results of the transaction
+    // should be available in the latest frame stored by the fake
+    // composer.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+        // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+        // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+        //
+        // sFakeComposer->runVSyncAndWait();
+    }
+
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+
+    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+                                                  // no extra frames.
+
+    // NOTE: Frame 0 is produced in the SetUp.
+    auto frame1Ref = mBaseFrame;
+    frame1Ref[FG_LAYER].mDisplayFrame =
+            hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+    auto frame2Ref = frame1Ref;
+    frame2Ref[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerResize) {
+    ALOGD("TransactionTest::LayerResize");
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128));
+    }
+
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+
+    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+                                                  // no extra frames.
+
+    auto frame1Ref = mBaseFrame;
+    // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
+    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+    auto frame2Ref = frame1Ref;
+    frame2Ref[FG_LAYER].mSwapCount++;
+    frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+    frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerCrop) {
+    // TODO: Add scaling to confirm that crop happens in buffer space?
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        Rect cropRect(16, 16, 32, 32);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCrop) {
+    // TODO: Add scaling to confirm that crop happens in display space?
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        Rect cropRect(32, 32, 32 + 64, 32 + 64);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // In display space we are cropping with [32, 32, 96, 96] against display rect
+    // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
+
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCropEmpty) {
+    // TODO: Add scaling to confirm that crop happens in display space?
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        Rect cropRect(16, 16, 32, 32);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // In display space we are cropping with [16, 16, 32, 32] against display rect
+    // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
+    std::vector<RenderState> referenceFrame(1);
+    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayer) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // The layers will switch order, but both are rendered because the background layer is
+    // transparent (RGBA8888).
+    std::vector<RenderState> referenceFrame(2);
+    referenceFrame[0] = mBaseFrame[FG_LAYER];
+    referenceFrame[1] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayerOpaque) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+        ASSERT_EQ(NO_ERROR,
+                  mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque,
+                                              layer_state_t::eLayerOpaque));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // The former foreground layer is now covered with opaque layer - it should have disappeared
+    std::vector<RenderState> referenceFrame(1);
+    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetLayerStack) {
+    ALOGD("TransactionTest::SetLayerStack");
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1));
+    }
+
+    // Foreground layer should have disappeared.
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerShowHide) {
+    ALOGD("TransactionTest::LayerShowHide");
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide());
+    }
+
+    // Foreground layer should have disappeared.
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+    }
+
+    // Foreground layer should be back
+    ASSERT_EQ(3, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetAlpha) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f));
+    }
+
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetFlags) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR,
+                  mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden,
+                                              layer_state_t::eLayerHidden));
+    }
+
+    // Foreground layer should have disappeared.
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetMatrix) {
+    struct matrixTestData {
+        float matrix[4];
+        hwc_transform_t expectedTransform;
+        hwc_rect_t expectedDisplayFrame;
+    };
+
+    // The matrix operates on the display frame and is applied before
+    // the position is added. So, the foreground layer rect is (0, 0,
+    // 64, 64) is first transformed, potentially yielding negative
+    // coordinates and then the position (64, 64) is added yielding
+    // the final on-screen rectangles given.
+
+    const matrixTestData MATRIX_TESTS[7] = // clang-format off
+            {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
+             {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
+             {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
+             {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
+             {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
+             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
+             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
+    // clang-format on
+    constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData);
+
+    for (int i = 0; i < TEST_COUNT; i++) {
+        // TODO: How to leverage the HWC2 stringifiers?
+        const matrixTestData& xform = MATRIX_TESTS[i];
+        SCOPED_TRACE(i);
+        {
+            GlobalTransactionScope gts(*sFakeComposer);
+            ASSERT_EQ(NO_ERROR,
+                      mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1],
+                                                   xform.matrix[2], xform.matrix[3]));
+        }
+
+        auto referenceFrame = mBaseFrame;
+        referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+        referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+}
+
+#if 0
+TEST_F(TransactionTest, LayerSetMatrix2) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        // TODO: PLEASE SPEC THE FUNCTION!
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f,
+                                                         -2.33f, 0.22f));
+    }
+    auto referenceFrame = mBaseFrame;
+    // TODO: Is this correct for sure?
+    //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
+
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+#endif
+
+TEST_F(TransactionTest, DeferredTransaction) {
+    // Synchronization surface
+    constexpr static int SYNC_LAYER = 2;
+    auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+                                                             PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(syncSurfaceControl != nullptr);
+    ASSERT_TRUE(syncSurfaceControl->isValid());
+
+    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1));
+        ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2));
+        ASSERT_EQ(NO_ERROR, syncSurfaceControl->show());
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+                                            mDisplayWidth - 1, mDisplayHeight - 1));
+    referenceFrame[SYNC_LAYER].mSwapCount = 1;
+    EXPECT_EQ(2, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // set up two deferred transactions on different frames - these should not yield composited
+    // frames
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75));
+        mFGSurfaceControl
+                ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+                                        syncSurfaceControl->getSurface()->getNextFrameNumber());
+    }
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+        mFGSurfaceControl
+                ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+                                        syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+    }
+    EXPECT_EQ(4, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // should trigger the first deferred transaction, but not the second one
+    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+    sFakeComposer->runVSyncAndWait();
+    EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+    referenceFrame[SYNC_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // should show up immediately since it's not deferred
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0));
+    }
+    referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+    EXPECT_EQ(6, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // trigger the second deferred transaction
+    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+    sFakeComposer->runVSyncAndWait();
+    // TODO: Compute from layer size?
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+    referenceFrame[SYNC_LAYER].mSwapCount++;
+    EXPECT_EQ(7, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetRelativeLayer) {
+    constexpr int RELATIVE_LAYER = 2;
+    auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
+                                                                 PIXEL_FORMAT_RGBA_8888, 0);
+    fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+    // Now we stack the surface above the foreground surface and make sure it is visible.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        relativeSurfaceControl->setPosition(64, 64);
+        relativeSurfaceControl->show();
+        relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1);
+    }
+    auto referenceFrame = mBaseFrame;
+    // NOTE: All three layers will be visible as the surfaces are
+    // transparent because of the RGBA format.
+    referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+    referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // A call to setLayer will override a call to setRelativeLayer
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        relativeSurfaceControl->setLayer(0);
+    }
+
+    // Previous top layer will now appear at the bottom.
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+    EXPECT_EQ(3, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+class ChildLayerTest : public TransactionTest {
+protected:
+    constexpr static int CHILD_LAYER = 2;
+
+    void SetUp() override {
+        TransactionTest::SetUp();
+        mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+        fillSurfaceRGBA8(mChild, LIGHT_GRAY);
+
+        sFakeComposer->runVSyncAndWait();
+        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+    }
+    void TearDown() override {
+        mChild = 0;
+        TransactionTest::TearDown();
+    }
+
+    sp<SurfaceControl> mChild;
+};
+
+TEST_F(ChildLayerTest, Positioning) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(10, 10);
+        // Move to the same position as in the original setup.
+        mFGSurfaceControl->setPosition(64, 64);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0));
+    }
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+    referenceFrame2[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Cropping) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5));
+    }
+    // NOTE: The foreground surface would be occluded by the child
+    // now, but is included in the stack because the child is
+    // transparent.
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, FinalCropping) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5));
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Constraints) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mFGSurfaceControl->setPosition(0, 0);
+        mChild->setPosition(63, 63);
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Scaling) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setPosition(0, 0);
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0);
+    }
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, LayerAlpha) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+        ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5));
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5));
+    }
+
+    auto referenceFrame2 = referenceFrame;
+    referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
+    referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(10, 10);
+        mFGSurfaceControl->setPosition(64, 64);
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle());
+    }
+
+    auto referenceFrame2 = referenceFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, DetachChildren) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(10, 10);
+        mFGSurfaceControl->setPosition(64, 64);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->detachChildren();
+    }
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->hide();
+    }
+
+    // Nothing should have changed. The child control becomes a no-op
+    // zombie on detach. See comments for detachChildren in the
+    // SurfaceControl.h file.
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+    }
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // We cause scaling by 2.
+        mFGSurfaceControl->setSize(128, 128);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+    }
+
+    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+    // the WM specified state size.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 64);
+    }
+
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    fillSurfaceRGBA8(mFGSurfaceControl, RED);
+    sFakeComposer->runVSyncAndWait();
+
+    // The child should still be in the same place and not have any strange scaling as in
+    // b/37673612.
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+    referenceFrame[FG_LAYER].mSwapCount++;
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+    // Destroy the child layer
+    mChild.clear();
+
+    // Now recreate it as hidden
+    mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
+                                            mFGSurfaceControl.get());
+
+    // Show the child layer in a deferred transaction
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(),
+                                      mFGSurfaceControl->getSurface()->getNextFrameNumber());
+        mChild->show();
+    }
+
+    // Render the foreground surface a few times
+    //
+    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+    // never acquire/release the first buffer
+    ALOGI("Filling 1");
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+    ALOGI("Filling 2");
+    fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
+    sFakeComposer->runVSyncAndWait();
+    ALOGI("Filling 3");
+    fillSurfaceRGBA8(mFGSurfaceControl, RED);
+    sFakeComposer->runVSyncAndWait();
+    ALOGI("Filling 4");
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+}
+
+class LatchingTest : public TransactionTest {
+protected:
+    void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+
+    void unlockFGBuffer() {
+        sp<Surface> s = mFGSurfaceControl->getSurface();
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        sFakeComposer->runVSyncAndWait();
+    }
+
+    void completeFGResize() {
+        fillSurfaceRGBA8(mFGSurfaceControl, RED);
+        sFakeComposer->runVSyncAndWait();
+    }
+    void restoreInitialState() {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(64, 64);
+        mFGSurfaceControl->setPosition(64, 64);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64));
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+    }
+};
+
+TEST_F(LatchingTest, SurfacePositionLatching) {
+    // By default position can be updated even while
+    // a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(32, 32);
+        mFGSurfaceControl->setPosition(100, 100);
+    }
+
+    // The size should not have updated as we have not provided a new buffer.
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    // Now we repeat with setGeometryAppliesWithResize
+    // and verify the position DOESN'T latch.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setSize(32, 32);
+        mFGSurfaceControl->setPosition(100, 100);
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, CropLatching) {
+    // Normally the crop applies immediately even while a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+    }
+
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+    referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatching) {
+    // Normally the crop applies immediately even while a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame1[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame2[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+// In this test we ensure that setGeometryAppliesWithResize actually demands
+// a buffer of the new size, and not just any size.
+TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
+    // Normally the crop applies immediately even while a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame1[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
+    // initiating the resize.
+    lockAndFillFGBuffer();
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    // We now submit our old buffer, at the old size, and ensure it doesn't
+    // trigger geometry latching.
+    unlockFGBuffer();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+    auto referenceFrame3 = referenceFrame2;
+    referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame3[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    referenceFrame3[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
+    // In this scenario, we attempt to set the final crop a second time while the resize
+    // is still pending, and ensure we are successful. Success meaning the second crop
+    // is the one which eventually latches and not the first.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+    ::testing::AddGlobalTestEnvironment(fakeEnvironment);
+    ::testing::InitGoogleMock(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
index 062485e..4055527 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -382,7 +382,9 @@
         if (outErr) {
             *outErr = err;
         } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position";
+            ASSERT_TRUE((err == HWC2_ERROR_NONE) ||
+                (err == HWC2_ERROR_BAD_LAYER)) <<
+                "failed to set cursor position";
         }
     }
 
@@ -652,7 +654,7 @@
             hwc2_layer_request_t request = requests.at(i);
 
             EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer),
-                    0) << "get display requests returned an unknown layer";
+                    1) << "get display requests returned an unknown layer";
             EXPECT_NE(request, 0) << "returned empty request for layer "
                     << requestedLayer;
 
@@ -1603,9 +1605,10 @@
         EXPECT_EQ(layers.size(), fences.size());
 
         for (int32_t fence : fences) {
-            EXPECT_GE(sync_wait(fence, msWait), 0);
-            if (fence >= 0)
+            if (fence >= 0) {
+                EXPECT_GE(sync_wait(fence, msWait), 0);
                 close(fence);
+            }
         }
     }
 
@@ -1643,8 +1646,9 @@
                 testLayers->getBlendMode(layer)));
         EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer,
                 testLayers->getColor(layer)));
-        EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left,
-                cursor.top));
+        if (composition == HWC2_COMPOSITION_CURSOR)
+            EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer,
+            cursor.left, cursor.top));
         EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer,
                 testLayers->getDataspace(layer)));
         EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer,
@@ -2895,7 +2899,6 @@
     ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
             [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
                     Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
                 const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
                 EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
                         cursorPosition.left, cursorPosition.top, outErr));
@@ -4406,11 +4409,11 @@
 /* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */
 TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter)
 {
-    hwc2_display_t display = HWC_DISPLAY_PRIMARY;
     hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+    for (auto display : mDisplays) {
+        ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
+        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
+    }
 }
 
 /* TESTCASE: Tests that the HWC2 can get the max virtual display count. */
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 5e32af0..fd271d0 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -232,7 +232,7 @@
 
 VrHwc::~VrHwc() {}
 
-bool VrHwc::hasCapability(Capability capability) const { return false; }
+bool VrHwc::hasCapability(Capability /* capability */) const { return false; }
 
 void VrHwc::removeClient() {
   std::lock_guard<std::mutex> guard(mutex_);
@@ -306,13 +306,15 @@
   return Error::NONE;
 }
 
-Error VrHwc::getClientTargetSupport(Display display, uint32_t width,
-                                    uint32_t height, PixelFormat format,
-                                    Dataspace dataspace) {
+Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */,
+                                    uint32_t /* height */,
+                                    PixelFormat /* format */,
+                                    Dataspace /* dataspace */) {
   return Error::NONE;
 }
 
-Error VrHwc::getColorModes(Display display, hidl_vec<ColorMode>* outModes) {
+Error VrHwc::getColorModes(Display /* display */,
+                           hidl_vec<ColorMode>* outModes) {
   std::vector<ColorMode> color_modes(1, ColorMode::NATIVE);
   *outModes = hidl_vec<ColorMode>(color_modes);
   return Error::NONE;
@@ -379,7 +381,7 @@
   return Error::NONE;
 }
 
-Error VrHwc::getDisplayName(Display display, hidl_string* outName) {
+Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) {
   *outName = hidl_string();
   return Error::NONE;
 }
@@ -409,7 +411,8 @@
   return Error::NONE;
 }
 
-Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
+Error VrHwc::getHdrCapabilities(Display /* display */,
+                                hidl_vec<Hdr>* /* outTypes */,
                                 float* outMaxLuminance,
                                 float* outMaxAverageLuminance,
                                 float* outMinLuminance) {
@@ -473,8 +476,8 @@
 }
 
 Error VrHwc::setClientTarget(Display display, buffer_handle_t target,
-                             int32_t acquireFence, int32_t dataspace,
-                             const std::vector<hwc_rect_t>& damage) {
+                             int32_t acquireFence, int32_t /* dataspace */,
+                             const std::vector<hwc_rect_t>& /* damage */) {
   base::unique_fd fence(acquireFence);
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
@@ -490,7 +493,7 @@
   return Error::NONE;
 }
 
-Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer,
+Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */,
                              int32_t releaseFence) {
   base::unique_fd fence(releaseFence);
   std::lock_guard<std::mutex> guard(mutex_);
@@ -505,8 +508,9 @@
 Error VrHwc::validateDisplay(
     Display display, std::vector<Layer>* outChangedLayers,
     std::vector<IComposerClient::Composition>* outCompositionTypes,
-    uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-    std::vector<uint32_t>* outRequestMasks) {
+    uint32_t* /* outDisplayRequestMask */,
+    std::vector<Layer>* /* outRequestedLayers */,
+    std::vector<uint32_t>* /* outRequestMasks */) {
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
   if (!display_ptr)
@@ -517,7 +521,7 @@
   return Error::NONE;
 }
 
-Error VrHwc::acceptDisplayChanges(Display display) { return Error::NONE; }
+Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; }
 
 Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence,
                             std::vector<Layer>* outLayers,
@@ -709,8 +713,8 @@
   return Error::NONE;
 }
 
-Error VrHwc::setLayerSidebandStream(Display display, Layer layer,
-                                    buffer_handle_t stream) {
+Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */,
+                                    buffer_handle_t /* stream */) {
   std::lock_guard<std::mutex> guard(mutex_);
   if (!FindDisplay(display))
     return Error::BAD_DISPLAY;
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 3a2b1ef..2981a95 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
 // API version (major.minor.patch)
 define VERSION_MAJOR 1
 define VERSION_MINOR 0
-define VERSION_PATCH 54
+define VERSION_PATCH 61
 
 // API limits
 define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -102,6 +102,10 @@
 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION           1
 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_NAME                   "VK_NV_glsl_shader"
 
+// 14
+@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION   1
+@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_NAME           "VK_EXT_depth_range_unrestricted"
+
 // 15
 @extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION   1
 @extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_NAME           "VK_KHR_sampler_mirror_clamp_to_edge"
@@ -183,7 +187,7 @@
 @extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
 
 // 61
-@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1
+@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 2
 @extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
 
 // 62
@@ -267,7 +271,7 @@
 @extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
 
 // 87
-@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3
 @extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
 
 // 88
@@ -319,7 +323,7 @@
 @extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
 
 // 105
-@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 2
+@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 3
 @extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
 
 // 106
@@ -346,6 +350,10 @@
 @extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1
 @extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd"
 
+// 118
+@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_SPEC_VERSION 1
+@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2"
+
 // 120
 @extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
 @extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
@@ -363,7 +371,7 @@
 @extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
 
 // 128
-@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 1
+@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3
 @extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation"
 
 // 131
@@ -378,10 +386,34 @@
 @extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 1
 @extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16"
 
+// 137
+@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1
+@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples"
+
+// 138
+@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1
+@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask"
+
+// 141
+@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1
+@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export"
+
+// 144
+@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1
+@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations"
+
+// 145
+@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1
+@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout"
+
 // 147
 @extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_SPEC_VERSION 1
 @extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_EXTENSION_NAME "VK_KHR_get_memory_requirements2"
 
+// 148
+@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1
+@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list"
+
 // 149
 @extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2
 @extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced"
@@ -398,6 +430,26 @@
 @extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_SPEC_VERSION 1
 @extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle"
 
+// 156
+@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1
+@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage"
+
+// 157
+@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1
+@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion"
+
+// 158
+@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_SPEC_VERSION 1
+@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_EXTENSION_NAME "VK_KHR_bind_memory2"
+
+// 161
+@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1
+@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache"
+
+// 165
+@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1
+@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer"
+
 /////////////
 //  Types  //
 /////////////
@@ -456,6 +508,11 @@
 @extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
 @extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
 
+// 157
+@extension("VK_KHR_sampler_ycbcr_conversion") @nonDispatchHandle type u64 VkSamplerYcbcrConversionKHR
+
+// 161
+@extension("VK_EXT_validation_cache") @nonDispatchHandle type u64 VkValidationCacheEXT
 
 /////////////
 //  Enums  //
@@ -477,6 +534,10 @@
 
     //@extension("VK_KHR_shared_presentable_image") // 112
     VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR                      = 1000111000,
+
+    //@extension("VK_KHR_maintenance2") // 118
+    VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR  = 1000117000,
+    VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR  = 1000117001,
 }
 
 enum VkAttachmentLoadOp {
@@ -960,6 +1021,42 @@
     VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG                    = 1000054005,
     VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG                    = 1000054006,
     VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG                    = 1000054007,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_FORMAT_G8B8G8R8_422_UNORM_KHR                            = 1000156000,
+    VK_FORMAT_B8G8R8G8_422_UNORM_KHR                            = 1000156001,
+    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR                     = 1000156002,
+    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR                      = 1000156003,
+    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR                     = 1000156004,
+    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR                      = 1000156005,
+    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR                     = 1000156006,
+    VK_FORMAT_R10X6_UNORM_PACK16_KHR                            = 1000156007,
+    VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR                      = 1000156008,
+    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR            = 1000156009,
+    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR        = 1000156010,
+    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR        = 1000156011,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR    = 1000156012,
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR     = 1000156013,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR    = 1000156014,
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR     = 1000156015,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR    = 1000156016,
+    VK_FORMAT_R12X4_UNORM_PACK16_KHR                            = 1000156017,
+    VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR                      = 1000156018,
+    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR            = 1000156019,
+    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR        = 1000156020,
+    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR        = 1000156021,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR    = 1000156022,
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR     = 1000156023,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR    = 1000156024,
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR     = 1000156025,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR    = 1000156026,
+    VK_FORMAT_G16B16G16R16_422_UNORM_KHR                        = 1000156027,
+    VK_FORMAT_B16G16R16G16_422_UNORM_KHR                        = 1000156028,
+    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR                  = 1000156029,
+    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR                   = 1000156030,
+    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR                  = 1000156031,
+    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR                   = 1000156032,
+    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR                  = 1000156033,
 }
 
 /// Structure type enumerant
@@ -1096,8 +1193,6 @@
 
     //@extension("VK_KHX_device_group") // 61
     VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX            = 1000060000,
-    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX               = 1000060001,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX                = 1000060002,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX   = 1000060003,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX              = 1000060005,
@@ -1108,6 +1203,8 @@
     VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX               = 1000060010,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX             = 1000060011,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX    = 1000060012,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHX  = 1000060013,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHX   = 1000060014,
 
     //@extension("VK_EXT_validation_flags") // 62
     VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT                      = 1000061000,
@@ -1183,7 +1280,7 @@
     VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV  = 1000087000,
 
     //@extension("VK_EXT_display_surface_counter") // 91
-    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT                 = 1000090000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT                = 1000090000,
 
     //@extension("VK_EXT_display_control") // 92
     VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT                    = 1000091000,
@@ -1226,6 +1323,12 @@
     VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR                  = 1000115000,
     VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR                     = 1000115001,
 
+    //@extension("VK_KHR_maintenance2") // 118
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR             = 1000117000,
+    VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR       = 1000117001,
+    VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR                          = 1000117002,
+    VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003,
+
     //@extension("VK_KHR_get_surface_capabilities2") // 120
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR        = 1000119000,
     VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR                = 1000119001,
@@ -1248,6 +1351,13 @@
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000,
     VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001,
 
+    //@extension("VK_EXT_sample_locations") // 144
+    VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT                         = 1000143000,
+    VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT       = 1000143001,
+    VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT   = 1000143002,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT   = 1000143003,
+    VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT                        = 1000143004,
+
     //@extension("VK_KHR_get_memory_requirements2") // 147
     VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR         = 1000146000,
     VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR          = 1000146001,
@@ -1255,6 +1365,9 @@
     VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR                     = 1000146003,
     VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR        = 1000146004,
 
+    //@extension("VK_KHR_image_format_list") // 148
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR             = 1000147000,
+
     //@extension("VK_EXT_blend_operation_advanced") // 149
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001,
@@ -1263,8 +1376,24 @@
     //@extension("VK_NV_fragment_coverage_to_color") // 150
     VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000,
 
-    //@structure("VK_NV_framebuffer_mixed_samples") // 153
+    //@extension("VK_NV_framebuffer_mixed_samples") // 153
     VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR              = 1000156000,
+    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR                     = 1000156001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR                      = 1000156002,
+    VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR              = 1000156003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004,
+    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR  = 1000156005,
+
+    //@extension("VK_KHR_bind_memory2") // 158
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR                       = 1000157000,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR                        = 1000157001,
+
+    //@extension("VK_EXT_validation_cache") // 161
+    VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT                  = 1000160000,
+    VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT    = 1000160001,
 }
 
 enum VkSubpassContents {
@@ -1343,6 +1472,9 @@
 
     //@extension("VK_EXT_discard_rectangles") // 100
     VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT                  = 1000099000,
+
+    //@extension("VK_EXT_sample_locations") // 144
+    VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT                   = 1000143000,
 }
 
 enum VkObjectType {
@@ -1392,6 +1524,12 @@
     //@extension("VK_NVX_device_generated_commands") // 87
     VK_OBJECT_TYPE_OBJECT_TABLE_NVX                         = 1000086000,
     VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX             = 1000086001,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR             = 1000156000,
+
+    //@extension("VK_EXT_validation_cache") // 161
+    VK_OBJECT_TYPE_VALIDATION_CACHE_EXT                     = 1000160000,
 }
 
 @extension("VK_KHR_surface") // 1
@@ -1463,8 +1601,14 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT        = 31,
     VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
 
+    //extension("VK_EXT_validation_cache") // 161
+    VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT        = 33,
+
     //extension("VK_KHR_descriptor_update_template") // 86
     VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000,
 }
 
 @extension("VK_AMD_rasterization_order") // 19
@@ -1541,6 +1685,18 @@
     VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
 }
 
+@extension("VK_KHR_maintenance2") // 118
+enum VkPointClippingBehaviorKHR {
+    VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR          = 0,
+    VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR    = 1,
+}
+
+@extension("VK_KHR_maintenance2") // 118
+enum VkTessellationDomainOriginKHR {
+    VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR            = 0,
+    VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR            = 1,
+}
+
 @extension("VK_EXT_sampler_filter_minmax") // 131
 enum VkSamplerReductionModeEXT {
     VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT          = 0,
@@ -1563,6 +1719,32 @@
     VK_COVERAGE_MODULATION_MODE_RGBA_NV                     = 3,
 }
 
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+enum VkSamplerYcbcrModelConversionKHR {
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR      = 0,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR    = 1,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR         = 2,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR         = 3,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR        = 4,
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+enum VkSamplerYcbcrRangeKHR {
+    VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR                     = 0,
+    VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR                   = 1,
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+enum VkChromaLocationKHR {
+    VK_CHROMA_LOCATION_COSITED_EVEN_KHR                     = 0,
+    VK_CHROMA_LOCATION_MIDPOINT_KHR                         = 1,
+}
+
+@extension("VK_EXT_validation_cache") // 161
+enum VkValidationCacheHeaderVersionEXT {
+    VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT              = 1,
+}
+
 /////////////////
 //  Bitfields  //
 /////////////////
@@ -1698,6 +1880,19 @@
 
     //@extension("VK_KHX_device_group") // 61
     VK_IMAGE_CREATE_BIND_SFR_BIT_KHX                        = 0x00000040,
+
+    //@extension("VK_EXT_sample_locations") // 144
+    VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000,
+
+    //@extension("VK_KHR_maintenance2") // 118
+    VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR     = 0x00000080,
+    VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR                  = 0x00000100,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_IMAGE_CREATE_DISJOINT_BIT_KHR                        = 0x00000200,
+
+    //@extension("VK_KHR_bind_memory2") // 158
+    VK_IMAGE_CREATE_ALIAS_BIT_KHR                           = 0x00000400,
 }
 
 /// Image view creation flags
@@ -1763,6 +1958,15 @@
 
     //@extension("VK_EXT_sampler_filter_minmax") // 131
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT   = 0x00010000,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR                                                   = 0x00020000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR                              = 0x00040000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR             = 0x00080000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR             = 0x00100000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR   = 0x00200000,
+    VK_FORMAT_FEATURE_DISJOINT_BIT_KHR                                                                  = 0x00400000,
+    VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR                                                    = 0x00800000,
 }
 
 /// Query control flags
@@ -1826,6 +2030,11 @@
     VK_IMAGE_ASPECT_DEPTH_BIT                               = 0x00000002,
     VK_IMAGE_ASPECT_STENCIL_BIT                             = 0x00000004,
     VK_IMAGE_ASPECT_METADATA_BIT                            = 0x00000008,
+
+    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+    VK_IMAGE_ASPECT_PLANE_0_BIT_KHR                         = 0x00000010,
+    VK_IMAGE_ASPECT_PLANE_1_BIT_KHR                         = 0x00000020,
+    VK_IMAGE_ASPECT_PLANE_2_BIT_KHR                         = 0x00000040,
 }
 
 /// Sparse memory bind flags
@@ -2353,6 +2562,12 @@
 //bitfield VkPipelineCoverageModulationStateCreateFlagBitsNV {
 //}
 
+@extension("VK_EXT_validation_cache") // 161
+type VkFlags VkValidationCacheCreateFlagsEXT
+@extension("VK_EXT_validation_cache") // 161
+//bitfield VkValidationCacheCreateFlagBitsEXT {
+//}
+
 //////////////////
 //  Structures  //
 //////////////////
@@ -3901,23 +4116,17 @@
 }
 
 @extension("VK_KHX_device_group") // 61
-class VkBindBufferMemoryInfoKHX {
+class VkBindBufferMemoryDeviceGroupInfoKHX {
     VkStructureType                             sType
     const void*                                 pNext
-    VkBuffer                                    buffer
-    VkDeviceMemory                              memory
-    VkDeviceSize                                memoryOffset
     u32                                         deviceIndexCount
     const u32*                                  pDeviceIndices
 }
 
 @extension("VK_KHX_device_group") // 61
-class VkBindImageMemoryInfoKHX {
+class VkBindImageMemoryDeviceGroupInfoKHX {
     VkStructureType                             sType
     const void*                                 pNext
-    VkImage                                     image
-    VkDeviceMemory                              memory
-    VkDeviceSize                                memoryOffset
     u32                                         deviceIndexCount
     const u32*                                  pDeviceIndices
     u32                                         SFRRectCount
@@ -4674,6 +4883,42 @@
     VkExternalFenceHandleTypeFlagBitsKHR            handleType
 }
 
+@extension("VK_KHR_maintenance2") // 118
+class VkPhysicalDevicePointClippingPropertiesKHR {
+    VkStructureType                                 sType
+    void*                                           pNext
+    VkPointClippingBehaviorKHR                      pointClippingBehavior
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkInputAttachmentAspectReferenceKHR {
+    u32                                             subpass
+    u32                                             inputAttachmentIndex
+    VkImageAspectFlags                              aspectMask
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkRenderPassInputAttachmentAspectCreateInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    u32                                             aspectReferenceCount
+    const VkInputAttachmentAspectReferenceKHR*      pAspectReferences
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkImageViewUsageCreateInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkImageUsageFlags                               usage
+}
+
+@extension("VK_KHR_maintenance2") // 118
+class VkPipelineTessellationDomainOriginStateCreateInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkTessellationDomainOriginKHR                   domainOrigin
+}
+
 @extension("VK_KHR_get_surface_capabilities2") // 120
 class VkPhysicalDeviceSurfaceInfo2KHR {
     VkStructureType                                 sType
@@ -4750,6 +4995,70 @@
     VkBool32                                        filterMinmaxImageComponentMapping
 }
 
+@extension("VK_EXT_sample_locations") // 144
+class VkSampleLocationEXT {
+    f32                                             x
+    f32                                             y
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkSampleLocationsInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkSampleCountFlagBits                           sampleLocationsPerPixel
+    VkExtent2D                                      sampleLocationGridSize
+    u32                                             sampleLocationsCount
+    const VkSampleLocationEXT*                      pSampleLocations
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkAttachmentSampleLocationsEXT {
+    u32                                             attachmentIndex
+    VkSampleLocationsInfoEXT                        sampleLocationsInfo
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkSubpassSampleLocationsEXT {
+    u32                                             subpassIndex
+    VkSampleLocationsInfoEXT                        sampleLocationsInfo
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkRenderPassSampleLocationsBeginInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    u32                                             attachmentInitialSampleLocationsCount
+    const VkAttachmentSampleLocationsEXT*           pAttachmentInitialSampleLocations
+    u32                                             postSubpassSampleLocationsCount
+    const VkSubpassSampleLocationsEXT*              pSubpassSampleLocations
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkPipelineSampleLocationsStateCreateInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkBool32                                        sampleLocationsEnable
+    VkSampleLocationsInfoEXT                        sampleLocationsInfo
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkPhysicalDeviceSampleLocationsPropertiesEXT {
+    VkStructureType                                 sType
+    void*                                           pNext
+    VkSampleCountFlags                              sampleLocationSampleCounts
+    VkExtent2D                                      maxSampleLocationGridSize
+    f32[2]                                          sampleLocationCoordinateRange
+    u32                                             sampleLocationSubPixelBits
+    VkBool32                                        variableSampleLocations
+}
+
+@extension("VK_EXT_sample_locations") // 144
+class VkMultisamplePropertiesEXT {
+    VkStructureType                                 sType
+    void*                                           pNext
+    VkExtent2D                                      maxSampleLocationGridSize
+}
+
 @extension("VK_KHR_get_memory_requirements2") // 147
 class VkBufferMemoryRequirementsInfo2KHR {
     VkStructureType                                 sType
@@ -4785,6 +5094,14 @@
     VkSparseImageMemoryRequirements                 memoryRequirements
 }
 
+@extension("VK_KHR_image_format_list") // 148
+class VkImageFormatListCreateInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    u32                                             viewFormatCount
+    const VkFormat*                                 pViewFormats
+}
+
 @extension("VK_EXT_blend_operation_advanced") // 149
 class VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT {
     VkStructureType                                 sType
@@ -4833,6 +5150,89 @@
     const f32*                                      pCoverageModulationTable
 }
 
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkSamplerYcbcrConversionCreateInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkFormat                                        format
+    VkSamplerYcbcrModelConversionKHR                ycbcrModel
+    VkSamplerYcbcrRangeKHR                          ycbcrRange
+    VkComponentMapping                              components
+    VkChromaLocationKHR                             xChromaOffset
+    VkChromaLocationKHR                             yChromaOffset
+    VkFilter                                        chromaFilter
+    VkBool32                                        forceExplicitReconstruction
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkSamplerYcbcrConversionInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkSamplerYcbcrConversionKHR                     conversion
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkBindImagePlaneMemoryInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkImageAspectFlagBits                           planeAspect
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkImagePlaneMemoryRequirementsInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkImageAspectFlagBits                           planeAspect
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR {
+    VkStructureType                                 sType
+    void*                                           pNext
+    VkBool32                                        samplerYcbcrConversion
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+class VkSamplerYcbcrConversionImageFormatPropertiesKHR {
+    VkStructureType                                 sType
+    void*                                           pNext
+    u32                                             combinedImageSamplerDescriptorCount
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+class VkBindBufferMemoryInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkBuffer                                        buffer
+    VkDeviceMemory                                  memory
+    VkDeviceSize                                    memoryOffset
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+class VkBindImageMemoryInfoKHR {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkImage                                         image
+    VkDeviceMemory                                  memory
+    VkDeviceSize                                    memoryOffset
+}
+
+@extension("VK_EXT_validation_cache") // 161
+class VkValidationCacheCreateInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkValidationCacheCreateFlagsEXT                 flags
+    platform.size_t                                 initialDataSize
+    const void*                                     pInitialData
+}
+
+@extension("VK_EXT_validation_cache") // 161
+class VkShaderModuleValidationCacheCreateInfoEXT {
+    VkStructureType                                 sType
+    const void*                                     pNext
+    VkValidationCacheEXT                            validationCache
+}
+
 ////////////////
 //  Commands  //
 ////////////////
@@ -7454,21 +7854,21 @@
 @extension("VK_EXT_debug_marker") // 23
 cmd VkResult vkDebugMarkerSetObjectTagEXT(
         VkDevice                                    device,
-        VkDebugMarkerObjectTagInfoEXT*              pTagInfo) {
+        const VkDebugMarkerObjectTagInfoEXT*        pTagInfo) {
     return ?
 }
 
 @extension("VK_EXT_debug_marker") // 23
 cmd VkResult vkDebugMarkerSetObjectNameEXT(
         VkDevice                                    device,
-        VkDebugMarkerObjectNameInfoEXT*             pNameInfo) {
+        const VkDebugMarkerObjectNameInfoEXT*       pNameInfo) {
     return ?
 }
 
 @extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerBeginEXT(
         VkCommandBuffer                             commandBuffer,
-        VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
+        const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo) {
 }
 
 @extension("VK_EXT_debug_marker") // 23
@@ -7479,7 +7879,7 @@
 @extension("VK_EXT_debug_marker") // 23
 cmd void vkCmdDebugMarkerInsertEXT(
         VkCommandBuffer                             commandBuffer,
-        VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo) {
+        const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo) {
 }
 
 @extension("VK_AMD_draw_indirect_count") // 34
@@ -7584,22 +7984,6 @@
 }
 
 @extension("VK_KHX_device_group") // 61
-cmd VkResult vkBindBufferMemory2KHX(
-        VkDevice                                    device,
-        u32                                         bindInfoCount,
-        const VkBindBufferMemoryInfoKHX*            pBindInfos) {
-    return ?
-}
-
-@extension("VK_KHX_device_group") // 61
-cmd VkResult vkBindImageMemory2KHX(
-        VkDevice                                    device,
-        u32                                         bindInfoCount,
-        const VkBindImageMemoryInfoKHX*             pBindInfos) {
-    return ?
-}
-
-@extension("VK_KHX_device_group") // 61
 cmd void vkCmdSetDeviceMaskKHX(
         VkCommandBuffer                             commandBuffer,
         u32                                         deviceMask) {
@@ -8058,6 +8442,19 @@
     return ?
 }
 
+@extension("VK_EXT_sample_locations") // 144
+cmd void vkCmdSetSampleLocationsEXT(
+        VkCommandBuffer                             commandBuffer,
+        const VkSampleLocationsInfoEXT*             pSampleLocationsInfo) {
+}
+
+@extension("VK_EXT_sample_locations") // 144
+cmd void vkGetPhysicalDeviceMultisamplePropertiesEXT(
+        VkPhysicalDevice                            physicalDevice,
+        VkSampleCountFlagBits                       samples,
+        VkMultisamplePropertiesEXT*                 pMultisampleProperties) {
+}
+
 @extension("VK_KHR_get_memory_requirements2") // 147
 cmd void vkGetImageMemoryRequirements2KHR(
         VkDevice                                    device,
@@ -8080,6 +8477,72 @@
         VkSparseImageMemoryRequirements2KHR*            pSparseMemoryRequirements) {
 }
 
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+cmd VkResult vkCreateSamplerYcbcrConversionKHR(
+        VkDevice                                        device,
+        const VkSamplerYcbcrConversionCreateInfoKHR*    pCreateInfo,
+        const VkAllocationCallbacks*                    pAllocator,
+        VkSamplerYcbcrConversionKHR*                    pYcbcrConversion) {
+    return ?
+}
+
+@extension("VK_KHR_sampler_ycbcr_conversion") // 157
+cmd void vkDestroySamplerYcbcrConversionKHR(
+        VkDevice                                        device,
+        VkSamplerYcbcrConversionKHR                     ycbcrConversion,
+        const VkAllocationCallbacks*                    pAllocator) {
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+cmd VkResult vkBindBufferMemory2KHR(
+        VkDevice                                        device,
+        u32                                             bindInfoCount,
+        const VkBindBufferMemoryInfoKHR*                pBindInfos) {
+    return ?
+}
+
+@extension("VK_KHR_bind_memory2") // 158
+cmd VkResult vkBindImageMemory2KHR(
+        VkDevice                                        device,
+        u32                                             bindInfoCount,
+        const VkBindImageMemoryInfoKHR*                 pBindInfos) {
+    return ?
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd VkResult vkCreateValidationCacheEXT(
+        VkDevice                                    device,
+        const VkValidationCacheCreateInfoEXT*       pCreateInfo,
+        const VkAllocationCallbacks*                pAllocator,
+        VkValidationCacheEXT*                       pValidationCache) {
+    return ?
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd void vkDestroyValidationCacheEXT(
+        VkDevice                                    device,
+        VkValidationCacheEXT                        validationCache,
+        const VkAllocationCallbacks*                pAllocator) {
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd VkResult vkMergeValidationCachesEXT(
+        VkDevice                                    device,
+        VkValidationCacheEXT                        dstCache,
+        u32                                         srcCacheCount,
+        const VkValidationCacheEXT*                 pSrcCaches) {
+    return ?
+}
+
+@extension("VK_EXT_validation_cache") // 161
+cmd VkResult vkGetValidationCacheDataEXT(
+        VkDevice                                    device,
+        VkValidationCacheEXT                        validationCache,
+        platform.size_t*                            pDataSize,
+        void*                                       pData) {
+    return ?
+}
+
 ////////////////
 // Validation //
 ////////////////
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 0271d38..7813e4b 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -34,16 +34,16 @@
     (((major) << 22) | ((minor) << 12) | (patch))
 
 // DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead.
-//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0)
+//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0
 
 // Vulkan 1.0 version number
-#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)
+#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)// Patch version should always be set to 0
 
 #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
 #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
 #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
 // Version of this file
-#define VK_HEADER_VERSION 54
+#define VK_HEADER_VERSION 61
 
 
 #define VK_NULL_HANDLE 0
@@ -241,16 +241,16 @@
     VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
     VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000,
-    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006,
+    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060013,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060014,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007,
     VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008,
     VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009,
-    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011,
     VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012,
     VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000,
@@ -293,7 +293,7 @@
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004,
     VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005,
     VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000,
-    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000,
+    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000,
     VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000,
     VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001,
     VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002,
@@ -313,6 +313,10 @@
     VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002,
     VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000,
     VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = 1000117000,
+    VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = 1000117001,
+    VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = 1000117002,
+    VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000,
     VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001,
     VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002,
@@ -323,16 +327,32 @@
     VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = 1000127001,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000,
     VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001,
+    VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000,
+    VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001,
+    VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003,
+    VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004,
     VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146000,
     VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146001,
     VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146002,
     VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = 1000146003,
     VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = 1000146004,
+    VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001,
     VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002,
     VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000,
     VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000,
+    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = 1000156000,
+    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = 1000156001,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = 1000156002,
+    VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = 1000156003,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004,
+    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = 1000156005,
+    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = 1000157000,
+    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = 1000157001,
+    VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000,
+    VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001,
     VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO,
     VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO,
     VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1),
@@ -553,6 +573,40 @@
     VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
     VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
     VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
+    VK_FORMAT_G8B8G8R8_422_UNORM_KHR = 1000156000,
+    VK_FORMAT_B8G8R8G8_422_UNORM_KHR = 1000156001,
+    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = 1000156002,
+    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = 1000156003,
+    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = 1000156004,
+    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = 1000156005,
+    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = 1000156006,
+    VK_FORMAT_R10X6_UNORM_PACK16_KHR = 1000156007,
+    VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = 1000156008,
+    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = 1000156009,
+    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = 1000156010,
+    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = 1000156011,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = 1000156012,
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = 1000156013,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = 1000156014,
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = 1000156015,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = 1000156016,
+    VK_FORMAT_R12X4_UNORM_PACK16_KHR = 1000156017,
+    VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = 1000156018,
+    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = 1000156019,
+    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = 1000156020,
+    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = 1000156021,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = 1000156022,
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = 1000156023,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = 1000156024,
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = 1000156025,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = 1000156026,
+    VK_FORMAT_G16B16G16R16_422_UNORM_KHR = 1000156027,
+    VK_FORMAT_B16G16R16G16_422_UNORM_KHR = 1000156028,
+    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = 1000156029,
+    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = 1000156030,
+    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = 1000156031,
+    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = 1000156032,
+    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = 1000156033,
     VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED,
     VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
     VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1),
@@ -621,6 +675,8 @@
     VK_IMAGE_LAYOUT_PREINITIALIZED = 8,
     VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002,
     VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000,
+    VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = 1000117000,
+    VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = 1000117001,
     VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED,
     VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED,
     VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1),
@@ -851,6 +907,7 @@
     VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8,
     VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000,
     VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000,
+    VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000,
     VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT,
     VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE,
     VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1),
@@ -1009,6 +1066,8 @@
     VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = 1000085000,
     VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000,
     VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001,
+    VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = 1000156000,
+    VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
     VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN,
     VK_OBJECT_TYPE_END_RANGE = VK_OBJECT_TYPE_COMMAND_POOL,
     VK_OBJECT_TYPE_RANGE_SIZE = (VK_OBJECT_TYPE_COMMAND_POOL - VK_OBJECT_TYPE_UNKNOWN + 1),
@@ -1035,6 +1094,13 @@
     VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000,
     VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000,
     VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000,
+    VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000,
+    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000,
+    VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = 0x00400000,
+    VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000,
     VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkFormatFeatureFlagBits;
 typedef VkFlags VkFormatFeatureFlags;
@@ -1060,6 +1126,11 @@
     VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010,
     VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040,
     VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020,
+    VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080,
+    VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = 0x00000100,
+    VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000,
+    VK_IMAGE_CREATE_DISJOINT_BIT_KHR = 0x00000200,
+    VK_IMAGE_CREATE_ALIAS_BIT_KHR = 0x00000400,
     VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkImageCreateFlagBits;
 typedef VkFlags VkImageCreateFlags;
@@ -1133,6 +1204,9 @@
     VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002,
     VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004,
     VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008,
+    VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = 0x00000010,
+    VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = 0x00000020,
+    VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = 0x00000040,
     VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
 } VkImageAspectFlagBits;
 typedef VkFlags VkImageAspectFlags;
@@ -1366,6 +1440,27 @@
 } VkStencilFaceFlagBits;
 typedef VkFlags VkStencilFaceFlags;
 
+typedef struct VkApplicationInfo {
+    VkStructureType    sType;
+    const void*        pNext;
+    const char*        pApplicationName;
+    uint32_t           applicationVersion;
+    const char*        pEngineName;
+    uint32_t           engineVersion;
+    uint32_t           apiVersion;
+} VkApplicationInfo;
+
+typedef struct VkInstanceCreateInfo {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkInstanceCreateFlags       flags;
+    const VkApplicationInfo*    pApplicationInfo;
+    uint32_t                    enabledLayerCount;
+    const char* const*          ppEnabledLayerNames;
+    uint32_t                    enabledExtensionCount;
+    const char* const*          ppEnabledExtensionNames;
+} VkInstanceCreateInfo;
+
 typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)(
     void*                                       pUserData,
     size_t                                      size,
@@ -1395,29 +1490,6 @@
     VkInternalAllocationType                    allocationType,
     VkSystemAllocationScope                     allocationScope);
 
-typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void);
-
-typedef struct VkApplicationInfo {
-    VkStructureType    sType;
-    const void*        pNext;
-    const char*        pApplicationName;
-    uint32_t           applicationVersion;
-    const char*        pEngineName;
-    uint32_t           engineVersion;
-    uint32_t           apiVersion;
-} VkApplicationInfo;
-
-typedef struct VkInstanceCreateInfo {
-    VkStructureType             sType;
-    const void*                 pNext;
-    VkInstanceCreateFlags       flags;
-    const VkApplicationInfo*    pApplicationInfo;
-    uint32_t                    enabledLayerCount;
-    const char* const*          ppEnabledLayerNames;
-    uint32_t                    enabledExtensionCount;
-    const char* const*          ppEnabledExtensionNames;
-} VkInstanceCreateInfo;
-
 typedef struct VkAllocationCallbacks {
     void*                                   pUserData;
     PFN_vkAllocationFunction                pfnAllocation;
@@ -1658,6 +1730,7 @@
     VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
 } VkPhysicalDeviceMemoryProperties;
 
+typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void);
 typedef struct VkDeviceQueueCreateInfo {
     VkStructureType             sType;
     const void*                 pNext;
@@ -4774,6 +4847,62 @@
     int*                                        pFd);
 #endif
 
+#define VK_KHR_maintenance2 1
+#define VK_KHR_MAINTENANCE2_SPEC_VERSION  1
+#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2"
+
+
+typedef enum VkPointClippingBehaviorKHR {
+    VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = 0,
+    VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = 1,
+    VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE_KHR = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR,
+    VK_POINT_CLIPPING_BEHAVIOR_END_RANGE_KHR = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR,
+    VK_POINT_CLIPPING_BEHAVIOR_RANGE_SIZE_KHR = (VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR + 1),
+    VK_POINT_CLIPPING_BEHAVIOR_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkPointClippingBehaviorKHR;
+
+typedef enum VkTessellationDomainOriginKHR {
+    VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = 0,
+    VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = 1,
+    VK_TESSELLATION_DOMAIN_ORIGIN_BEGIN_RANGE_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR,
+    VK_TESSELLATION_DOMAIN_ORIGIN_END_RANGE_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR,
+    VK_TESSELLATION_DOMAIN_ORIGIN_RANGE_SIZE_KHR = (VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR + 1),
+    VK_TESSELLATION_DOMAIN_ORIGIN_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkTessellationDomainOriginKHR;
+
+typedef struct VkPhysicalDevicePointClippingPropertiesKHR {
+    VkStructureType               sType;
+    void*                         pNext;
+    VkPointClippingBehaviorKHR    pointClippingBehavior;
+} VkPhysicalDevicePointClippingPropertiesKHR;
+
+typedef struct VkInputAttachmentAspectReferenceKHR {
+    uint32_t              subpass;
+    uint32_t              inputAttachmentIndex;
+    VkImageAspectFlags    aspectMask;
+} VkInputAttachmentAspectReferenceKHR;
+
+typedef struct VkRenderPassInputAttachmentAspectCreateInfoKHR {
+    VkStructureType                               sType;
+    const void*                                   pNext;
+    uint32_t                                      aspectReferenceCount;
+    const VkInputAttachmentAspectReferenceKHR*    pAspectReferences;
+} VkRenderPassInputAttachmentAspectCreateInfoKHR;
+
+typedef struct VkImageViewUsageCreateInfoKHR {
+    VkStructureType      sType;
+    const void*          pNext;
+    VkImageUsageFlags    usage;
+} VkImageViewUsageCreateInfoKHR;
+
+typedef struct VkPipelineTessellationDomainOriginStateCreateInfoKHR {
+    VkStructureType                  sType;
+    const void*                      pNext;
+    VkTessellationDomainOriginKHR    domainOrigin;
+} VkPipelineTessellationDomainOriginStateCreateInfoKHR;
+
+
+
 #define VK_KHR_get_surface_capabilities2 1
 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
@@ -4827,7 +4956,7 @@
 
 
 #define VK_KHR_dedicated_allocation 1
-#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 1
+#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3
 #define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation"
 
 typedef struct VkMemoryDedicatedRequirementsKHR {
@@ -4851,6 +4980,11 @@
 #define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class"
 
 
+#define VK_KHR_relaxed_block_layout 1
+#define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1
+#define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout"
+
+
 #define VK_KHR_get_memory_requirements2 1
 #define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1
 #define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2"
@@ -4908,6 +5042,152 @@
     VkSparseImageMemoryRequirements2KHR*        pSparseMemoryRequirements);
 #endif
 
+#define VK_KHR_image_format_list 1
+#define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1
+#define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list"
+
+typedef struct VkImageFormatListCreateInfoKHR {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           viewFormatCount;
+    const VkFormat*    pViewFormats;
+} VkImageFormatListCreateInfoKHR;
+
+
+
+#define VK_KHR_sampler_ycbcr_conversion 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversionKHR)
+
+#define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1
+#define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion"
+
+
+typedef enum VkSamplerYcbcrModelConversionKHR {
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = 0,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = 1,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = 2,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = 3,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = 4,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_BEGIN_RANGE_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_END_RANGE_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR,
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_RANGE_SIZE_KHR = (VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR + 1),
+    VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSamplerYcbcrModelConversionKHR;
+
+typedef enum VkSamplerYcbcrRangeKHR {
+    VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = 0,
+    VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = 1,
+    VK_SAMPLER_YCBCR_RANGE_BEGIN_RANGE_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR,
+    VK_SAMPLER_YCBCR_RANGE_END_RANGE_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR,
+    VK_SAMPLER_YCBCR_RANGE_RANGE_SIZE_KHR = (VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR - VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR + 1),
+    VK_SAMPLER_YCBCR_RANGE_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkSamplerYcbcrRangeKHR;
+
+typedef enum VkChromaLocationKHR {
+    VK_CHROMA_LOCATION_COSITED_EVEN_KHR = 0,
+    VK_CHROMA_LOCATION_MIDPOINT_KHR = 1,
+    VK_CHROMA_LOCATION_BEGIN_RANGE_KHR = VK_CHROMA_LOCATION_COSITED_EVEN_KHR,
+    VK_CHROMA_LOCATION_END_RANGE_KHR = VK_CHROMA_LOCATION_MIDPOINT_KHR,
+    VK_CHROMA_LOCATION_RANGE_SIZE_KHR = (VK_CHROMA_LOCATION_MIDPOINT_KHR - VK_CHROMA_LOCATION_COSITED_EVEN_KHR + 1),
+    VK_CHROMA_LOCATION_MAX_ENUM_KHR = 0x7FFFFFFF
+} VkChromaLocationKHR;
+
+typedef struct VkSamplerYcbcrConversionCreateInfoKHR {
+    VkStructureType                     sType;
+    const void*                         pNext;
+    VkFormat                            format;
+    VkSamplerYcbcrModelConversionKHR    ycbcrModel;
+    VkSamplerYcbcrRangeKHR              ycbcrRange;
+    VkComponentMapping                  components;
+    VkChromaLocationKHR                 xChromaOffset;
+    VkChromaLocationKHR                 yChromaOffset;
+    VkFilter                            chromaFilter;
+    VkBool32                            forceExplicitReconstruction;
+} VkSamplerYcbcrConversionCreateInfoKHR;
+
+typedef struct VkSamplerYcbcrConversionInfoKHR {
+    VkStructureType                sType;
+    const void*                    pNext;
+    VkSamplerYcbcrConversionKHR    conversion;
+} VkSamplerYcbcrConversionInfoKHR;
+
+typedef struct VkBindImagePlaneMemoryInfoKHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkImageAspectFlagBits    planeAspect;
+} VkBindImagePlaneMemoryInfoKHR;
+
+typedef struct VkImagePlaneMemoryRequirementsInfoKHR {
+    VkStructureType          sType;
+    const void*              pNext;
+    VkImageAspectFlagBits    planeAspect;
+} VkImagePlaneMemoryRequirementsInfoKHR;
+
+typedef struct VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR {
+    VkStructureType    sType;
+    void*              pNext;
+    VkBool32           samplerYcbcrConversion;
+} VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR;
+
+typedef struct VkSamplerYcbcrConversionImageFormatPropertiesKHR {
+    VkStructureType    sType;
+    void*              pNext;
+    uint32_t           combinedImageSamplerDescriptorCount;
+} VkSamplerYcbcrConversionImageFormatPropertiesKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateSamplerYcbcrConversionKHR)(VkDevice device, const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversionKHR* pYcbcrConversion);
+typedef void (VKAPI_PTR *PFN_vkDestroySamplerYcbcrConversionKHR)(VkDevice device, VkSamplerYcbcrConversionKHR ycbcrConversion, const VkAllocationCallbacks* pAllocator);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateSamplerYcbcrConversionKHR(
+    VkDevice                                    device,
+    const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkSamplerYcbcrConversionKHR*                pYcbcrConversion);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversionKHR(
+    VkDevice                                    device,
+    VkSamplerYcbcrConversionKHR                 ycbcrConversion,
+    const VkAllocationCallbacks*                pAllocator);
+#endif
+
+#define VK_KHR_bind_memory2 1
+#define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1
+#define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2"
+
+typedef struct VkBindBufferMemoryInfoKHR {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBuffer           buffer;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+} VkBindBufferMemoryInfoKHR;
+
+typedef struct VkBindImageMemoryInfoKHR {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkImage            image;
+    VkDeviceMemory     memory;
+    VkDeviceSize       memoryOffset;
+} VkBindImageMemoryInfoKHR;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHR* pBindInfos);
+typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHR(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindBufferMemoryInfoKHR*            pBindInfos);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHR(
+    VkDevice                                    device,
+    uint32_t                                    bindInfoCount,
+    const VkBindImageMemoryInfoKHR*             pBindInfos);
+#endif
+
 #define VK_EXT_debug_report 1
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
 
@@ -4951,10 +5231,12 @@
     VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30,
     VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31,
     VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
+    VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = 33,
     VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000,
     VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT,
-    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT,
-    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
+    VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT,
+    VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1),
     VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF
 } VkDebugReportObjectTypeEXT;
 
@@ -5021,6 +5303,11 @@
 #define VK_NV_GLSL_SHADER_EXTENSION_NAME  "VK_NV_glsl_shader"
 
 
+#define VK_EXT_depth_range_unrestricted 1
+#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1
+#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME "VK_EXT_depth_range_unrestricted"
+
+
 #define VK_IMG_filter_cubic 1
 #define VK_IMG_FILTER_CUBIC_SPEC_VERSION  1
 #define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic"
@@ -5088,31 +5375,31 @@
 } VkDebugMarkerMarkerInfoEXT;
 
 
-typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo);
-typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo);
-typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo);
+typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
 typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer);
-typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
 
 #ifndef VK_NO_PROTOTYPES
 VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(
     VkDevice                                    device,
-    VkDebugMarkerObjectTagInfoEXT*              pTagInfo);
+    const VkDebugMarkerObjectTagInfoEXT*        pTagInfo);
 
 VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(
     VkDevice                                    device,
-    VkDebugMarkerObjectNameInfoEXT*             pNameInfo);
+    const VkDebugMarkerObjectNameInfoEXT*       pNameInfo);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(
     VkCommandBuffer                             commandBuffer,
-    VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo);
+    const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(
     VkCommandBuffer                             commandBuffer);
 
 VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(
     VkCommandBuffer                             commandBuffer,
-    VkDebugMarkerMarkerInfoEXT*                 pMarkerInfo);
+    const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo);
 #endif
 
 #define VK_AMD_gcn_shader 1
@@ -5350,9 +5637,9 @@
 #endif /* VK_USE_PLATFORM_WIN32_KHR */
 
 #define VK_KHX_device_group 1
-#define VK_MAX_DEVICE_GROUP_SIZE_KHX      32
-#define VK_KHX_DEVICE_GROUP_SPEC_VERSION  1
+#define VK_KHX_DEVICE_GROUP_SPEC_VERSION  2
 #define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group"
+#define VK_MAX_DEVICE_GROUP_SIZE_KHX      32
 
 
 typedef enum VkPeerMemoryFeatureFlagBitsKHX {
@@ -5386,28 +5673,6 @@
     uint32_t                    deviceMask;
 } VkMemoryAllocateFlagsInfoKHX;
 
-typedef struct VkBindBufferMemoryInfoKHX {
-    VkStructureType    sType;
-    const void*        pNext;
-    VkBuffer           buffer;
-    VkDeviceMemory     memory;
-    VkDeviceSize       memoryOffset;
-    uint32_t           deviceIndexCount;
-    const uint32_t*    pDeviceIndices;
-} VkBindBufferMemoryInfoKHX;
-
-typedef struct VkBindImageMemoryInfoKHX {
-    VkStructureType    sType;
-    const void*        pNext;
-    VkImage            image;
-    VkDeviceMemory     memory;
-    VkDeviceSize       memoryOffset;
-    uint32_t           deviceIndexCount;
-    const uint32_t*    pDeviceIndices;
-    uint32_t           SFRRectCount;
-    const VkRect2D*    pSFRRects;
-} VkBindImageMemoryInfoKHX;
-
 typedef struct VkDeviceGroupRenderPassBeginInfoKHX {
     VkStructureType    sType;
     const void*        pNext;
@@ -5440,6 +5705,22 @@
     uint32_t           memoryDeviceIndex;
 } VkDeviceGroupBindSparseInfoKHX;
 
+typedef struct VkBindBufferMemoryDeviceGroupInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+} VkBindBufferMemoryDeviceGroupInfoKHX;
+
+typedef struct VkBindImageMemoryDeviceGroupInfoKHX {
+    VkStructureType    sType;
+    const void*        pNext;
+    uint32_t           deviceIndexCount;
+    const uint32_t*    pDeviceIndices;
+    uint32_t           SFRRectCount;
+    const VkRect2D*    pSFRRects;
+} VkBindImageMemoryDeviceGroupInfoKHX;
+
 typedef struct VkDeviceGroupPresentCapabilitiesKHX {
     VkStructureType                     sType;
     const void*                         pNext;
@@ -5486,14 +5767,12 @@
 
 
 typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures);
-typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos);
-typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos);
 typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask);
+typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities);
 typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes);
-typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
-typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
 typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
+typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex);
 
 #ifndef VK_NO_PROTOTYPES
 VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX(
@@ -5503,34 +5782,10 @@
     uint32_t                                    remoteDeviceIndex,
     VkPeerMemoryFeatureFlagsKHX*                pPeerMemoryFeatures);
 
-VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX(
-    VkDevice                                    device,
-    uint32_t                                    bindInfoCount,
-    const VkBindBufferMemoryInfoKHX*            pBindInfos);
-
-VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX(
-    VkDevice                                    device,
-    uint32_t                                    bindInfoCount,
-    const VkBindImageMemoryInfoKHX*             pBindInfos);
-
 VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX(
     VkCommandBuffer                             commandBuffer,
     uint32_t                                    deviceMask);
 
-VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
-    VkDevice                                    device,
-    VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities);
-
-VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
-    VkDevice                                    device,
-    VkSurfaceKHR                                surface,
-    VkDeviceGroupPresentModeFlagsKHX*           pModes);
-
-VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
-    VkDevice                                    device,
-    const VkAcquireNextImageInfoKHX*            pAcquireInfo,
-    uint32_t*                                   pImageIndex);
-
 VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX(
     VkCommandBuffer                             commandBuffer,
     uint32_t                                    baseGroupX,
@@ -5540,11 +5795,25 @@
     uint32_t                                    groupCountY,
     uint32_t                                    groupCountZ);
 
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX(
+    VkDevice                                    device,
+    VkDeviceGroupPresentCapabilitiesKHX*        pDeviceGroupPresentCapabilities);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX(
+    VkDevice                                    device,
+    VkSurfaceKHR                                surface,
+    VkDeviceGroupPresentModeFlagsKHX*           pModes);
+
 VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX(
     VkPhysicalDevice                            physicalDevice,
     VkSurfaceKHR                                surface,
     uint32_t*                                   pRectCount,
     VkRect2D*                                   pRects);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX(
+    VkDevice                                    device,
+    const VkAcquireNextImageInfoKHX*            pAcquireInfo,
+    uint32_t*                                   pImageIndex);
 #endif
 
 #define VK_EXT_validation_flags 1
@@ -5639,7 +5908,7 @@
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX)
 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX)
 
-#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1
+#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3
 #define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
 
 
@@ -5929,6 +6198,7 @@
 #define VK_EXT_display_surface_counter 1
 #define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
 #define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
+#define VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT
 
 
 typedef enum VkSurfaceCounterFlagBitsEXT {
@@ -6328,6 +6598,96 @@
 #define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16"
 
 
+#define VK_AMD_mixed_attachment_samples 1
+#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1
+#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples"
+
+
+#define VK_AMD_shader_fragment_mask 1
+#define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1
+#define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask"
+
+
+#define VK_EXT_shader_stencil_export 1
+#define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1
+#define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export"
+
+
+#define VK_EXT_sample_locations 1
+#define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1
+#define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations"
+
+typedef struct VkSampleLocationEXT {
+    float    x;
+    float    y;
+} VkSampleLocationEXT;
+
+typedef struct VkSampleLocationsInfoEXT {
+    VkStructureType               sType;
+    const void*                   pNext;
+    VkSampleCountFlagBits         sampleLocationsPerPixel;
+    VkExtent2D                    sampleLocationGridSize;
+    uint32_t                      sampleLocationsCount;
+    const VkSampleLocationEXT*    pSampleLocations;
+} VkSampleLocationsInfoEXT;
+
+typedef struct VkAttachmentSampleLocationsEXT {
+    uint32_t                    attachmentIndex;
+    VkSampleLocationsInfoEXT    sampleLocationsInfo;
+} VkAttachmentSampleLocationsEXT;
+
+typedef struct VkSubpassSampleLocationsEXT {
+    uint32_t                    subpassIndex;
+    VkSampleLocationsInfoEXT    sampleLocationsInfo;
+} VkSubpassSampleLocationsEXT;
+
+typedef struct VkRenderPassSampleLocationsBeginInfoEXT {
+    VkStructureType                          sType;
+    const void*                              pNext;
+    uint32_t                                 attachmentInitialSampleLocationsCount;
+    const VkAttachmentSampleLocationsEXT*    pAttachmentInitialSampleLocations;
+    uint32_t                                 postSubpassSampleLocationsCount;
+    const VkSubpassSampleLocationsEXT*       pSubpassSampleLocations;
+} VkRenderPassSampleLocationsBeginInfoEXT;
+
+typedef struct VkPipelineSampleLocationsStateCreateInfoEXT {
+    VkStructureType             sType;
+    const void*                 pNext;
+    VkBool32                    sampleLocationsEnable;
+    VkSampleLocationsInfoEXT    sampleLocationsInfo;
+} VkPipelineSampleLocationsStateCreateInfoEXT;
+
+typedef struct VkPhysicalDeviceSampleLocationsPropertiesEXT {
+    VkStructureType       sType;
+    void*                 pNext;
+    VkSampleCountFlags    sampleLocationSampleCounts;
+    VkExtent2D            maxSampleLocationGridSize;
+    float                 sampleLocationCoordinateRange[2];
+    uint32_t              sampleLocationSubPixelBits;
+    VkBool32              variableSampleLocations;
+} VkPhysicalDeviceSampleLocationsPropertiesEXT;
+
+typedef struct VkMultisamplePropertiesEXT {
+    VkStructureType    sType;
+    void*              pNext;
+    VkExtent2D         maxSampleLocationGridSize;
+} VkMultisamplePropertiesEXT;
+
+
+typedef void (VKAPI_PTR *PFN_vkCmdSetSampleLocationsEXT)(VkCommandBuffer commandBuffer, const VkSampleLocationsInfoEXT* pSampleLocationsInfo);
+typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT)(VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples, VkMultisamplePropertiesEXT* pMultisampleProperties);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR void VKAPI_CALL vkCmdSetSampleLocationsEXT(
+    VkCommandBuffer                             commandBuffer,
+    const VkSampleLocationsInfoEXT*             pSampleLocationsInfo);
+
+VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMultisamplePropertiesEXT(
+    VkPhysicalDevice                            physicalDevice,
+    VkSampleCountFlagBits                       samples,
+    VkMultisamplePropertiesEXT*                 pMultisampleProperties);
+#endif
+
 #define VK_EXT_blend_operation_advanced 1
 #define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2
 #define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced"
@@ -6421,6 +6781,78 @@
 #define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle"
 
 
+#define VK_EXT_post_depth_coverage 1
+#define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1
+#define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage"
+
+
+#define VK_EXT_validation_cache 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkValidationCacheEXT)
+
+#define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1
+#define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache"
+
+
+typedef enum VkValidationCacheHeaderVersionEXT {
+    VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1,
+    VK_VALIDATION_CACHE_HEADER_VERSION_BEGIN_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT,
+    VK_VALIDATION_CACHE_HEADER_VERSION_END_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT,
+    VK_VALIDATION_CACHE_HEADER_VERSION_RANGE_SIZE_EXT = (VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT - VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT + 1),
+    VK_VALIDATION_CACHE_HEADER_VERSION_MAX_ENUM_EXT = 0x7FFFFFFF
+} VkValidationCacheHeaderVersionEXT;
+
+typedef VkFlags VkValidationCacheCreateFlagsEXT;
+
+typedef struct VkValidationCacheCreateInfoEXT {
+    VkStructureType                    sType;
+    const void*                        pNext;
+    VkValidationCacheCreateFlagsEXT    flags;
+    size_t                             initialDataSize;
+    const void*                        pInitialData;
+} VkValidationCacheCreateInfoEXT;
+
+typedef struct VkShaderModuleValidationCacheCreateInfoEXT {
+    VkStructureType         sType;
+    const void*             pNext;
+    VkValidationCacheEXT    validationCache;
+} VkShaderModuleValidationCacheCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateValidationCacheEXT)(VkDevice device, const VkValidationCacheCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkValidationCacheEXT* pValidationCache);
+typedef void (VKAPI_PTR *PFN_vkDestroyValidationCacheEXT)(VkDevice device, VkValidationCacheEXT validationCache, const VkAllocationCallbacks* pAllocator);
+typedef VkResult (VKAPI_PTR *PFN_vkMergeValidationCachesEXT)(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, const VkValidationCacheEXT* pSrcCaches);
+typedef VkResult (VKAPI_PTR *PFN_vkGetValidationCacheDataEXT)(VkDevice device, VkValidationCacheEXT validationCache, size_t* pDataSize, void* pData);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateValidationCacheEXT(
+    VkDevice                                    device,
+    const VkValidationCacheCreateInfoEXT*       pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkValidationCacheEXT*                       pValidationCache);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyValidationCacheEXT(
+    VkDevice                                    device,
+    VkValidationCacheEXT                        validationCache,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkMergeValidationCachesEXT(
+    VkDevice                                    device,
+    VkValidationCacheEXT                        dstCache,
+    uint32_t                                    srcCacheCount,
+    const VkValidationCacheEXT*                 pSrcCaches);
+
+VKAPI_ATTR VkResult VKAPI_CALL vkGetValidationCacheDataEXT(
+    VkDevice                                    device,
+    VkValidationCacheEXT                        validationCache,
+    size_t*                                     pDataSize,
+    void*                                       pData);
+#endif
+
+#define VK_EXT_shader_viewport_index_layer 1
+#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1
+#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer"
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 001eaf1..8dd55f4 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -467,6 +467,7 @@
         "vkGetPhysicalDeviceImageFormatProperties2KHR",
         "vkGetPhysicalDeviceMemoryProperties",
         "vkGetPhysicalDeviceMemoryProperties2KHR",
+        "vkGetPhysicalDeviceMultisamplePropertiesEXT",
         "vkGetPhysicalDevicePresentRectanglesKHX",
         "vkGetPhysicalDeviceProperties",
         "vkGetPhysicalDeviceProperties2KHR",