Merge "Revert "EGL: Ensure surfaces are disconnected when destroyed"" into nyc-dev
diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp
index 6892b57..917c813 100644
--- a/cmds/bugreport/bugreport.cpp
+++ b/cmds/bugreport/bugreport.cpp
@@ -28,6 +28,11 @@
 // output. All of the dumpstate output is written to stdout, including
 // any errors encountered while reading/writing the output.
 int main() {
+
+  fprintf(stderr, "=============================================================================\n");
+  fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n");
+  fprintf(stderr, "=============================================================================\n\n\n");
+
   // Start the dumpstate service.
   property_set("ctl.start", "dumpstate");
 
diff --git a/cmds/bugreportz/Android.mk b/cmds/bugreportz/Android.mk
new file mode 100644
index 0000000..14ba225
--- /dev/null
+++ b/cmds/bugreportz/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= bugreportz.cpp
+
+LOCAL_MODULE:= bugreportz
+
+LOCAL_CFLAGS := -Wall
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
new file mode 100644
index 0000000..19d2d64
--- /dev/null
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+static constexpr char VERSION[] = "1.0";
+
+static void show_usage() {
+  fprintf(stderr,
+          "usage: bugreportz [-h | -v]\n"
+          "  -h: to display this help message\n"
+          "  -v: to display the version\n"
+          "  or no arguments to generate a zipped bugreport\n");
+}
+
+static void show_version() {
+  fprintf(stderr, "%s\n", VERSION);
+}
+
+int main(int argc, char *argv[]) {
+
+    if (argc > 1) {
+        /* parse arguments */
+        int c;
+        while ((c = getopt(argc, argv, "vh")) != -1) {
+            switch (c) {
+                case 'h':
+                    show_usage();
+                    return EXIT_SUCCESS;
+                case 'v':
+                    show_version();
+                    return EXIT_SUCCESS;
+                default:
+                    show_usage();
+                    return EXIT_FAILURE;
+            }
+        }
+        // passed an argument not starting with -
+        if (optind > 1 || argv[optind] != nullptr) {
+            show_usage();
+            return EXIT_FAILURE;
+        }
+    }
+
+    // TODO: code below was copy-and-pasted from bugreport.cpp (except by the timeout value);
+    // should be reused instead.
+
+    // Start the dumpstatez service.
+    property_set("ctl.start", "dumpstatez");
+
+    // Socket will not be available until service starts.
+    int s;
+    for (int i = 0; i < 20; i++) {
+        s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+        if (s >= 0)
+            break;
+        // Try again in 1 second.
+        sleep(1);
+    }
+
+    if (s == -1) {
+        printf("Failed to connect to dumpstatez service: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    // Set a timeout so that if nothing is read in 10 minutes, we'll stop
+    // reading and quit. No timeout in dumpstate is longer than 60 seconds,
+    // so this gives lots of leeway in case of unforeseen time outs.
+    struct timeval tv;
+    tv.tv_sec = 10 * 60;
+    tv.tv_usec = 0;
+    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+        printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+    }
+
+    while (1) {
+        char buffer[65536];
+        ssize_t bytes_read = TEMP_FAILURE_RETRY(
+                read(s, buffer, sizeof(buffer)));
+        if (bytes_read == 0) {
+            break;
+        } else if (bytes_read == -1) {
+            // EAGAIN really means time out, so change the errno.
+            if (errno == EAGAIN) {
+                errno = ETIMEDOUT;
+            }
+            printf("\nBugreport read terminated abnormally (%s).\n",
+                    strerror(errno));
+            break;
+        }
+
+        ssize_t bytes_to_send = bytes_read;
+        ssize_t bytes_written;
+        do {
+            bytes_written = TEMP_FAILURE_RETRY(
+                    write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send,
+                            bytes_to_send));
+            if (bytes_written == -1) {
+                printf(
+                        "Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
+                        bytes_read, bytes_to_send, strerror(errno));
+                return EXIT_FAILURE;
+            }
+            bytes_to_send -= bytes_written;
+        } while (bytes_written != 0 && bytes_to_send > 0);
+    }
+
+    close(s);
+    return EXIT_SUCCESS;
+}
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
new file mode 100644
index 0000000..85aafce
--- /dev/null
+++ b/cmds/bugreportz/readme.md
@@ -0,0 +1,12 @@
+# bugreportz protocol
+
+`bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using
+the simple protocol defined below.
+
+
+## Version 1.0
+On version 1.0, `bugreportz` does not generate any output on `stdout` until the bugreport is
+finished, when it then prints one line with the result:
+
+- `OK:<path_to_bugreport_file>` in case of success.
+- `FAIL:<error message>` in case of failure.
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index 484f97f..d7837ae 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -26,16 +26,16 @@
 is a failure, in which case it reverts to the flat file that is zipped by
 **Shell** and hence the end result is the _v0_ format).
 
-The zip file is by default called _bugreport-DATE.zip_ and it contains a
-_bugreport-DATE.txt_ entry, although the end user can change the name (through
-**Shell**), in which case they would be called _bugreport-NEW_NAME.zip_ and
-_bugreport-NEW_NAME.txt_ respectively.
+The zip file is by default called _bugreport-BUILD_ID-DATE.zip_ and it contains a
+_bugreport-BUILD_ID-DATE.txt_ entry, although the end user can change the name (through
+**Shell**), in which case they would be called _bugreport-BUILD_ID-NEW_NAME.zip_ and
+_bugreport-BUILD_ID-NEW_NAME.txt_ respectively.
 
 The zip file also contains 2 metadata entries generated by `dumpstate`:
 
 - `version.txt`:  whose value is **v1**.
 - `main-entry.txt`: whose value is the name of the flat text entry (i.e.,
-  _bugreport-DATE.txt_ or _bugreport-NEW_NAME.txt_).
+  _bugreport-BUILD_ID-DATE.txt_ or _bugreport-NEW_NAME.txt_).
 
 `dumpstate` can also copy files from the device’s filesystem into the zip file
 under the `FS` folder. For example, a `/dirA/dirB/fileC` file in the device
@@ -62,9 +62,9 @@
 
 For example, the initial version during _Android N_ development was
 **v1-dev1**. When `dumpsys` was split in 2 sections but not all tools were
-ready to parse that format, the version was named **v1-dev1-dumpsys-split**,
+ready to parse that format, the version was named **v1-dev2**,
 which had to be passed do `dumpsys` explicitly (i.e., trhough a
-`-V v1-dev1-dumpsys-split` argument). Once that format became stable and tools
+`-V v1-dev2` argument). Once that format became stable and tools
 knew how to parse it, the default version became **v1-dev2**.
 
 Similarly, if changes in the file format are made after the initial release of
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 30ddec3..ea14c66 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -63,12 +63,14 @@
 void add_mountinfo();
 static bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
 static bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
+static int control_socket_fd;
 
 #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
 
 #define RAFT_DIR "/data/misc/raft/"
 #define RECOVERY_DIR "/cache/recovery"
 #define RECOVERY_DATA_DIR "/data/misc/recovery"
+#define LOGPERSIST_DATA_DIR "/data/misc/logd"
 #define TOMBSTONE_DIR "/data/tombstones"
 #define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
 /* Can accomodate a tombstone number up to 9999. */
@@ -91,7 +93,7 @@
  * See bugreport-format.txt for more info.
  */
 // TODO: change to "v1" before final N build
-static std::string VERSION_DEFAULT = "v1-dev3";
+static std::string VERSION_DEFAULT = "v1-dev4";
 
 /* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
  * otherwise gets just those modified in the last half an hour. */
@@ -169,11 +171,15 @@
     closedir(d);
 }
 
-static void dump_systrace() {
+static void dump_systrace(const std::string& systrace_path) {
     if (!zip_writer) {
         MYLOGD("Not dumping systrace because zip_writer is not set\n");
         return;
     }
+    if (systrace_path.empty()) {
+        MYLOGE("Not dumping systrace because path is empty\n");
+        return;
+    }
     const char* path = "/sys/kernel/debug/tracing/tracing_on";
     long int is_tracing;
     if (read_file_as_long(path, &is_tracing)) {
@@ -184,28 +190,24 @@
         return;
     }
 
-    DurationReporter duration_reporter("SYSTRACE", nullptr);
-    // systrace output can be many MBs, so we need to redirect its stdout straight to the zip file
-    // by forking and using a pipe.
-    int pipefd[2];
-    pipe(pipefd);
-    if (fork() == 0) {
-        close(pipefd[0]);    // close reading end in the child
-        dup2(pipefd[1], STDOUT_FILENO);  // send stdout to the pipe
-        dup2(pipefd[1], STDERR_FILENO);  // send stderr to the pipe
-        close(pipefd[1]);    // this descriptor is no longer needed
-
-        // TODO: ideally it should use run_command, but it doesn't work well with pipes.
-        // The drawback of calling execl directly is that we're not timing out if it hangs.
-        MYLOGD("Running '/system/bin/atrace --async_dump', which can take several seconds");
-        execl("/system/bin/atrace", "/system/bin/atrace", "--async_dump", nullptr);
-        // execl should never return, but if it did, we need to exit.
-        MYLOGD("execl on '/system/bin/atrace --async_dump' failed: %s", strerror(errno));
-        // Must call _exit (instead of exit), otherwise it will corrupt the zip file.
-        _exit(EXIT_FAILURE);
+    MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
+            systrace_path.c_str());
+    if (run_command("SYSTRACE", 120, "/system/bin/atrace", "--async_dump", "-o",
+            systrace_path.c_str(), NULL)) {
+        MYLOGE("systrace timed out, its zip entry will be incomplete\n");
+        // TODO: run_command tries to kill the process, but atrace doesn't die peacefully; ideally,
+        // we should call strace to stop itself, but there is no such option yet (just a
+        // --async_stop, which stops and dump
+        //        if (run_command("SYSTRACE", 10, "/system/bin/atrace", "--kill", NULL)) {
+        //            MYLOGE("could not stop systrace ");
+        //        }
+    }
+    if (!add_zip_entry("systrace.txt", systrace_path)) {
+        MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
     } else {
-        close(pipefd[1]);  // close the write end of the pipe in the parent
-        add_zip_entry_from_fd("systrace.txt", pipefd[0]); // write output to zip file
+        if (remove(systrace_path.c_str())) {
+            MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno));
+        }
     }
 }
 
@@ -927,20 +929,26 @@
 }
 
 static void usage() {
-    fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s] [-q] [-B] [-P] [-R] [-V version]\n"
-            "  -b: play sound file instead of vibrate, at beginning of job\n"
-            "  -e: play sound file instead of vibrate, at end of job\n"
-            "  -o: write to file (instead of stdout)\n"
-            "  -d: append date to filename (requires -o)\n"
-            "  -p: capture screenshot to filename.png (requires -o)\n"
-            "  -z: generates zipped file (requires -o)\n"
-            "  -s: write output to control socket (for init)\n"
-            "  -q: disable vibrate\n"
-            "  -B: send broadcast when finished (requires -o)\n"
-            "  -P: send broadcast when started and update system properties on progress (requires -o and -B)\n"
-            "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, shouldn't be used with -P)\n"
-            "  -V: sets the bugreport format version (valid values: %s)\n",
-            VERSION_DEFAULT.c_str());
+  fprintf(stderr,
+          "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
+          "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+          "  -h: display this help message\n"
+          "  -b: play sound file instead of vibrate, at beginning of job\n"
+          "  -e: play sound file instead of vibrate, at end of job\n"
+          "  -o: write to file (instead of stdout)\n"
+          "  -d: append date to filename (requires -o)\n"
+          "  -p: capture screenshot to filename.png (requires -o)\n"
+          "  -z: generate zipped file (requires -o)\n"
+          "  -s: write output to control socket (for init)\n"
+          "  -S: write file location to control socket (for init; requires -o and -z)"
+          "  -q: disable vibrate\n"
+          "  -B: send broadcast when finished (requires -o)\n"
+          "  -P: send broadcast when started and update system properties on "
+          "progress (requires -o and -B)\n"
+          "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
+          "shouldn't be used with -P)\n"
+          "  -V: sets the bugreport format version (valid values: %s)\n",
+          VERSION_DEFAULT.c_str());
 }
 
 static void sigpipe_handler(int n) {
@@ -1012,6 +1020,7 @@
     int do_vibrate = 1;
     char* use_outfile = 0;
     int use_socket = 0;
+    int use_control_socket = 0;
     int do_fb = 0;
     int do_broadcast = 0;
     int do_early_screenshot = 0;
@@ -1020,15 +1029,6 @@
 
     now = time(NULL);
 
-    if (getuid() != 0) {
-        // Old versions of the adb client would call the
-        // dumpstate command directly. Newer clients
-        // call /system/bin/bugreport instead. If we detect
-        // we're being called incorrectly, then exec the
-        // correct program.
-        return execl("/system/bin/bugreport", "/system/bin/bugreport", NULL);
-    }
-
     MYLOGI("begin\n");
 
     /* gets the sequential id */
@@ -1057,12 +1057,13 @@
     format_args(argc, const_cast<const char **>(argv), &args);
     MYLOGD("Dumpstate command line: %s\n", args.c_str());
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
         switch (c) {
             case 'd': do_add_date = 1;          break;
             case 'z': do_zip_file = 1;          break;
             case 'o': use_outfile = optarg;     break;
             case 's': use_socket = 1;           break;
+            case 'S': use_control_socket = 1;   break;
             case 'v': break;  // compatibility no-op
             case 'q': do_vibrate = 0;           break;
             case 'p': do_fb = 1;                break;
@@ -1082,6 +1083,11 @@
         exit(1);
     }
 
+    if (use_control_socket && !do_zip_file) {
+        usage();
+        exit(1);
+    }
+
     if (do_update_progress && !do_broadcast) {
         usage();
         exit(1);
@@ -1093,8 +1099,8 @@
     }
 
     if (version != VERSION_DEFAULT) {
-        usage();
-        exit(1);
+      usage();
+      exit(1);
     }
 
     MYLOGI("bugreport format version: %s\n", version.c_str());
@@ -1107,6 +1113,11 @@
         redirect_to_socket(stdout, "dumpstate");
     }
 
+    if (use_control_socket) {
+        MYLOGD("Opening control socket\n");
+        control_socket_fd = open_socket("dumpstate");
+    }
+
     /* full path of the directory where the bugreport files will be written */
     std::string bugreport_dir;
 
@@ -1116,6 +1127,9 @@
     /* full path of the file containing the dumpstate logs*/
     std::string log_path;
 
+    /* full path of the systrace file, when enabled */
+    std::string systrace_path;
+
     /* full path of the temporary file containing the screenshot (when requested) */
     std::string screenshot_path;
 
@@ -1145,6 +1159,9 @@
         } else {
             suffix = "undated";
         }
+        char build_id[PROPERTY_VALUE_MAX];
+        property_get("ro.build.id", build_id, "UNKNOWN_BUILD");
+        base_name = base_name + "-" + build_id;
         if (do_fb) {
             // TODO: if dumpstate was an object, the paths could be internal variables and then
             // we could have a function to calculate the derived values, such as:
@@ -1154,6 +1171,7 @@
         tmp_path = bugreport_dir + "/" + base_name + "-" + suffix + ".tmp";
         log_path = bugreport_dir + "/dumpstate_log-" + suffix + "-"
                 + std::to_string(getpid()) + ".txt";
+        systrace_path = bugreport_dir + "/systrace-" + suffix + ".txt";
 
         MYLOGD("Bugreport dir: %s\n"
                 "Base name: %s\n"
@@ -1248,7 +1266,7 @@
     print_header(version);
 
     // Dumps systrace right away, otherwise it will be filled with unnecessary events.
-    dump_systrace();
+    dump_systrace(systrace_path);
 
     // Invoking the following dumpsys calls before dump_traces() to try and
     // keep the system stats as close to its initial state as possible.
@@ -1262,6 +1280,7 @@
     get_tombstone_fds(tombstone_data);
     add_dir(RECOVERY_DIR, true);
     add_dir(RECOVERY_DATA_DIR, true);
+    add_dir(LOGPERSIST_DATA_DIR, false);
     add_mountinfo();
 
     if (!drop_root_user()) {
@@ -1338,6 +1357,14 @@
                 path.clear();
             }
         }
+        if (use_control_socket) {
+            if (do_text_file) {
+                dprintf(control_socket_fd, "FAIL:could not create zip file, check %s "
+                        "for more details\n", log_path.c_str());
+            } else {
+                dprintf(control_socket_fd, "OK:%s\n", path.c_str());
+            }
+        }
     }
 
     /* vibrate a few but shortly times to let user know it's finished */
@@ -1385,5 +1412,10 @@
         fclose(stderr);
     }
 
+    if (use_control_socket && control_socket_fd >= 0) {
+        MYLOGD("Closing control socket\n");
+        close(control_socket_fd);
+    }
+
     return 0;
 }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 943f38e..c51c79a 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -109,10 +109,14 @@
 int run_command_as_shell(const char *title, int timeout_seconds, const char *command, ...);
 int run_command(const char *title, int timeout_seconds, const char *command, ...);
 
+enum RootMode { DROP_ROOT, DONT_DROP_ROOT };
+enum StdoutMode { NORMAL_STDOUT, REDIRECT_TO_STDERR };
+
 /* forks a command and waits for it to finish
    first element of args is the command, and last must be NULL.
    command is always ran, even when _DUMPSTATE_DRY_RUN_ is defined. */
-int run_command_always(const char *title, bool drop_root, int timeout_seconds, const char *args[]);
+int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
+        int timeout_seconds, const char *args[]);
 
 /* switch to non-root user and group */
 bool drop_root_user();
@@ -126,6 +130,9 @@
 /* prints all the system properties */
 void print_properties();
 
+/** opens a socket and returns its file descriptor */
+int open_socket(const char *service);
+
 /* redirect output to a service control socket */
 void redirect_to_socket(FILE *redirect, const char *service);
 
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 96232c4..1f56d21 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -9,6 +9,15 @@
     disabled
     oneshot
 
+# dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
+# it is finished.
+service dumpstatez /system/bin/dumpstate -S -d -z \
+        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
+    socket dumpstate stream 0660 shell log
+    class main
+    disabled
+    oneshot
+
 # bugreportplus is an enhanced version of bugreport that provides a better
 # user interface (like displaying progress and allowing user to enter details).
 # It's typically triggered by the power button or developer settings.
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 8c0e840..f1a1ed6 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -51,10 +51,14 @@
 static const int64_t NANOS_PER_SEC = 1000000000;
 
 /* list of native processes to include in the native dumps */
+// This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
 static const char* native_processes_to_dump[] = {
         "/system/bin/audioserver",
         "/system/bin/cameraserver",
         "/system/bin/drmserver",
+        "/system/bin/mediacodec",     // media.codec
+        "/system/bin/mediadrmserver",
+        "/system/bin/mediaextractor", // media.extractor
         "/system/bin/mediaserver",
         "/system/bin/sdcard",
         "/system/bin/surfaceflinger",
@@ -664,7 +668,7 @@
 
     ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
 
-    int status = run_command_always(title, false, timeout_seconds, args);
+    int status = run_command_always(title, DONT_DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
     va_end(ap);
     return status;
 }
@@ -701,13 +705,15 @@
 
     ON_DRY_RUN({ update_progress(timeout_seconds); va_end(ap); return 0; });
 
-    int status = run_command_always(title, true, timeout_seconds, args);
+    int status = run_command_always(title, DROP_ROOT, NORMAL_STDOUT, timeout_seconds, args);
     va_end(ap);
     return status;
 }
 
 /* forks a command and waits for it to finish */
-int run_command_always(const char *title, bool drop_root, int timeout_seconds, const char *args[]) {
+int run_command_always(const char *title, RootMode root_mode, StdoutMode stdout_mode,
+        int timeout_seconds, const char *args[]) {
+    bool silent = (stdout_mode == REDIRECT_TO_STDERR);
     // TODO: need to check if args is null-terminated, otherwise execvp will crash dumpstate
 
     /* TODO: for now we're simplifying the progress calculation by using the timeout as the weight.
@@ -721,17 +727,25 @@
 
     /* handle error case */
     if (pid < 0) {
-        printf("*** fork: %s\n", strerror(errno));
+        if (!silent) printf("*** fork: %s\n", strerror(errno));
+        MYLOGE("*** fork: %s\n", strerror(errno));
         return pid;
     }
 
     /* handle child case */
     if (pid == 0) {
-        if (drop_root && !drop_root_user()) {
-            printf("*** could not drop root before running %s: %s\n", command, strerror(errno));
+        if (root_mode == DROP_ROOT && !drop_root_user()) {
+        if (!silent) printf("*** fail todrop root before running %s: %s\n", command,
+                strerror(errno));
+            MYLOGE("*** could not drop root before running %s: %s\n", command, strerror(errno));
             return -1;
         }
 
+        if (silent) {
+            // Redirect stderr to stdout
+            dup2(STDERR_FILENO, STDOUT_FILENO);
+        }
+
         /* make sure the child dies when dumpstate dies */
         prctl(PR_SET_PDEATHSIG, SIGKILL);
 
@@ -758,14 +772,14 @@
     if (!ret) {
         if (errno == ETIMEDOUT) {
             format_args(command, args, &cmd);
-            printf("*** command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
+            if (!silent) printf("*** command '%s' timed out after %.3fs (killing pid %d)\n",
+            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
             MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", cmd.c_str(),
                    (float) elapsed / NANOS_PER_SEC, pid);
         } else {
             format_args(command, args, &cmd);
-            printf("*** command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
-                   (float) elapsed / NANOS_PER_SEC, pid);
+            if (!silent) printf("*** command '%s': Error after %.4fs (killing pid %d)\n",
+            cmd.c_str(), (float) elapsed / NANOS_PER_SEC, pid);
             MYLOGE("command '%s': Error after %.4fs (killing pid %d)\n", cmd.c_str(),
                    (float) elapsed / NANOS_PER_SEC, pid);
         }
@@ -773,22 +787,25 @@
         if (!waitpid_with_timeout(pid, 5, NULL)) {
             kill(pid, SIGKILL);
             if (!waitpid_with_timeout(pid, 5, NULL)) {
-                printf("couldn not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
-                MYLOGE("couldn not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
+                if (!silent) printf("could not kill command '%s' (pid %d) even with SIGKILL.\n",
+                        command, pid);
+                MYLOGE("could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid);
             }
         }
         return -1;
     } else if (status) {
         format_args(command, args, &cmd);
-        printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
+        if (!silent) printf("*** command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
         MYLOGE("command '%s' failed: %s\n", cmd.c_str(), strerror(errno));
         return -2;
     }
 
     if (WIFSIGNALED(status)) {
-        printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
+        if (!silent) printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
+        MYLOGE("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
     } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
-        printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
+        if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
+        MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
     }
 
     if (weight > 0) {
@@ -859,7 +876,7 @@
     std::string args_string;
     format_args(am_index + 1, am_args, &args_string);
     MYLOGD("send_broadcast command: %s\n", args_string.c_str());
-    run_command_always(NULL, true, 20, am_args);
+    run_command_always(NULL, DROP_ROOT, REDIRECT_TO_STDERR, 20, am_args);
 }
 
 size_t num_props = 0;
@@ -896,8 +913,7 @@
     printf("\n");
 }
 
-/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service) {
+int open_socket(const char *service) {
     int s = android_get_control_socket(service);
     if (s < 0) {
         MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
@@ -917,11 +933,18 @@
         exit(1);
     }
 
+    return fd;
+}
+
+/* redirect output to a service control socket */
+void redirect_to_socket(FILE *redirect, const char *service) {
+    int fd = open_socket(service);
     fflush(redirect);
     dup2(fd, fileno(redirect));
     close(fd);
 }
 
+// TODO: should call is_valid_output_file and/or be merged into it.
 void create_parent_dirs(const char *path) {
     char *chp = const_cast<char *> (path);
 
@@ -1198,7 +1221,7 @@
 
 void take_screenshot(const std::string& path) {
     const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
-    run_command_always(NULL, false, 10, args);
+    run_command_always(NULL, DONT_DROP_ROOT, REDIRECT_TO_STDERR, 10, args);
 }
 
 void vibrate(FILE* vibrator, int ms) {
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 95bc4b9..f4d894b 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -30,6 +30,7 @@
 
 #include <android-base/stringprintf.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/log.h>               // TODO: Move everything to base/logging.
 #include <cutils/sched_policy.h>
@@ -73,7 +74,7 @@
     uid_t uid = multiuser_get_uid(userid, appid);
     int target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
     if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_package_path(uuid, userid, pkgname);
+        auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
         if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
             PLOG(ERROR) << "Failed to prepare " << path;
             return -1;
@@ -123,7 +124,7 @@
     // consistent location.  This only works on non-FBE devices, since we
     // never want to risk exposing data on a device with real CE/DE storage.
 
-    auto ce_path = create_data_user_package_path(uuid, userid, pkgname);
+    auto ce_path = create_data_user_ce_package_path(uuid, userid, pkgname);
     auto de_path = create_data_user_de_package_path(uuid, userid, pkgname);
 
     // If neither directory is marked as default, assume CE is default
@@ -162,8 +163,8 @@
 }
 
 static bool clear_profile(const std::string& profile) {
-    fd_t fd = open(profile.c_str(), O_WRONLY | O_NOFOLLOW);
-    if (fd < 0) {
+    base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+    if (ufd.get() < 0) {
         if (errno != ENOENT) {
             PLOG(WARNING) << "Could not open profile " << profile;
             return false;
@@ -173,7 +174,7 @@
         }
     }
 
-    if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
+    if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
         if (errno != EWOULDBLOCK) {
             PLOG(WARNING) << "Error locking profile " << profile;
         }
@@ -195,11 +196,11 @@
         return false;
     }
 
-    bool truncated = ftruncate(fd, 0) == 0;
+    bool truncated = ftruncate(ufd.get(), 0) == 0;
     if (!truncated) {
         PLOG(WARNING) << "Could not truncate " << profile;
     }
-    if (flock(fd, LOCK_UN) != 0) {
+    if (flock(ufd.get(), LOCK_UN) != 0) {
         PLOG(WARNING) << "Error unlocking profile " << profile;
     }
     return truncated;
@@ -233,7 +234,8 @@
     return success ? 0 : -1;
 }
 
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
+int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
+        ino_t ce_data_inode) {
     std::string suffix = "";
     bool only_cache = false;
     if (flags & FLAG_CLEAR_CACHE_ONLY) {
@@ -246,7 +248,7 @@
 
     int res = 0;
     if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_package_path(uuid, userid, pkgname) + suffix;
+        auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode) + suffix;
         if (access(path.c_str(), F_OK) == 0) {
             res |= delete_dir_contents(path);
         }
@@ -288,11 +290,12 @@
     return result;
 }
 
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
+int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
+        ino_t ce_data_inode) {
     int res = 0;
     if (flags & FLAG_STORAGE_CE) {
         res |= delete_dir_contents_and_dir(
-                create_data_user_package_path(uuid, userid, pkgname));
+                create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode));
     }
     if (flags & FLAG_STORAGE_DE) {
         res |= delete_dir_contents_and_dir(
@@ -345,9 +348,9 @@
     // Copy private data for all known users
     // TODO: handle user_de paths
     for (auto user : users) {
-        std::string from(create_data_user_package_path(from_uuid, user, package_name));
-        std::string to(create_data_user_package_path(to_uuid, user, package_name));
-        std::string to_parent(create_data_user_path(to_uuid, user));
+        std::string from(create_data_user_ce_package_path(from_uuid, user, package_name));
+        std::string to(create_data_user_ce_package_path(to_uuid, user, package_name));
+        std::string to_parent(create_data_user_ce_path(to_uuid, user));
 
         // Data source may not exist for all users; that's okay
         if (access(from.c_str(), F_OK) != 0) {
@@ -355,7 +358,7 @@
             continue;
         }
 
-        std::string user_path(create_data_user_path(to_uuid, user));
+        std::string user_path(create_data_user_ce_path(to_uuid, user));
         if (fs_prepare_dir(user_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM) != 0) {
             LOG(ERROR) << "Failed to prepare user target " << user_path;
             goto fail;
@@ -408,7 +411,7 @@
         }
     }
     for (auto user : users) {
-        std::string to(create_data_user_package_path(to_uuid, user, package_name));
+        std::string to(create_data_user_ce_package_path(to_uuid, user, package_name));
         if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
             LOG(WARNING) << "Failed to rollback " << to;
         }
@@ -416,38 +419,29 @@
     return -1;
 }
 
-int make_user_config(userid_t userid)
-{
-    if (ensure_config_user_dirs(userid) == -1) {
-        return -1;
+int create_user_data(const char *uuid, userid_t userid, int user_serial ATTRIBUTE_UNUSED,
+        int flags) {
+    if (flags & FLAG_STORAGE_DE) {
+        if (uuid == nullptr) {
+            return ensure_config_user_dirs(userid);
+        }
     }
-
     return 0;
 }
 
-int delete_user(const char *uuid, userid_t userid) {
+int destroy_user_data(const char *uuid, userid_t userid, int flags) {
     int res = 0;
-
-    std::string data_path(create_data_user_path(uuid, userid));
-    std::string data_de_path(create_data_user_de_path(uuid, userid));
-    std::string media_path(create_data_media_path(uuid, userid));
-    std::string profiles_path(create_data_user_profiles_path(userid));
-
-    res |= delete_dir_contents_and_dir(data_path);
-    // TODO: include result once 25796509 is fixed
-    delete_dir_contents_and_dir(data_de_path);
-    res |= delete_dir_contents_and_dir(media_path);
-    res |= delete_dir_contents_and_dir(profiles_path);
-
-    // Config paths only exist on internal storage
-    if (uuid == nullptr) {
-        char config_path[PATH_MAX];
-        if ((create_user_config_path(config_path, userid) != 0)
-                || (delete_dir_contents(config_path, 1, NULL) != 0)) {
-            res = -1;
+    if (flags & FLAG_STORAGE_DE) {
+        res |= delete_dir_contents_and_dir(create_data_user_de_path(uuid, userid), true);
+        if (uuid == nullptr) {
+            res |= delete_dir_contents_and_dir(create_data_misc_legacy_path(userid), true);
+            res |= delete_dir_contents_and_dir(create_data_user_profiles_path(userid), true);
         }
     }
-
+    if (flags & FLAG_STORAGE_CE) {
+        res |= delete_dir_contents_and_dir(create_data_user_ce_path(uuid, userid), true);
+        res |= delete_dir_contents_and_dir(create_data_media_path(uuid, userid), true);
+    }
     return res;
 }
 
@@ -479,7 +473,7 @@
 
     // Special case for owner on internal storage
     if (uuid == nullptr) {
-        std::string _tmpdir(create_data_user_path(nullptr, 0));
+        std::string _tmpdir(create_data_user_ce_path(nullptr, 0));
         add_cache_files(cache, _tmpdir.c_str(), "cache");
     }
 
@@ -566,142 +560,96 @@
     }
 }
 
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags,
-        const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath,
-        const char *asecpath, const char *instruction_set, int64_t *_codesize, int64_t *_datasize,
-        int64_t *_cachesize, int64_t* _asecsize) {
+static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *datasize,
+        int64_t *cachesize) {
     DIR *d;
     int dfd;
     struct dirent *de;
     struct stat s;
-    char path[PKG_PATH_MAX];
 
-    int64_t codesize = 0;
-    int64_t datasize = 0;
-    int64_t cachesize = 0;
-    int64_t asecsize = 0;
+    d = opendir(path.c_str());
+    if (d == nullptr) {
+        PLOG(WARNING) << "Failed to open " << path;
+        return;
+    }
+    dfd = dirfd(d);
+    while ((de = readdir(d))) {
+        const char *name = de->d_name;
 
-    /* count the source apk as code -- but only if it's not
-     * on the /system partition and its not on the sdcard. */
-    if (validate_system_app_path(apkpath) &&
-            strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) {
-        if (stat(apkpath, &s) == 0) {
-            codesize += stat_size(&s);
-            if (S_ISDIR(s.st_mode)) {
-                d = opendir(apkpath);
-                if (d != NULL) {
-                    dfd = dirfd(d);
-                    codesize += calculate_dir_size(dfd);
-                    closedir(d);
-                }
+        int64_t statsize = 0;
+        if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+            statsize = stat_size(&s);
+        }
+
+        if (de->d_type == DT_DIR) {
+            int subfd;
+            int64_t dirsize = 0;
+            /* always skip "." and ".." */
+            if (name[0] == '.') {
+                if (name[1] == 0) continue;
+                if ((name[1] == '.') && (name[2] == 0)) continue;
             }
-        }
-    }
-
-    /* count the forward locked apk as code if it is given */
-    if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') {
-        if (stat(fwdlock_apkpath, &s) == 0) {
-            codesize += stat_size(&s);
-        }
-    }
-
-    /* count the cached dexfile as code */
-    if (create_cache_path(path, apkpath, instruction_set)) {
-        if (stat(path, &s) == 0) {
-            codesize += stat_size(&s);
-        }
-    }
-
-    /* add in size of any libraries */
-    if (libdirpath != NULL && libdirpath[0] != '!') {
-        d = opendir(libdirpath);
-        if (d != NULL) {
-            dfd = dirfd(d);
-            codesize += calculate_dir_size(dfd);
-            closedir(d);
-        }
-    }
-
-    /* compute asec size if it is given */
-    if (asecpath != NULL && asecpath[0] != '!') {
-        if (stat(asecpath, &s) == 0) {
-            asecsize += stat_size(&s);
-        }
-    }
-
-    std::vector<userid_t> users;
-    if (userid == -1) {
-        users = get_known_users(uuid);
-    } else {
-        users.push_back(userid);
-    }
-
-    for (auto user : users) {
-        // TODO: handle user_de directories
-        if (!(flags & FLAG_STORAGE_CE)) continue;
-
-        std::string _pkgdir(create_data_user_package_path(uuid, user, pkgname));
-        const char* pkgdir = _pkgdir.c_str();
-
-        d = opendir(pkgdir);
-        if (d == NULL) {
-            PLOG(WARNING) << "Failed to open " << pkgdir;
-            continue;
-        }
-        dfd = dirfd(d);
-
-        /* most stuff in the pkgdir is data, except for the "cache"
-         * directory and below, which is cache, and the "lib" directory
-         * and below, which is code...
-         */
-        while ((de = readdir(d))) {
-            const char *name = de->d_name;
-
-            if (de->d_type == DT_DIR) {
-                int subfd;
-                int64_t statsize = 0;
-                int64_t dirsize = 0;
-                    /* always skip "." and ".." */
-                if (name[0] == '.') {
-                    if (name[1] == 0) continue;
-                    if ((name[1] == '.') && (name[2] == 0)) continue;
-                }
-                if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-                    statsize = stat_size(&s);
-                }
-                subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
-                if (subfd >= 0) {
-                    dirsize = calculate_dir_size(subfd);
-                }
-                if(!strcmp(name,"lib")) {
-                    codesize += dirsize + statsize;
-                } else if(!strcmp(name,"cache")) {
-                    cachesize += dirsize + statsize;
-                } else {
-                    datasize += dirsize + statsize;
-                }
-            } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) {
-                // This is the symbolic link to the application's library
-                // code.  We'll count this as code instead of data, since
-                // it is not something that the app creates.
-                if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-                    codesize += stat_size(&s);
-                }
+            subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+            if (subfd >= 0) {
+                dirsize = calculate_dir_size(subfd);
+                close(subfd);
+            }
+            // TODO: check xattrs!
+            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
+                *datasize += statsize;
+                *cachesize += dirsize;
             } else {
-                if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-                    datasize += stat_size(&s);
-                }
+                *datasize += dirsize + statsize;
             }
+        } else if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
+            *codesize += statsize;
+        } else {
+            *datasize += statsize;
         }
+    }
+    closedir(d);
+}
+
+int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
+        const char *code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
+        int64_t* asecsize) {
+    DIR *d;
+    int dfd;
+
+    d = opendir(code_path);
+    if (d != nullptr) {
+        dfd = dirfd(d);
+        *codesize += calculate_dir_size(dfd);
         closedir(d);
     }
-    *_codesize = codesize;
-    *_datasize = datasize;
-    *_cachesize = cachesize;
-    *_asecsize = asecsize;
+
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
+        add_app_data_size(path, codesize, datasize, cachesize);
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid, userid, pkgname);
+        add_app_data_size(path, codesize, datasize, cachesize);
+    }
+
+    *asecsize = 0;
+
     return 0;
 }
 
+int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) {
+    struct stat buf;
+    memset(&buf, 0, sizeof(buf));
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
+        if (stat(path.c_str(), &buf) == 0) {
+            *inode = buf.st_ino;
+            return 0;
+        }
+    }
+    return -1;
+}
+
 static int split_count(const char *str)
 {
   char *ctx;
@@ -1303,13 +1251,17 @@
     return true;
 }
 
-static int open_output_file(char* file_name, bool recreate) {
+static int open_output_file(char* file_name, bool recreate, int permissions) {
     int flags = O_RDWR | O_CREAT;
     if (recreate) {
-        unlink(file_name);
+        if (unlink(file_name) < 0) {
+            if (errno != ENOENT) {
+                PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
+            }
+        }
         flags |= O_EXCL;
     }
-    return open(file_name, flags, 0600);
+    return open(file_name, flags, permissions);
 }
 
 static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) {
@@ -1426,8 +1378,7 @@
         return -1;
     }
 
-    unlink(out_path);
-    out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+    out_fd = open_output_file(out_path, /*recreate*/true, /*permissions*/0644);
     if (out_fd < 0) {
         ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
         goto fail;
@@ -1441,7 +1392,7 @@
         // Make sure there really is enough space.
         strcpy(swap_file_name, out_path);
         if (add_extension_to_file_name(swap_file_name, ".swap")) {
-            swap_fd = open_output_file(swap_file_name, /*recreate*/true);
+            swap_fd = open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600);
         }
         if (swap_fd < 0) {
             // Could not create swap file. Optimistically go on and hope that we can compile
@@ -1449,7 +1400,9 @@
             ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
         } else {
             // Immediately unlink. We don't really want to hit flash.
-            unlink(swap_file_name);
+            if (unlink(swap_file_name) < 0) {
+                PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
+            }
         }
     }
 
@@ -1465,7 +1418,7 @@
       if (profile_guided && have_app_image_format) {
           // Recreate is true since we do not want to modify a mapped image. If the app is already
           // running and we modify the image file, it can cause crashes (b/27493510).
-          image_fd = open_output_file(image_path, /*recreate*/true);
+          image_fd = open_output_file(image_path, /*recreate*/true, /*permissions*/0600);
           if (image_fd < 0) {
               // Could not create application image file. Go on since we can compile without it.
               ALOGE("installd could not create '%s' for image file during dexopt\n", image_path);
@@ -1475,7 +1428,11 @@
       }
       // If we have a valid image file path but no image fd, erase the image file.
       if (image_fd < 0) {
-          unlink(image_path);
+          if (unlink(image_path) < 0) {
+              if (errno != ENOENT) {
+                  PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+              }
+          }
       }
     }
 
@@ -1607,7 +1564,7 @@
     struct stat s, libStat;
     int rc = 0;
 
-    std::string _pkgdir(create_data_user_package_path(uuid, userId, pkgname));
+    std::string _pkgdir(create_data_user_ce_package_path(uuid, userId, pkgname));
     std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
 
     const char* pkgdir = _pkgdir.c_str();
@@ -1794,7 +1751,7 @@
 
     uid_t uid = multiuser_get_uid(userid, appid);
     if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_package_path(uuid, userid, pkgName);
+        auto path = create_data_user_ce_package_path(uuid, userid, pkgName);
         if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
             PLOG(ERROR) << "restorecon failed for " << path;
             res = -1;
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index 13143c5..81c13b4 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -33,19 +33,22 @@
 int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
         appid_t appid, const char* seinfo);
 int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
+int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
+        ino_t ce_data_inode);
+int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
+        ino_t ce_data_inode);
 
 int move_complete_app(const char* from_uuid, const char *to_uuid, const char *package_name,
         const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version);
 
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags,
-        const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath,
-        const char *asecpath, const char *instruction_set, int64_t *codesize, int64_t *datasize,
-        int64_t *cachesize, int64_t *asecsize);
+int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
+        const char* code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
+        int64_t *asecsize);
+int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode);
 
-int make_user_config(userid_t userid);
-int delete_user(const char *uuid, userid_t userid);
+int create_user_data(const char *uuid, userid_t userid, int user_serial, int flags);
+int destroy_user_data(const char *uuid, userid_t userid, int flags);
+
 int rm_dex(const char *path, const char *instruction_set);
 int free_cache(const char *uuid, int64_t free_size);
 
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index de3f54a..eb51e70 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -207,13 +207,13 @@
 }
 
 static int do_clear_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags */
-    return clear_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
+    /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
+    return clear_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
 }
 
 static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags */
-    return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
+    /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
+    return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
 }
 
 // We use otapreopt_chroot to get into the chroot.
@@ -303,11 +303,10 @@
     int64_t asecsize = 0;
     int res = 0;
 
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags,
-            const char *apkpath, const char *libdirpath, const char *fwdlock_apkpath,
-            const char *asecpath, const char *instruction_set */
-    res = get_app_size(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), arg[4], arg[5],
-            arg[6], arg[7], arg[8], &codesize, &datasize, &cachesize, &asecsize);
+    /* const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
+            const char* code_path */
+    res = get_app_size(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]),
+            arg[5], &codesize, &datasize, &cachesize, &asecsize);
 
     /*
      * Each int64_t can take up 22 characters printed out. Make sure it
@@ -318,6 +317,17 @@
     return res;
 }
 
+static int do_get_app_data_inode(char **arg, char reply[REPLY_MAX]) {
+    ino_t inode = 0;
+    int res = 0;
+
+    /* const char *uuid, const char *pkgname, int userid, int flags */
+    res = get_app_data_inode(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), &inode);
+
+    snprintf(reply, REPLY_MAX, "%" PRId64, (int64_t) inode);
+    return res;
+}
+
 static int do_move_complete_app(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
     /* const char* from_uuid, const char *to_uuid, const char *package_name,
             const char *data_app_name, appid_t appid, const char* seinfo,
@@ -326,14 +336,16 @@
                              atoi(arg[4]), arg[5], atoi(arg[6]));
 }
 
-static int do_mk_user_config(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+static int do_create_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
 {
-    return make_user_config(atoi(arg[0])); /* userid */
+    /* const char *uuid, userid_t userid, int user_serial, int flags */
+    return create_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]), atoi(arg[3]));
 }
 
-static int do_rm_user(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+static int do_destroy_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
 {
-    return delete_user(parse_null(arg[0]), atoi(arg[1])); /* uuid, userid */
+    /* const char *uuid, userid_t userid, int flags */
+    return destroy_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]));
 }
 
 static int do_linklib(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
@@ -393,18 +405,20 @@
     { "create_app_data",      7, do_create_app_data },
     { "restorecon_app_data",  6, do_restorecon_app_data },
     { "migrate_app_data",     4, do_migrate_app_data },
-    { "clear_app_data",       4, do_clear_app_data },
-    { "destroy_app_data",     4, do_destroy_app_data },
+    { "clear_app_data",       5, do_clear_app_data },
+    { "destroy_app_data",     5, do_destroy_app_data },
     { "move_complete_app",    7, do_move_complete_app },
-    { "get_app_size",         9, do_get_app_size },
+    { "get_app_size",         6, do_get_app_size },
+    { "get_app_data_inode",   4, do_get_app_data_inode },
+
+    { "create_user_data",     4, do_create_user_data },
+    { "destroy_user_data",    3, do_destroy_user_data },
 
     { "dexopt",               9, do_dexopt },
     { "markbootcomplete",     1, do_mark_boot_complete },
     { "rmdex",                2, do_rm_dex },
     { "freecache",            2, do_free_cache },
     { "linklib",              4, do_linklib },
-    { "mkuserconfig",         1, do_mk_user_config },
-    { "rmuser",               2, do_rm_user },
     { "idmap",                3, do_idmap },
     { "createoatdir",         2, do_create_oat_dir },
     { "rmpackagedir",         1, do_rm_package_dir },
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index ff69f4b..9b2de88 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -472,13 +472,13 @@
 }
 
 TEST_F(UtilsTest, CreateDataUserPath) {
-    EXPECT_EQ("/data/data", create_data_user_path(nullptr, 0));
-    EXPECT_EQ("/data/user/10", create_data_user_path(nullptr, 10));
+    EXPECT_EQ("/data/data", create_data_user_ce_path(nullptr, 0));
+    EXPECT_EQ("/data/user/10", create_data_user_ce_path(nullptr, 10));
 
     EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/0",
-            create_data_user_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0));
+            create_data_user_ce_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0));
     EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/10",
-            create_data_user_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10));
+            create_data_user_ce_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10));
 }
 
 TEST_F(UtilsTest, CreateDataMediaPath) {
@@ -499,13 +499,13 @@
 }
 
 TEST_F(UtilsTest, CreateDataUserPackagePath) {
-    EXPECT_EQ("/data/data/com.example", create_data_user_package_path(nullptr, 0, "com.example"));
-    EXPECT_EQ("/data/user/10/com.example", create_data_user_package_path(nullptr, 10, "com.example"));
+    EXPECT_EQ("/data/data/com.example", create_data_user_ce_package_path(nullptr, 0, "com.example"));
+    EXPECT_EQ("/data/user/10/com.example", create_data_user_ce_package_path(nullptr, 10, "com.example"));
 
     EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/0/com.example",
-            create_data_user_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
+            create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
     EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/user/10/com.example",
-            create_data_user_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example"));
+            create_data_user_ce_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 10, "com.example"));
 }
 
 }  // namespace installd
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index d84d9f6..90d2a9e 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -81,11 +81,44 @@
  * volume UUID, package name, and user ID. An empty UUID is assumed to be
  * internal storage.
  */
-std::string create_data_user_package_path(const char* volume_uuid,
+std::string create_data_user_ce_package_path(const char* volume_uuid,
         userid_t user, const char* package_name) {
     check_package_name(package_name);
     return StringPrintf("%s/%s",
-            create_data_user_path(volume_uuid, user).c_str(), package_name);
+            create_data_user_ce_path(volume_uuid, user).c_str(), package_name);
+}
+
+std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user,
+        const char* package_name, ino_t ce_data_inode) {
+    // For testing purposes, rely on the inode when defined; this could be
+    // optimized to use access() in the future.
+    auto fallback = create_data_user_ce_package_path(volume_uuid, user, package_name);
+    if (ce_data_inode != 0) {
+        auto user_path = create_data_user_ce_path(volume_uuid, user);
+        DIR* dir = opendir(user_path.c_str());
+        if (dir == nullptr) {
+            PLOG(ERROR) << "Failed to opendir " << user_path;
+            return fallback;
+        }
+
+        struct dirent* ent;
+        while ((ent = readdir(dir))) {
+            if (ent->d_ino == ce_data_inode) {
+                auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
+                if (resolved != fallback) {
+                    LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
+                            << " instead of " << fallback;
+                }
+                closedir(dir);
+                return resolved;
+            }
+        }
+        LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
+        closedir(dir);
+        return fallback;
+    } else {
+        return fallback;
+    }
 }
 
 std::string create_data_user_de_package_path(const char* volume_uuid,
@@ -102,7 +135,7 @@
         return -1;
     }
 
-    std::string _tmp(create_data_user_package_path(nullptr, userid, pkgname) + postfix);
+    std::string _tmp(create_data_user_ce_package_path(nullptr, userid, pkgname) + postfix);
     const char* tmp = _tmp.c_str();
     if (strlen(tmp) >= PKG_PATH_MAX) {
         path[0] = '\0';
@@ -132,7 +165,7 @@
 /**
  * Create the path name for user data for a certain userid.
  */
-std::string create_data_user_path(const char* volume_uuid, userid_t userid) {
+std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid) {
     std::string data(create_data_path(volume_uuid));
     if (volume_uuid == nullptr) {
         if (userid == 0) {
@@ -160,6 +193,10 @@
     return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
 }
 
+std::string create_data_misc_legacy_path(userid_t userid) {
+    return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid);
+}
+
 std::string create_data_user_profiles_path(userid_t userid) {
     return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
 }
@@ -206,17 +243,6 @@
     return users;
 }
 
-/**
- * Create the path name for config for a certain userid.
- * Returns 0 on success, and -1 on failure.
- */
-int create_user_config_path(char path[PATH_MAX], userid_t userid) {
-    if (snprintf(path, PATH_MAX, "%s%d", "/data/misc/user/", userid) > PATH_MAX) {
-        return -1;
-    }
-    return 0;
-}
-
 int create_move_path(char path[PKG_PATH_MAX],
     const char* pkgname,
     const char* leaf,
@@ -1186,19 +1212,13 @@
 }
 
 int ensure_config_user_dirs(userid_t userid) {
-    char config_user_path[PATH_MAX];
-
     // writable by system, readable by any app within the same user
     const int uid = multiuser_get_uid(userid, AID_SYSTEM);
     const int gid = multiuser_get_uid(userid, AID_EVERYBODY);
 
     // Ensure /data/misc/user/<userid> exists
-    create_user_config_path(config_user_path, userid);
-    if (fs_prepare_dir(config_user_path, 0750, uid, gid) == -1) {
-        return -1;
-    }
-
-   return 0;
+    auto path = create_data_misc_legacy_path(userid);
+    return fs_prepare_dir(path.c_str(), 0750, uid, gid);
 }
 
 int wait_child(pid_t pid)
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 416a726..477baea 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -73,17 +73,20 @@
 
 std::string create_data_app_package_path(const char* volume_uuid, const char* package_name);
 
-// TODO: finish refactoring to "_ce"
-std::string create_data_user_path(const char* volume_uuid, userid_t userid);
+std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid);
 std::string create_data_user_de_path(const char* volume_uuid, userid_t userid);
 
-std::string create_data_user_package_path(const char* volume_uuid,
+std::string create_data_user_ce_package_path(const char* volume_uuid,
         userid_t user, const char* package_name);
+std::string create_data_user_ce_package_path(const char* volume_uuid,
+        userid_t user, const char* package_name, ino_t ce_data_inode);
 std::string create_data_user_de_package_path(const char* volume_uuid,
         userid_t user, const char* package_name);
 
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
 
+std::string create_data_misc_legacy_path(userid_t userid);
+
 std::string create_data_user_profiles_path(userid_t userid);
 std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
 std::string create_data_ref_profile_package_path(const char* package_name);
@@ -139,8 +142,6 @@
 char *build_string2(const char *s1, const char *s2);
 char *build_string3(const char *s1, const char *s2, const char *s3);
 
-int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
-int ensure_media_user_dirs(const char* uuid, userid_t userid);
 int ensure_config_user_dirs(userid_t userid);
 
 int wait_child(pid_t pid);
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 1ba339d..aee7bd8 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -11,4 +11,5 @@
     onrestart restart inputflinger
     onrestart restart drm
     onrestart restart cameraserver
+    writepid /dev/cpuset/system-background/tasks
 
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 8ed8460..ab89ef5 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -24,15 +24,11 @@
 -->
 <permissions>
     <feature name="android.hardware.audio.output" />
-    <feature name="android.hardware.camera" />
     <feature name="android.hardware.location" />
     <feature name="android.hardware.location.network" />
-    <feature name="android.hardware.sensor.compass" />
-    <feature name="android.hardware.sensor.accelerometer" />
     <feature name="android.hardware.bluetooth" />
     <feature name="android.hardware.touchscreen" />
     <feature name="android.hardware.microphone" />
-    <feature name="android.hardware.screen.landscape" />
     <!-- Feature to specify if the device is a car -->
     <feature name="android.hardware.type.automotive" />
 
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 346abad..4b7a706 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -22,7 +22,8 @@
 -->
 <permissions>
     <feature name="android.hardware.location" />
-    <feature name="android.hardware.sensor.compass" />
+    <!-- devices supporting compass/magnitometer sensor must include
+	 android.hardware.sensor.compass.xml -->
     <feature name="android.hardware.sensor.accelerometer" />
     <feature name="android.hardware.bluetooth" />
     <feature name="android.hardware.touchscreen" />
@@ -31,8 +32,10 @@
     <!-- basic system services -->
     <feature name="android.software.home_screen" />
 
-    <!-- devices that support a device administrator policy must include
-         android.software.device_admin.xml -->
+    <!-- device administration -->
+    <feature name="android.software.device_admin" />
+    <feature name="android.software.managed_users" />
+
     <!-- devices with GPS must include device/google/clockwork/gps.xml -->
     <!-- devices with an autofocus camera and/or flash must include either
          android.hardware.camera.autofocus.xml or
diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
index 912dcf6..b399905 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -68,6 +68,7 @@
     int batteryCurrent;
     int batteryCycleCount;
     int batteryFullCharge;
+    int batteryChargeCounter;
     String8 batteryTechnology;
 
     status_t writeToParcel(Parcel* parcel) const;
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
index f9edc2a..64cf72e 100644
--- a/include/binder/ProcessState.h
+++ b/include/binder/ProcessState.h
@@ -91,6 +91,8 @@
             size_t              mExecutingThreadsCount;
             // Maximum number for binder threads allowed for this process.
             size_t              mMaxThreads;
+            // Time when thread pool was emptied
+            int64_t             mStarvationStartTimeMs;
 
     mutable Mutex               mLock;  // protects everything below.
 
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index d232dbb..4337da9 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -319,6 +319,9 @@
         android_dataspace dataspace;
     } mSharedBufferCache;
 
+    // The slot of the last queued buffer
+    int mLastQueuedSlot;
+
 }; // class BufferQueueCore
 
 } // namespace android
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 691487d..7256f73 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -182,6 +182,10 @@
     // See IGraphicBufferProducer::setDequeueTimeout
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
 
+    // See IGraphicBufferProducer::getLastQueuedBuffer
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence) override;
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 32bf988..049406c 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -557,6 +557,15 @@
     //               happen because of trying to allocate/deallocate the async
     //               buffer.
     virtual status_t setDequeueTimeout(nsecs_t timeout) = 0;
+
+    // Returns the last queued buffer along with a fence which must signal
+    // before the contents of the buffer are read. If there are no buffers in
+    // the queue, outBuffer will be populated with nullptr and outFence will be
+    // populated with Fence::NO_FENCE
+    //
+    // Returns NO_ERROR or the status of the Binder transaction
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 8c3d49e..af26721 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -39,6 +39,7 @@
 class DisplayState;
 struct DisplayInfo;
 struct DisplayStatInfo;
+class HdrCapabilities;
 class IDisplayEventConnection;
 class IMemoryHeap;
 class Rect;
@@ -157,6 +158,13 @@
      * Requires the ACCESS_SURFACE_FLINGER permission.
      */
     virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0;
+
+    /* Gets the supported HDR capabilities of the given display.
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getHdrCapabilities(const sp<IBinder>& display,
+            HdrCapabilities* outCapabilities) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -184,6 +192,7 @@
         GET_ANIMATION_FRAME_STATS,
         SET_POWER_MODE,
         GET_DISPLAY_STATS,
+        GET_HDR_CAPABILITIES,
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index 3792540..353003c 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -56,7 +56,7 @@
         uint8_t b[16];
     } uuid_t;
 
-    Sensor();
+    Sensor(const char * name = "");
     Sensor(struct sensor_t const* hwSensor, int halVersion = 0);
     ~Sensor();
 
@@ -80,6 +80,7 @@
     int32_t getMaxDelay() const;
     uint32_t getFlags() const;
     bool isWakeUpSensor() const;
+    bool isDynamicSensor() const;
     int32_t getReportingMode() const;
     const uuid_t& getUuid() const;
 
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 5d1d7bf..bbf427d 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -122,6 +122,17 @@
     // See IGraphicBufferProducer::setDequeueTimeout
     status_t setDequeueTimeout(nsecs_t timeout);
 
+    /*
+     * Wait for frame number to increase past lastFrame for at most
+     * timeoutNs. Useful for one thread to wait for another unknown
+     * thread to queue a buffer.
+     */
+    bool waitForNextFrame(uint64_t lastFrame, nsecs_t timeout);
+
+    // See IGraphicBufferProducer::getLastQueuedBuffer
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence);
+
 protected:
     virtual ~Surface();
 
@@ -348,6 +359,8 @@
     // This is true if the shared buffer has already been queued/canceled. It's
     // used to prevent a mismatch between the number of queue/dequeue calls.
     bool mSharedBufferHasBeenQueued;
+
+    Condition mQueueBufferCondition;
 };
 
 namespace view {
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index 794fe4c..73f923c 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -40,6 +40,7 @@
 
 class DisplayInfo;
 class Composer;
+class HdrCapabilities;
 class ISurfaceComposerClient;
 class IGraphicBufferProducer;
 class Region;
@@ -137,6 +138,8 @@
     status_t    setLayerStack(const sp<IBinder>& id, uint32_t layerStack);
     status_t    deferTransactionUntil(const sp<IBinder>& id,
             const sp<IBinder>& handle, uint64_t frameNumber);
+    status_t    setOverrideScalingMode(const sp<IBinder>& id,
+            int32_t overrideScalingMode);
     status_t    destroySurface(const sp<IBinder>& id);
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
@@ -145,6 +148,9 @@
     static status_t clearAnimationFrameStats();
     static status_t getAnimationFrameStats(FrameStats* outStats);
 
+    static status_t getHdrCapabilities(const sp<IBinder>& display,
+            HdrCapabilities* outCapabilities);
+
     static void setDisplaySurface(const sp<IBinder>& token,
             const sp<IGraphicBufferProducer>& bufferProducer);
     static void setDisplayLayerStack(const sp<IBinder>& token,
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index 35644db..bedebb6 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -77,6 +77,11 @@
     // identified by handle reaches the given frameNumber
     status_t deferTransactionUntil(sp<IBinder> handle, uint64_t frameNumber);
 
+    // Set an override scaling mode as documented in <system/window.h>
+    // the override scaling mode will take precedence over any client
+    // specified scaling mode. -1 will clear the override scaling mode.
+    status_t setOverrideScalingMode(int32_t overrideScalingMode);
+
     static status_t writeSurfaceToParcel(
             const sp<SurfaceControl>& control, Parcel* parcel);
 
diff --git a/include/input/Input.h b/include/input/Input.h
index 3b1c86b..55787e7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -45,6 +45,19 @@
 };
 
 enum {
+
+    /**
+     * This flag indicates that the window that received this motion event is partly
+     * or wholly obscured by another visible window above it.  This flag is set to true
+     * even if the event did not directly pass through the obscured area.
+     * A security sensitive application can check this flag to identify situations in which
+     * a malicious application may have covered up part of its content for the purpose
+     * of misleading the user or hijacking touches.  An appropriate response might be
+     * to drop the suspect touches or to take additional precautions to confirm the user's
+     * actual intent.
+     */
+    AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
+
     /* Motion event is inconsistent with previously sent motion events. */
     AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index 1a2f0b5..4bb5f9a 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -77,10 +77,13 @@
 
 /** VP9 profiles */
 typedef enum OMX_VIDEO_VP9PROFILETYPE {
-    OMX_VIDEO_VP9Profile0 = 0x0,
-    OMX_VIDEO_VP9Profile1 = 0x1,
-    OMX_VIDEO_VP9Profile2 = 0x2,
-    OMX_VIDEO_VP9Profile3 = 0x3,
+    OMX_VIDEO_VP9Profile0 = 0x1,
+    OMX_VIDEO_VP9Profile1 = 0x2,
+    OMX_VIDEO_VP9Profile2 = 0x4,
+    OMX_VIDEO_VP9Profile3 = 0x8,
+    // HDR profiles also support passing HDR metadata
+    OMX_VIDEO_VP9Profile2HDR = 0x1000,
+    OMX_VIDEO_VP9Profile3HDR = 0x2000,
     OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF,
     OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF
 } OMX_VIDEO_VP9PROFILETYPE;
@@ -252,12 +255,13 @@
 /** Dolby Vision Profile enum type */
 typedef enum OMX_VIDEO_DOLBYVISIONPROFILETYPE {
     OMX_VIDEO_DolbyVisionProfileUnknown = 0x0,
-    OMX_VIDEO_DolbyVisionProfileDvavDer = 0x1,
-    OMX_VIDEO_DolbyVisionProfileDvavDen = 0x2,
-    OMX_VIDEO_DolbyVisionProfileDvheDer = 0x3,
-    OMX_VIDEO_DolbyVisionProfileDvheDen = 0x4,
-    OMX_VIDEO_DolbyVisionProfileDvheDtr = 0x5,
-    OMX_VIDEO_DolbyVisionProfileDvheStn = 0x6,
+    OMX_VIDEO_DolbyVisionProfileDvavPer = 0x1,
+    OMX_VIDEO_DolbyVisionProfileDvavPen = 0x2,
+    OMX_VIDEO_DolbyVisionProfileDvheDer = 0x4,
+    OMX_VIDEO_DolbyVisionProfileDvheDen = 0x8,
+    OMX_VIDEO_DolbyVisionProfileDvheDtr = 0x10,
+    OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20,
+    OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40,
     OMX_VIDEO_DolbyVisionProfileMax     = 0x7FFFFFFF
 } OMX_VIDEO_DOLBYVISIONPROFILETYPE;
 
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 078720a..92d31d1 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -53,7 +53,8 @@
         eLayerStackChanged          = 0x00000080,
         eCropChanged                = 0x00000100,
         eDeferTransaction           = 0x00000200,
-        eFinalCropChanged           = 0x00000400
+        eFinalCropChanged           = 0x00000400,
+        eOverrideScalingModeChanged = 0x00000800
     };
 
     layer_state_t()
@@ -61,7 +62,8 @@
             x(0), y(0), z(0), w(0), h(0), layerStack(0),
             alpha(0), flags(0), mask(0),
             reserved(0), crop(Rect::INVALID_RECT),
-            finalCrop(Rect::INVALID_RECT), frameNumber(0)
+            finalCrop(Rect::INVALID_RECT), frameNumber(0),
+            overrideScalingMode(-1)
     {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
@@ -93,6 +95,7 @@
             Rect            finalCrop;
             sp<IBinder>     handle;
             uint64_t        frameNumber;
+            int32_t         overrideScalingMode;
             // non POD must be last. see write/read
             Region          transparentRegion;
 };
diff --git a/include/ui/HdrCapabilities.h b/include/ui/HdrCapabilities.h
new file mode 100644
index 0000000..a7cd5fb
--- /dev/null
+++ b/include/ui/HdrCapabilities.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 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_UI_HDR_CAPABILTIES_H
+#define ANDROID_UI_HDR_CAPABILTIES_H
+
+#include <binder/Parcelable.h>
+
+namespace android {
+
+class HdrCapabilities : public Parcelable
+{
+public:
+    HdrCapabilities(const std::vector<int32_t /*android_hdr_t*/>& types,
+            float maxLuminance, float maxAverageLuminance, float minLuminance)
+      : mSupportedHdrTypes(types),
+        mMaxLuminance(maxLuminance),
+        mMaxAverageLuminance(maxAverageLuminance),
+        mMinLuminance(minLuminance) {}
+
+    // Make this move-constructable and move-assignable
+    HdrCapabilities(HdrCapabilities&& other) = default;
+    HdrCapabilities& operator=(HdrCapabilities&& other) = default;
+
+    HdrCapabilities()
+      : mSupportedHdrTypes(),
+        mMaxLuminance(-1.0f),
+        mMaxAverageLuminance(-1.0f),
+        mMinLuminance(-1.0f) {}
+
+    virtual ~HdrCapabilities() = default;
+
+    const std::vector<int32_t /*android_hdr_t*/>& getSupportedHdrTypes() const {
+        return mSupportedHdrTypes;
+    }
+    float getDesiredMaxLuminance() const { return mMaxLuminance; }
+    float getDesiredMaxAverageLuminance() const { return mMaxAverageLuminance; }
+    float getDesiredMinLuminance() const { return mMinLuminance; }
+
+    // Parcelable interface
+    virtual status_t writeToParcel(Parcel* parcel) const override;
+    virtual status_t readFromParcel(const Parcel* parcel) override;
+
+private:
+    std::vector<int32_t /*android_hdr_t*/> mSupportedHdrTypes;
+    float mMaxLuminance;
+    float mMaxAverageLuminance;
+    float mMinLuminance;
+};
+
+} // namespace android
+
+#endif
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 1cbcfe4..d90798f 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -24,12 +24,14 @@
 
 #include <cutils/sched_policy.h>
 #include <utils/Log.h>
+#include <utils/SystemClock.h>
 #include <utils/threads.h>
 
 #include <private/binder/binder_module.h>
 #include <private/binder/Static.h>
 
 #include <errno.h>
+#include <inttypes.h>
 #include <pthread.h>
 #include <sched.h>
 #include <signal.h>
@@ -434,12 +436,25 @@
 
         pthread_mutex_lock(&mProcess->mThreadCountLock);
         mProcess->mExecutingThreadsCount++;
+        if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
+                mProcess->mStarvationStartTimeMs == 0) {
+            mProcess->mStarvationStartTimeMs = uptimeMillis();
+        }
         pthread_mutex_unlock(&mProcess->mThreadCountLock);
 
         result = executeCommand(cmd);
 
         pthread_mutex_lock(&mProcess->mThreadCountLock);
         mProcess->mExecutingThreadsCount--;
+        if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
+                mProcess->mStarvationStartTimeMs != 0) {
+            int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
+            if (starvationTimeMs > 100) {
+                ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
+                      mProcess->mMaxThreads, starvationTimeMs);
+            }
+            mProcess->mStarvationStartTimeMs = 0;
+        }
         pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
         pthread_mutex_unlock(&mProcess->mThreadCountLock);
 
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 33fe26c..f13f49f 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -40,12 +40,12 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 
 #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
 #define DEFAULT_MAX_BINDER_THREADS 15
 
-
-// ---------------------------------------------------------------------------
+// -------------------------------------------------------------------------
 
 namespace android {
 
@@ -278,8 +278,9 @@
 
 String8 ProcessState::makeBinderThreadName() {
     int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+    pid_t pid = getpid();
     String8 name;
-    name.appendFormat("Binder_%X", s);
+    name.appendFormat("Binder:%d_%X", pid, s);
     return name;
 }
 
@@ -342,6 +343,7 @@
     , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
     , mExecutingThreadsCount(0)
     , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
+    , mStarvationStartTimeMs(0)
     , mManagesContexts(false)
     , mBinderContextCheckFunc(NULL)
     , mBinderContextUserData(NULL)
diff --git a/libs/diskusage/dirsize.c b/libs/diskusage/dirsize.c
index 7576994..5b5ab70 100644
--- a/libs/diskusage/dirsize.c
+++ b/libs/diskusage/dirsize.c
@@ -24,16 +24,7 @@
 
 int64_t stat_size(struct stat *s)
 {
-    int64_t blksize = s->st_blksize;
-    // count actual blocks used instead of nominal file size
-    int64_t size = s->st_blocks * 512;
-
-    if (blksize) {
-        /* round up to filesystem block size */
-        size = (size + blksize - 1) & (~(blksize - 1));
-    }
-
-    return size;
+    return s->st_blocks * 512;
 }
 
 int64_t calculate_dir_size(int dfd)
@@ -51,9 +42,6 @@
 
     while ((de = readdir(d))) {
         const char *name = de->d_name;
-        if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-            size += stat_size(&s);
-        }
         if (de->d_type == DT_DIR) {
             int subfd;
 
@@ -65,10 +53,17 @@
                     continue;
             }
 
+            if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+                size += stat_size(&s);
+            }
             subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
             if (subfd >= 0) {
                 size += calculate_dir_size(subfd);
             }
+        } else {
+            if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+                size += stat_size(&s);
+            }
         }
     }
     closedir(d);
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index b591c4e..cbc8893 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -622,6 +622,10 @@
             return NO_INIT;
         }
 
+        if (maxAcquiredBuffers == mCore->mMaxAcquiredBufferCount) {
+            return NO_ERROR;
+        }
+
         // The new maxAcquiredBuffers count should not be violated by the number
         // of currently acquired buffers
         int acquiredCount = 0;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index cdece73..052de3d 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -200,6 +200,10 @@
     }
     mSlots[slot].mFence = Fence::NO_FENCE;
     mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
+
+    if (mLastQueuedSlot == slot) {
+        mLastQueuedSlot = INVALID_BUFFER_SLOT;
+    }
 }
 
 void BufferQueueCore::freeAllBuffersLocked() {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index bb8d39b..0b7ce17 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -101,6 +101,10 @@
             return NO_INIT;
         }
 
+        if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) {
+            return NO_ERROR;
+        }
+
         // The new maxDequeuedBuffer count should not be violated by the number
         // of currently dequeued buffers
         int dequeuedCount = 0;
@@ -175,6 +179,10 @@
             return NO_INIT;
         }
 
+        if (async == mCore->mAsyncMode) {
+            return NO_ERROR;
+        }
+
         if ((mCore->mMaxAcquiredBufferCount + mCore->mMaxDequeuedBufferCount +
                 (async || mCore->mDequeueBufferCannotBlock ? 1 : 0)) >
                 mCore->mMaxBufferCount) {
@@ -199,7 +207,9 @@
         mCore->mAsyncMode = async;
         VALIDATE_CONSISTENCY();
         mCore->mDequeueCondition.broadcast();
-        listener = mCore->mConsumerListener;
+        if (delta < 0) {
+            listener = mCore->mConsumerListener;
+        }
     } // Autolock scope
 
     // Call back without lock held
@@ -834,30 +844,31 @@
             mCore->mQueue.push_back(item);
             frameAvailableListener = mCore->mConsumerListener;
         } else {
-            // When the queue is not empty, we need to look at the front buffer
-            // state to see if we need to replace it
-            BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
-            if (front->mIsDroppable) {
+            // When the queue is not empty, we need to look at the last buffer
+            // in the queue to see if we need to replace it
+            const BufferItem& last = mCore->mQueue.itemAt(
+                    mCore->mQueue.size() - 1);
+            if (last.mIsDroppable) {
 
-                if (!front->mIsStale) {
-                    mSlots[front->mSlot].mBufferState.freeQueued();
+                if (!last.mIsStale) {
+                    mSlots[last.mSlot].mBufferState.freeQueued();
 
                     // After leaving shared buffer mode, the shared buffer will
                     // still be around. Mark it as no longer shared if this
                     // operation causes it to be free.
                     if (!mCore->mSharedBufferMode &&
-                            mSlots[front->mSlot].mBufferState.isFree()) {
-                        mSlots[front->mSlot].mBufferState.mShared = false;
+                            mSlots[last.mSlot].mBufferState.isFree()) {
+                        mSlots[last.mSlot].mBufferState.mShared = false;
                     }
                     // Don't put the shared buffer on the free list.
-                    if (!mSlots[front->mSlot].mBufferState.isShared()) {
-                        mCore->mActiveBuffers.erase(front->mSlot);
-                        mCore->mFreeBuffers.push_back(front->mSlot);
+                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
+                        mCore->mActiveBuffers.erase(last.mSlot);
+                        mCore->mFreeBuffers.push_back(last.mSlot);
                     }
                 }
 
                 // Overwrite the droppable buffer with the incoming one
-                *front = item;
+                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                 frameReplacedListener = mCore->mConsumerListener;
             } else {
                 mCore->mQueue.push_back(item);
@@ -867,6 +878,7 @@
 
         mCore->mBufferHasBeenQueued = true;
         mCore->mDequeueCondition.broadcast();
+        mCore->mLastQueuedSlot = slot;
 
         output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
                 mCore->mTransformHint,
@@ -909,8 +921,8 @@
         // third. In the event that frames take varying time, this makes a
         // small trade-off in favor of latency rather than throughput.
         mLastQueueBufferFence->waitForever("Throttling EGL Production");
-        mLastQueueBufferFence = fence;
     }
+    mLastQueueBufferFence = fence;
 
     return NO_ERROR;
 }
@@ -1356,6 +1368,24 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence) {
+    ATRACE_CALL();
+    BQ_LOGV("getLastQueuedBuffer");
+
+    Mutex::Autolock lock(mCore->mMutex);
+    if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
+        *outBuffer = nullptr;
+        *outFence = Fence::NO_FENCE;
+        return NO_ERROR;
+    }
+
+    *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer;
+    *outFence = mLastQueueBufferFence;
+
+    return NO_ERROR;
+}
+
 void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
     // If we're here, it means that a producer we were connected to died.
     // We're guaranteed that we are still connected to it because we remove
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 7cdb8f4..c36fcad 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -54,6 +54,7 @@
     SET_SHARED_BUFFER_MODE,
     SET_AUTO_REFRESH,
     SET_DEQUEUE_TIMEOUT,
+    GET_LAST_QUEUED_BUFFER,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -379,6 +380,37 @@
         }
         return reply.readInt32();
     }
+
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER, data,
+                &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to transact: %d", result);
+            return result;
+        }
+        result = reply.readInt32();
+        if (result != NO_ERROR) {
+            return result;
+        }
+        sp<GraphicBuffer> buffer(new GraphicBuffer);
+        result = reply.read(*buffer);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+            return result;
+        }
+        sp<Fence> fence(new Fence);
+        result = reply.read(*fence);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
+            return result;
+        }
+        *outBuffer = buffer;
+        *outFence = fence;
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -590,6 +622,27 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case GET_LAST_QUEUED_BUFFER: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            sp<GraphicBuffer> buffer(nullptr);
+            sp<Fence> fence(Fence::NO_FENCE);
+            status_t result = getLastQueuedBuffer(&buffer, &fence);
+            reply->writeInt32(result);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply->write(*buffer);
+            if (result != NO_ERROR) {
+                ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
+                return result;
+            }
+            result = reply->write(*fence);
+            if (result != NO_ERROR) {
+                ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
+                return result;
+            }
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index b4cbf84..a8b4fa8 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -34,6 +34,7 @@
 
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/HdrCapabilities.h>
 
 #include <utils/Log.h>
 
@@ -282,6 +283,28 @@
         reply.read(*outStats);
         return reply.readInt32();
     }
+
+    virtual status_t getHdrCapabilities(const sp<IBinder>& display,
+            HdrCapabilities* outCapabilities) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getHdrCapabilities failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_HDR_CAPABILITIES,
+                data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getHdrCapabilities failed to transact: %d", result);
+            return result;
+        }
+        result = reply.readInt32();
+        if (result == NO_ERROR) {
+            result = reply.readParcelable(outCapabilities);
+        }
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -467,6 +490,23 @@
             setPowerMode(display, mode);
             return NO_ERROR;
         }
+        case GET_HDR_CAPABILITIES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getHdrCapabilities failed to readStrongBinder: %d",
+                        result);
+                return result;
+            }
+            HdrCapabilities capabilities;
+            result = getHdrCapabilities(display, &capabilities);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                reply->writeParcelable(capabilities);
+            }
+            return NO_ERROR;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e43342e..d1c576e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,6 +41,7 @@
     output.write(finalCrop);
     output.writeStrongBinder(handle);
     output.writeUint64(frameNumber);
+    output.writeInt32(overrideScalingMode);
     output.write(transparentRegion);
     return NO_ERROR;
 }
@@ -68,6 +69,7 @@
     input.read(finalCrop);
     handle = input.readStrongBinder();
     frameNumber = input.readUint64();
+    overrideScalingMode = input.readInt32();
     input.read(transparentRegion);
     return NO_ERROR;
 }
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index 0b2b942..0340d6b 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -35,8 +35,8 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-Sensor::Sensor()
-    : mHandle(0), mType(0),
+Sensor::Sensor(const char * name)
+    : mName(name), mHandle(0), mType(0),
       mMinValue(0), mMaxValue(0), mResolution(0),
       mPower(0), mMinDelay(0), mVersion(0), mFifoReservedEventCount(0),
       mFifoMaxEventCount(0), mRequiredAppOp(0),
@@ -272,6 +272,11 @@
         break;
     }
 
+    // Set DYNAMIC_SENSOR_MASK and ADDITIONAL_INFO_MASK flag here. Compatible with HAL 1_3.
+    if (halVersion >= SENSORS_DEVICE_API_VERSION_1_3) {
+        mFlags |= (hwSensor->flags & (DYNAMIC_SENSOR_MASK | ADDITIONAL_INFO_MASK));
+    }
+
     // Set DATA_INJECTION flag here. Defined in HAL 1_4.
     if (halVersion >= SENSORS_DEVICE_API_VERSION_1_4) {
         mFlags |= (hwSensor->flags & DATA_INJECTION_MASK);
@@ -390,6 +395,10 @@
     return mFlags & SENSOR_FLAG_WAKE_UP;
 }
 
+bool Sensor::isDynamicSensor() const {
+    return mFlags & SENSOR_FLAG_DYNAMIC_SENSOR;
+}
+
 int32_t Sensor::getReportingMode() const {
     return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
 }
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 76b62f1..b304633 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -127,6 +127,11 @@
     return mGraphicBufferProducer->setDequeueTimeout(timeout);
 }
 
+status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence) {
+    return mGraphicBufferProducer->getLastQueuedBuffer(outBuffer, outFence);
+}
+
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
     Surface* c = getSelf(window);
     return c->setSwapInterval(interval);
@@ -480,6 +485,8 @@
         mSharedBufferHasBeenQueued = true;
     }
 
+    mQueueBufferCondition.broadcast();
+
     return err;
 }
 
@@ -1259,6 +1266,15 @@
     return err;
 }
 
+bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
+    Mutex::Autolock lock(mMutex);
+    uint64_t currentFrame = mGraphicBufferProducer->getNextFrameNumber();
+    if (currentFrame > lastFrame) {
+      return true;
+    }
+    return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
+}
+
 namespace view {
 
 status_t Surface::writeToParcel(Parcel* parcel) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 04b5446..e33cc37 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -163,6 +163,8 @@
     status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, const sp<IBinder>& handle,
             uint64_t frameNumber);
+    status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id, int32_t overrideScalingMode);
 
     void setDisplaySurface(const sp<IBinder>& token,
             const sp<IGraphicBufferProducer>& bufferProducer);
@@ -414,6 +416,33 @@
     return NO_ERROR;
 }
 
+status_t Composer::setOverrideScalingMode(
+        const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id, int32_t overrideScalingMode) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+
+    switch (overrideScalingMode) {
+        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+        case -1:
+            break;
+        default:
+            ALOGE("unknown scaling mode: %d",
+                    overrideScalingMode);
+            return BAD_VALUE;
+    }
+
+    s->what |= layer_state_t::eOverrideScalingModeChanged;
+    s->overrideScalingMode = overrideScalingMode;
+    return NO_ERROR;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) {
@@ -650,6 +679,12 @@
     return getComposer().deferTransactionUntil(this, id, handle, frameNumber);
 }
 
+status_t SurfaceComposerClient::setOverrideScalingMode(
+        const sp<IBinder>& id, int32_t overrideScalingMode) {
+    return getComposer().setOverrideScalingMode(
+            this, id, overrideScalingMode);
+}
+
 // ----------------------------------------------------------------------------
 
 void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
@@ -722,6 +757,12 @@
     return ComposerService::getComposerService()->getAnimationFrameStats(outStats);
 }
 
+status_t SurfaceComposerClient::getHdrCapabilities(const sp<IBinder>& display,
+        HdrCapabilities* outCapabilities) {
+    return ComposerService::getComposerService()->getHdrCapabilities(display,
+            outCapabilities);
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::capture(
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 184de71..314d83a 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -165,6 +165,12 @@
     return mClient->deferTransactionUntil(mHandle, handle, frameNumber);
 }
 
+status_t SurfaceControl::setOverrideScalingMode(int32_t overrideScalingMode) {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->setOverrideScalingMode(mHandle, overrideScalingMode);
+}
+
 status_t SurfaceControl::clearLayerFrameStats() const {
     status_t err = validate();
     if (err < 0) return err;
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 82df9a9..9876d94 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -796,4 +796,56 @@
     ASSERT_EQ(OK, mProducer->attachBuffer(&slot, buffer));
 }
 
+TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, false, &output));
+
+    // Dequeue and queue the first buffer, storing the handle
+    int slot = BufferQueue::INVALID_BUFFER_SLOT;
+    sp<Fence> fence;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    sp<GraphicBuffer> firstBuffer;
+    ASSERT_EQ(OK, mProducer->requestBuffer(slot, &firstBuffer));
+
+    IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+        HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+    // Dequeue a second buffer
+    slot = BufferQueue::INVALID_BUFFER_SLOT;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    sp<GraphicBuffer> secondBuffer;
+    ASSERT_EQ(OK, mProducer->requestBuffer(slot, &secondBuffer));
+
+    // Ensure it's a new buffer
+    ASSERT_NE(firstBuffer->getNativeBuffer()->handle,
+            secondBuffer->getNativeBuffer()->handle);
+
+    // Queue the second buffer
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+    // Acquire and release both buffers
+    for (size_t i = 0; i < 2; ++i) {
+        BufferItem item;
+        ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+        ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+    }
+
+    // Make sure we got the second buffer back
+    sp<GraphicBuffer> returnedBuffer;
+    sp<Fence> returnedFence;
+    ASSERT_EQ(OK,
+            mProducer->getLastQueuedBuffer(&returnedBuffer, &returnedFence));
+    ASSERT_EQ(secondBuffer->getNativeBuffer()->handle,
+            returnedBuffer->getNativeBuffer()->handle);
+}
+
 } // namespace android
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index e4cdcab..ee6c093 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -40,12 +40,14 @@
 	GraphicBuffer.cpp \
 	GraphicBufferAllocator.cpp \
 	GraphicBufferMapper.cpp \
+	HdrCapabilities.cpp \
 	PixelFormat.cpp \
 	Rect.cpp \
 	Region.cpp \
 	UiConfig.cpp
 
 LOCAL_SHARED_LIBRARIES := \
+	libbinder \
 	libcutils \
 	libhardware \
 	libsync \
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp
new file mode 100644
index 0000000..511f68a
--- /dev/null
+++ b/libs/ui/HdrCapabilities.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 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 <ui/HdrCapabilities.h>
+
+#include <binder/Parcel.h>
+
+namespace android {
+
+status_t HdrCapabilities::writeToParcel(Parcel* parcel) const
+{
+    status_t result = parcel->writeInt32Vector(mSupportedHdrTypes);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->writeFloat(mMaxLuminance);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->writeFloat(mMaxAverageLuminance);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->writeFloat(mMinLuminance);
+    return result;
+}
+
+status_t HdrCapabilities::readFromParcel(const Parcel* parcel)
+{
+    status_t result = parcel->readInt32Vector(&mSupportedHdrTypes);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->readFloat(&mMaxLuminance);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->readFloat(&mMaxAverageLuminance);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->readFloat(&mMinLuminance);
+    return result;
+}
+
+} // namespace android
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 5d838e6..bef5f02 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -602,8 +602,8 @@
 #define EGL_ANDROID_create_native_client_buffer 1
 #define EGL_NATIVE_BUFFER_USAGE_ANDROID   0x3143
 #define EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID   0x00000001
-#define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_ANDROID   0x00000002
-#define EGL_NATIVE_BUFFER_USAGE_TEXTURE_ANDROID   0x00000004
+#define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID   0x00000002
+#define EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID   0x00000004
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLClientBuffer eglCreateNativeClientBufferANDROID (const EGLint *attrib_list);
 #else
@@ -616,6 +616,10 @@
 #define EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID 0x314C
 #endif
 
+#ifndef EGL_KHR_mutable_render_buffer
+#define EGL_KHR_mutable_render_buffer 1
+#define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index e7703d8..217c821 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -115,6 +115,8 @@
         "EGL_KHR_partial_update "               // strongly recommended
         "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
         "EGL_KHR_create_context_no_error "
+        "EGL_KHR_mutable_render_buffer "
+        "EGL_EXT_yuv_surface "
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -1814,21 +1816,17 @@
                 case EGL_NATIVE_BUFFER_USAGE_ANDROID:
                     if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_PROTECTED;
-                        // If we are using QCOM then add in extra bits.  This
-                        // should be removed before launch. These correspond to:
-                        // USAGE_PRIVATE_MM_HEAP | USAGE_PRIVATE_UNCACHED
-                        usage |= 0x82000000;
                     }
-                    if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_ANDROID) {
+                    if (value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_HW_RENDER;
                     }
-                    if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_ANDROID) {
+                    if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_HW_TEXTURE;
                     }
                     // The buffer must be used for either a texture or a
                     // renderbuffer.
-                    if ((value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_ANDROID) &&
-                        (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_ANDROID)) {
+                    if ((value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) &&
+                        (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID)) {
                         return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
                     }
                     break;
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 6a9d7b6..e335a6c 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -44,9 +44,10 @@
 
 static bool findExtension(const char* exts, const char* name, size_t nameLen) {
     if (exts) {
-        const char* match = strstr(exts, name);
-        if (match && (match[nameLen] == '\0' || match[nameLen] == ' ')) {
-            return true;
+        for (const char* match = strstr(exts, name); match; match = strstr(match + nameLen, name)) {
+            if (match[nameLen] == '\0' || match[nameLen] == ' ') {
+                return true;
+            }
         }
     }
     return false;
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
new file mode 100644
index 0000000..a6fae80
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -0,0 +1,197 @@
+Name
+
+    ANDROID_create_native_client_buffer
+
+Name Strings
+
+    EGL_ANDROID_create_native_client_buffer
+
+Contributors
+
+    Craig Donner
+
+Contact
+
+    Craig Donner, Google Inc. (cdonner 'at' google.com)
+
+Status
+
+    Draft
+
+Version
+
+    Version 1, January 19, 2016
+
+Number
+
+    EGL Extension #XXX
+
+Dependencies
+
+    Requires EGL 1.2.
+
+    EGL_ANDROID_image_native_buffer and EGL_KHR_image_base are required.
+
+    This extension is written against the wording of the EGL 1.2
+    Specification as modified by EGL_KHR_image_base and
+    EGL_ANDROID_image_native_buffer.
+
+Overview
+
+    This extension allows creating an EGLClientBuffer backed by an Android
+    window buffer (struct ANativeWindowBuffer) which can be later used to
+    create an EGLImage.
+
+New Types
+
+    None.
+
+New Procedures and Functions
+
+EGLClientBuffer eglCreateNativeClientBufferANDROID(
+                        const EGLint *attrib_list)
+
+New Tokens
+
+    EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
+    EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
+    EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
+    EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID 0x00000004
+
+Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
+
+    Add the following to section 2.5.1 "EGLImage Specification" (as modified by
+    the EGL_KHR_image_base and EGL_ANDROID_image_native_buffer specifications),
+    below the description of eglCreateImageKHR:
+
+   "The command
+
+        EGLClientBuffer eglCreateNativeClientBufferANDROID(
+                                const EGLint *attrib_list)
+
+    may be used to create an EGLClientBuffer backed by an ANativeWindowBuffer
+    struct. EGL implementations must guarantee that the lifetime of the
+    returned EGLClientBuffer is at least as long as the EGLImage(s) it is bound
+    to, following the lifetime semantics described below in section 2.5.2; the
+    EGLClientBuffer must be destroyed no earlier than when all of its associated
+    EGLImages are destroyed by eglDestroyImageKHR. <attrib_list> is a list of
+    attribute-value pairs which is used to specify the dimensions, format, and
+    usage of the underlying buffer structure. If <attrib_list> is non-NULL, the
+    last attribute specified in the list must be EGL_NONE.
+
+    Attribute names accepted in <attrib_list> are shown in Table aaa,
+    together with the <target> for which each attribute name is valid, and
+    the default value used for each attribute if it is not included in
+    <attrib_list>.
+
+      +---------------------------------+----------------------+---------------+
+      | Attribute                       | Description          | Default Value |
+      |                                 |                      |               |
+      +---------------------------------+----------------------+---------------+
+      | EGL_NONE                        | Marks the end of the | N/A           |
+      |                                 | attribute-value list |               |
+      | EGL_WIDTH                       | The width of the     | 0             |
+      |                                 | buffer data          |               |
+      | EGL_HEIGHT                      | The height of the    | 0             |
+      |                                 | buffer data          |               |
+      | EGL_RED_SIZE                    | The bits of Red in   | 0             |
+      |                                 | the color buffer     |               |
+      | EGL_GREEN_SIZE                  | The bits of Green in | 0             |
+      |                                 | the color buffer     |               |
+      | EGL_BLUE_SIZE                   | The bits of Blue in  | 0             |
+      |                                 | the color buffer     |               |
+      | EGL_ALPHA_SIZE                  | The bits of Alpha in | 0             |
+      |                                 | the color buffer     |               |
+      |                                 | buffer data          |               |
+      | EGL_NATIVE_BUFFER_USAGE_ANDROID | The usage bits of    | 0             |
+      |                                 | the buffer data      |               |
+      +---------------------------------+----------------------+---------------+
+       Table aaa.  Legal attributes for eglCreateNativeClientBufferANDROID
+       <attrib_list> parameter.
+
+    The maximum width and height may depend on the amount of available memory,
+    which may also depend on the format and usage flags. The values of
+    EGL_RED_SIZE, EGL_GREEN_SIZE, and EGL_BLUE_SIZE must be non-zero and
+    correspond to a valid pixel format for the implementation. If EGL_ALPHA_SIZE
+    is non-zero then the combination of all four sizes must correspond to a
+    valid pixel format for the implementation. The
+    EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of the following bits:
+
+        EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID: Indicates that the
+        created buffer must have a hardware-protected path to external display
+        sink. If a hardware-protected path is not available, then either don't
+        composite only this buffer (preferred) to the external sink, or (less
+        desirable) do not route the entire composition to the external sink.
+
+        EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID: The buffer will be
+        used to create a renderbuffer. This flag must not be set if
+        EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID is set.
+
+        EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID: The buffer will be used to
+        create a texture. This flag must not be set if
+        EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID is set.
+
+    Errors
+
+        If eglCreateNativeClientBufferANDROID fails, NULL will be returned, no
+        memory will be allocated, and one of the following errors will be
+        generated:
+
+       * If the value of EGL_WIDTH or EGL_HEIGHT is not positive, the error
+         EGL_BAD_PARAMETER is generated.
+
+       * If the combination of the values of EGL_RED_SIZE, EGL_GREEN_SIZE,
+         EGL_BLUE_SIZE, and EGL_ALPHA_SIZE is not a valid pixel format for the
+         EGL implementation, the error EGL_BAD_PARAMETER is generated.
+
+       * If the value of EGL_NATIVE_BUFFER_ANDROID is not a valid combination
+         of gralloc usage flags for the EGL implementation, or is incompatible
+         with the value of EGL_FORMAT, the error EGL_BAD_PARAMETER is
+         Generated.
+
+       * If both the EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID and
+         EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID are set in the value of
+         EGL_NATIVE_BUFFER_USAGE_ANDROID, the error EGL_BAD_PARAMETER is
+         Generated."
+
+Issues
+
+    1. Should this extension define what combinations of formats and usage flags
+    EGL implementations are required to support?
+
+    RESOLVED: Partially.
+
+    The set of valid color combinations is implementation-specific and may
+    depend on additional EGL extensions, but generally RGB565 and RGBA888 should
+    be supported. The particular valid combinations for a given Android version
+    and implementation should be documented by that version.
+
+    2. Should there be an eglDestroyNativeClientBufferANDROID to destroy the
+    client buffers created by this extension?
+
+    RESOLVED: No.
+
+    A destroy function would add several complications:
+
+        a) ANativeWindowBuffer is a reference counted object, may be used
+           outside of EGL.
+        b) The same buffer may back multiple EGLImages, though this usage may
+           result in undefined behavior.
+        c) The interactions between the lifetimes of EGLImages and their
+           EGLClientBuffers would become needlessly complex.
+
+    Because ANativeWindowBuffer is a reference counted object, implementations
+    of this extension should ensure the buffer has a lifetime at least as long
+    as a generated EGLImage (via EGL_ANDROID_image_native_buffer). The simplest
+    method is to increment the reference count of the buffer in
+    eglCreateImagKHR, and then decrement it in eglDestroyImageKHR. This should
+    ensure proper lifetime semantics.
+
+Revision History
+
+#2 (Craig Donner, April 15, 2016)
+    - Set color formats and usage bits explicitly using additional attributes,
+    and add value for new token EGL_NATIVE_BUFFER_USAGE_ANDROID.
+
+#1 (Craig Donner, January 19, 2016)
+    - Initial draft.
diff --git a/services/batteryservice/BatteryProperties.cpp b/services/batteryservice/BatteryProperties.cpp
index 07cc797..d89d4c9 100644
--- a/services/batteryservice/BatteryProperties.cpp
+++ b/services/batteryservice/BatteryProperties.cpp
@@ -41,6 +41,7 @@
     batteryLevel = p->readInt32();
     batteryVoltage = p->readInt32();
     batteryTemperature = p->readInt32();
+    batteryChargeCounter = p->readInt32();
     batteryTechnology = String8((p->readString16()).string());
     return OK;
 }
@@ -57,6 +58,7 @@
     p->writeInt32(batteryLevel);
     p->writeInt32(batteryVoltage);
     p->writeInt32(batteryTemperature);
+    p->writeInt32(batteryChargeCounter);
     p->writeString16(String16(batteryTechnology));
     return OK;
 }
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index eed14ab..3f69d49 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -1227,6 +1227,8 @@
                     int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
                     if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
                         outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+                    } else if (isWindowObscuredLocked(windowHandle)) {
+                        outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
                     }
 
                     mTempTouchState.addOrUpdateWindow(
@@ -1264,6 +1266,8 @@
         }
         if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
             targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+        } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
+            targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
         }
 
         // Update hover state.
@@ -1439,6 +1443,7 @@
                                 == InputWindowInfo::TYPE_WALLPAPER) {
                     mTempTouchState.addOrUpdateWindow(windowHandle,
                             InputTarget::FLAG_WINDOW_IS_OBSCURED
+                                    | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED
                                     | InputTarget::FLAG_DISPATCH_AS_IS,
                             BitSet32(0));
                 }
@@ -1633,6 +1638,27 @@
     return false;
 }
 
+
+bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
+    int32_t displayId = windowHandle->getInfo()->displayId;
+    const InputWindowInfo* windowInfo = windowHandle->getInfo();
+    size_t numWindows = mWindowHandles.size();
+    for (size_t i = 0; i < numWindows; i++) {
+        sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
+        if (otherHandle == windowHandle) {
+            break;
+        }
+
+        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        if (otherInfo->displayId == displayId
+                && otherInfo->visible && !otherInfo->isTrustedOverlay()
+                && otherInfo->overlaps(windowInfo)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
         const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
         const char* targetType) {
@@ -1907,6 +1933,9 @@
         if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
             dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
         }
+        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {
+            dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+        }
 
         if (!connection->inputState.trackMotion(motionEntry,
                 dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 98355c6..1c054f5 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -89,7 +89,7 @@
         /* This flag indicates that the event is being delivered to a foreground application. */
         FLAG_FOREGROUND = 1 << 0,
 
-        /* This flag indicates that the target of a MotionEvent is partly or wholly
+        /* This flag indicates that the MotionEvent falls within the area of the target
          * obscured by another visible window above it.  The motion event should be
          * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */
         FLAG_WINDOW_IS_OBSCURED = 1 << 1,
@@ -139,6 +139,12 @@
                 | FLAG_DISPATCH_AS_HOVER_EXIT
                 | FLAG_DISPATCH_AS_SLIPPERY_EXIT
                 | FLAG_DISPATCH_AS_SLIPPERY_ENTER,
+
+        /* This flag indicates that the target of a MotionEvent is partly or wholly
+         * obscured by another visible window above it.  The motion event should be
+         * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */
+        FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14,
+
     };
 
     // The input channel to be targeted.
@@ -1048,6 +1054,7 @@
             const InjectionState* injectionState);
     bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
             int32_t x, int32_t y) const;
+    bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const;
     String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
             const sp<InputWindowHandle>& windowHandle);
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 8556c23..374a5de 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -697,6 +697,21 @@
     return result;
 }
 
+void InputReader::toggleCapsLockState(int32_t deviceId) {
+    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+    if (deviceIndex < 0) {
+        ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
+        return;
+    }
+
+    InputDevice* device = mDevices.valueAt(deviceIndex);
+    if (device->isIgnored()) {
+        return;
+    }
+
+    device->updateMetaState(AKEYCODE_CAPS_LOCK);
+}
+
 bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
         size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
     AutoMutex _l(mLock);
@@ -1172,6 +1187,13 @@
     return result;
 }
 
+void InputDevice::updateMetaState(int32_t keyCode) {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        mMappers[i]->updateMetaState(keyCode);
+    }
+}
+
 void InputDevice::fadePointer() {
     size_t numMappers = mMappers.size();
     for (size_t i = 0; i < numMappers; i++) {
@@ -1880,6 +1902,9 @@
     return 0;
 }
 
+void InputMapper::updateMetaState(int32_t keyCode) {
+}
+
 void InputMapper::updateExternalStylusState(const StylusState& state) {
 
 }
@@ -2265,18 +2290,12 @@
         }
     }
 
-    int32_t oldMetaState = mMetaState;
-    int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);
-    bool metaStateChanged = oldMetaState != newMetaState;
-    if (metaStateChanged) {
-        mMetaState = newMetaState;
-        updateLedState(false);
-
+    if (updateMetaStateIfNeeded(keyCode, down)) {
         // If global meta state changed send it along with the key.
         // If it has not changed then we'll use what keymap gave us,
         // since key replacement logic might temporarily reset a few
         // meta bits for given key.
-        keyMetaState = newMetaState;
+        keyMetaState = mMetaState;
     }
 
     nsecs_t downTime = mDownTime;
@@ -2294,10 +2313,6 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    if (metaStateChanged) {
-        getContext()->updateGlobalMetaState();
-    }
-
     if (down && !isMetaKey(keyCode)) {
         getContext()->fadePointer();
     }
@@ -2335,6 +2350,24 @@
     return mMetaState;
 }
 
+void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+    updateMetaStateIfNeeded(keyCode, false);
+}
+
+bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
+    int32_t oldMetaState = mMetaState;
+    int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
+    bool metaStateChanged = oldMetaState != newMetaState;
+    if (metaStateChanged) {
+        mMetaState = newMetaState;
+        updateLedState(false);
+
+        getContext()->updateGlobalMetaState();
+    }
+
+    return metaStateChanged;
+}
+
 void KeyboardInputMapper::resetLedState() {
     initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK);
     initializeLedState(mNumLockLedState, ALED_NUM_LOCK);
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index e554e57..076f3d6 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -359,6 +359,9 @@
     virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
             int32_t sw) = 0;
 
+    /* Toggle Caps Lock */
+    virtual void toggleCapsLockState(int32_t deviceId) = 0;
+
     /* Determine whether physical keys exist for the given framework-domain key codes. */
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
             size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0;
@@ -461,6 +464,8 @@
     virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
             int32_t sw);
 
+    virtual void toggleCapsLockState(int32_t deviceId);
+
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
             size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
 
@@ -616,6 +621,7 @@
     void cancelTouch(nsecs_t when);
 
     int32_t getMetaState();
+    void updateMetaState(int32_t keyCode);
 
     void fadePointer();
 
@@ -1031,6 +1037,7 @@
     virtual void cancelTouch(nsecs_t when);
 
     virtual int32_t getMetaState();
+    virtual void updateMetaState(int32_t keyCode);
 
     virtual void updateExternalStylusState(const StylusState& state);
 
@@ -1116,6 +1123,7 @@
             const int32_t* keyCodes, uint8_t* outFlags);
 
     virtual int32_t getMetaState();
+    virtual void updateMetaState(int32_t keyCode);
 
 private:
     struct KeyDown {
@@ -1156,6 +1164,8 @@
 
     void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
 
+    bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
+
     ssize_t findKeyDown(int32_t scanCode);
 
     void resetLedState();
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index fda3ffa..1b913c5 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -36,14 +36,16 @@
 }
 
 bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
-    return x >= frameLeft && x <= frameRight
-            && y >= frameTop && y <= frameBottom;
+    return x >= frameLeft && x < frameRight
+            && y >= frameTop && y < frameBottom;
 }
 
 bool InputWindowInfo::isTrustedOverlay() const {
     return layoutParamsType == TYPE_INPUT_METHOD
             || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
             || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
+            || layoutParamsType == TYPE_STATUS_BAR
+            || layoutParamsType == TYPE_NAVIGATION_BAR
             || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
 }
 
@@ -51,6 +53,11 @@
     return layoutParamsFlags & FLAG_SPLIT_TOUCH;
 }
 
+bool InputWindowInfo::overlaps(const InputWindowInfo* other) const {
+    return frameLeft < other->frameRight && frameRight > other->frameLeft
+            && frameTop < other->frameBottom && frameBottom > other->frameTop;
+}
+
 
 // --- InputWindowHandle ---
 
diff --git a/services/inputflinger/InputWindow.h b/services/inputflinger/InputWindow.h
index 42457ce..0ac7fce 100644
--- a/services/inputflinger/InputWindow.h
+++ b/services/inputflinger/InputWindow.h
@@ -146,6 +146,8 @@
     bool isTrustedOverlay() const;
 
     bool supportsSplitTouch() const;
+
+    bool overlaps(const InputWindowInfo* other) const;
 };
 
 
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 57c56a6..85e96d6 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -16,6 +16,7 @@
     SensorEventConnection.cpp \
     MostRecentEventLogger.cpp \
     SensorRecord.cpp \
+    SensorList.cpp \
 
 
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
diff --git a/services/sensorservice/CorrectedGyroSensor.cpp b/services/sensorservice/CorrectedGyroSensor.cpp
index 7b1f346..005af18 100644
--- a/services/sensorservice/CorrectedGyroSensor.cpp
+++ b/services/sensorservice/CorrectedGyroSensor.cpp
@@ -30,15 +30,26 @@
 // ---------------------------------------------------------------------------
 
 CorrectedGyroSensor::CorrectedGyroSensor(sensor_t const* list, size_t count)
-    : mSensorDevice(SensorDevice::getInstance()),
-      mSensorFusion(SensorFusion::getInstance())
-{
+    : VirtualSensor() {
     for (size_t i=0 ; i<count ; i++) {
         if (list[i].type == SENSOR_TYPE_GYROSCOPE) {
             mGyro = Sensor(list + i);
             break;
         }
     }
+
+    const sensor_t sensor = {
+            .name       = "Corrected Gyroscope Sensor",
+            .vendor     = "AOSP",
+            .version    = 1,
+            .handle     = '_cgy',
+            .type       = SENSOR_TYPE_GYROSCOPE,
+            .maxRange   = mGyro.getMaxValue(),
+            .resolution = mGyro.getResolution(),
+            .power      = mSensorFusion.getPowerUsage(),
+            .minDelay   = mGyro.getMinDelay(),
+    };
+    mSensor = Sensor(&sensor);
 }
 
 bool CorrectedGyroSensor::process(sensors_event_t* outEvent,
@@ -66,21 +77,6 @@
     return mSensorFusion.setDelay(FUSION_9AXIS, ident, ns);
 }
 
-Sensor CorrectedGyroSensor::getSensor() const {
-    sensor_t hwSensor;
-    hwSensor.name       = "Corrected Gyroscope Sensor";
-    hwSensor.vendor     = "AOSP";
-    hwSensor.version    = 1;
-    hwSensor.handle     = '_cgy';
-    hwSensor.type       = SENSOR_TYPE_GYROSCOPE;
-    hwSensor.maxRange   = mGyro.getMaxValue();
-    hwSensor.resolution = mGyro.getResolution();
-    hwSensor.power      = mSensorFusion.getPowerUsage();
-    hwSensor.minDelay   = mGyro.getMinDelay();
-    Sensor sensor(&hwSensor);
-    return sensor;
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/CorrectedGyroSensor.h b/services/sensorservice/CorrectedGyroSensor.h
index 3c49c08..68acd43 100644
--- a/services/sensorservice/CorrectedGyroSensor.h
+++ b/services/sensorservice/CorrectedGyroSensor.h
@@ -31,19 +31,14 @@
 class SensorDevice;
 class SensorFusion;
 
-class CorrectedGyroSensor : public SensorInterface {
-    SensorDevice& mSensorDevice;
-    SensorFusion& mSensorFusion;
+class CorrectedGyroSensor : public VirtualSensor {
     Sensor mGyro;
 
 public:
     CorrectedGyroSensor(sensor_t const* list, size_t count);
-    virtual bool process(sensors_event_t* outEvent,
-            const sensors_event_t& event);
-    virtual status_t activate(void* ident, bool enabled);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return true; }
+    virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override;
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/GravitySensor.cpp b/services/sensorservice/GravitySensor.cpp
index a165a5b..9d8add1 100644
--- a/services/sensorservice/GravitySensor.cpp
+++ b/services/sensorservice/GravitySensor.cpp
@@ -29,16 +29,26 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-GravitySensor::GravitySensor(sensor_t const* list, size_t count)
-    : mSensorDevice(SensorDevice::getInstance()),
-      mSensorFusion(SensorFusion::getInstance())
-{
+GravitySensor::GravitySensor(sensor_t const* list, size_t count) {
     for (size_t i=0 ; i<count ; i++) {
         if (list[i].type == SENSOR_TYPE_ACCELEROMETER) {
             mAccelerometer = Sensor(list + i);
             break;
         }
     }
+
+    const sensor_t sensor = {
+        .name       = "Gravity Sensor",
+        .vendor     = "AOSP",
+        .version    = 3,
+        .handle     = '_grv',
+        .type       = SENSOR_TYPE_GRAVITY,
+        .maxRange   = GRAVITY_EARTH * 2,
+        .resolution = mAccelerometer.getResolution(),
+        .power      = mSensorFusion.getPowerUsage(),
+        .minDelay   = mSensorFusion.getMinDelay(),
+    };
+    mSensor = Sensor(&sensor);
 }
 
 bool GravitySensor::process(sensors_event_t* outEvent,
@@ -73,21 +83,6 @@
     return mSensorFusion.setDelay(FUSION_NOMAG, ident, ns);
 }
 
-Sensor GravitySensor::getSensor() const {
-    sensor_t hwSensor;
-    hwSensor.name       = "Gravity Sensor";
-    hwSensor.vendor     = "AOSP";
-    hwSensor.version    = 3;
-    hwSensor.handle     = '_grv';
-    hwSensor.type       = SENSOR_TYPE_GRAVITY;
-    hwSensor.maxRange   = GRAVITY_EARTH * 2;
-    hwSensor.resolution = mAccelerometer.getResolution();
-    hwSensor.power      = mSensorFusion.getPowerUsage();
-    hwSensor.minDelay   = mSensorFusion.getMinDelay();
-    Sensor sensor(&hwSensor);
-    return sensor;
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/GravitySensor.h b/services/sensorservice/GravitySensor.h
index ac177c4..8e33a73 100644
--- a/services/sensorservice/GravitySensor.h
+++ b/services/sensorservice/GravitySensor.h
@@ -31,19 +31,14 @@
 class SensorDevice;
 class SensorFusion;
 
-class GravitySensor : public SensorInterface {
-    SensorDevice& mSensorDevice;
-    SensorFusion& mSensorFusion;
+class GravitySensor : public VirtualSensor {
     Sensor mAccelerometer;
 
 public:
     GravitySensor(sensor_t const* list, size_t count);
-    virtual bool process(sensors_event_t* outEvent,
-            const sensors_event_t& event);
-    virtual status_t activate(void* ident, bool enabled);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return true; }
+    virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override;
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/LinearAccelerationSensor.cpp b/services/sensorservice/LinearAccelerationSensor.cpp
index d5f20d2..d1cd732 100644
--- a/services/sensorservice/LinearAccelerationSensor.cpp
+++ b/services/sensorservice/LinearAccelerationSensor.cpp
@@ -29,10 +29,21 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-LinearAccelerationSensor::LinearAccelerationSensor(sensor_t const* list, size_t count)
-    : mSensorDevice(SensorDevice::getInstance()),
-      mGravitySensor(list, count)
-{
+LinearAccelerationSensor::LinearAccelerationSensor(sensor_t const* list, size_t count) :
+        mGravitySensor(list, count) {
+    const Sensor &gsensor = mGravitySensor.getSensor();
+    const sensor_t sensor = {
+        .name       = "Linear Acceleration Sensor",
+        .vendor     = "AOSP",
+        .version    = gsensor.getVersion(),
+        .handle     = '_lin',
+        .type       = SENSOR_TYPE_LINEAR_ACCELERATION,
+        .maxRange   = gsensor.getMaxValue(),
+        .resolution = gsensor.getResolution(),
+        .power      = gsensor.getPowerUsage(),
+        .minDelay   = gsensor.getMinDelay(),
+    };
+    mSensor = Sensor(&sensor);
 }
 
 bool LinearAccelerationSensor::process(sensors_event_t* outEvent,
@@ -58,22 +69,6 @@
     return mGravitySensor.setDelay(ident, handle, ns);
 }
 
-Sensor LinearAccelerationSensor::getSensor() const {
-    Sensor gsensor(mGravitySensor.getSensor());
-    sensor_t hwSensor;
-    hwSensor.name       = "Linear Acceleration Sensor";
-    hwSensor.vendor     = "AOSP";
-    hwSensor.version    = gsensor.getVersion();
-    hwSensor.handle     = '_lin';
-    hwSensor.type       = SENSOR_TYPE_LINEAR_ACCELERATION;
-    hwSensor.maxRange   = gsensor.getMaxValue();
-    hwSensor.resolution = gsensor.getResolution();
-    hwSensor.power      = gsensor.getPowerUsage();
-    hwSensor.minDelay   = gsensor.getMinDelay();
-    Sensor sensor(&hwSensor);
-    return sensor;
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/LinearAccelerationSensor.h b/services/sensorservice/LinearAccelerationSensor.h
index 5deb24f..428baa6 100644
--- a/services/sensorservice/LinearAccelerationSensor.h
+++ b/services/sensorservice/LinearAccelerationSensor.h
@@ -32,18 +32,15 @@
 class SensorDevice;
 class SensorFusion;
 
-class LinearAccelerationSensor : public SensorInterface {
-    SensorDevice& mSensorDevice;
+class LinearAccelerationSensor : public VirtualSensor {
     GravitySensor mGravitySensor;
 
     virtual bool process(sensors_event_t* outEvent,
             const sensors_event_t& event);
 public:
     LinearAccelerationSensor(sensor_t const* list, size_t count);
-    virtual status_t activate(void* ident, bool enabled);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return true; }
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/OrientationSensor.cpp b/services/sensorservice/OrientationSensor.cpp
index d55f336..ea5dbc9 100644
--- a/services/sensorservice/OrientationSensor.cpp
+++ b/services/sensorservice/OrientationSensor.cpp
@@ -29,13 +29,19 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-OrientationSensor::OrientationSensor()
-    : mSensorDevice(SensorDevice::getInstance()),
-      mSensorFusion(SensorFusion::getInstance())
-{
-    // FIXME: instead of using the SensorFusion code, we should use
-    // the SENSOR_TYPE_ROTATION_VECTOR instead. This way we could use the
-    // HAL's implementation.
+OrientationSensor::OrientationSensor() {
+    const sensor_t sensor = {
+        .name       = "Orientation Sensor",
+        .vendor     = "AOSP",
+        .version    = 1,
+        .handle     = '_ypr',
+        .type       = SENSOR_TYPE_ORIENTATION,
+        .maxRange   = 360.0f,
+        .resolution = 1.0f/256.0f, // FIXME: real value here
+        .power      = mSensorFusion.getPowerUsage(),
+        .minDelay   = mSensorFusion.getMinDelay(),
+    };
+    mSensor = Sensor(&sensor);
 }
 
 bool OrientationSensor::process(sensors_event_t* outEvent,
@@ -73,21 +79,6 @@
     return mSensorFusion.setDelay(FUSION_9AXIS, ident, ns);
 }
 
-Sensor OrientationSensor::getSensor() const {
-    sensor_t hwSensor;
-    hwSensor.name       = "Orientation Sensor";
-    hwSensor.vendor     = "AOSP";
-    hwSensor.version    = 1;
-    hwSensor.handle     = '_ypr';
-    hwSensor.type       = SENSOR_TYPE_ORIENTATION;
-    hwSensor.maxRange   = 360.0f;
-    hwSensor.resolution = 1.0f/256.0f; // FIXME: real value here
-    hwSensor.power      = mSensorFusion.getPowerUsage();
-    hwSensor.minDelay   = mSensorFusion.getMinDelay();
-    Sensor sensor(&hwSensor);
-    return sensor;
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/OrientationSensor.h b/services/sensorservice/OrientationSensor.h
index 855949d..30ff226 100644
--- a/services/sensorservice/OrientationSensor.h
+++ b/services/sensorservice/OrientationSensor.h
@@ -31,18 +31,12 @@
 class SensorDevice;
 class SensorFusion;
 
-class OrientationSensor : public SensorInterface {
-    SensorDevice& mSensorDevice;
-    SensorFusion& mSensorFusion;
-
+class OrientationSensor : public VirtualSensor {
 public:
     OrientationSensor();
-    virtual bool process(sensors_event_t* outEvent,
-            const sensors_event_t& event);
-    virtual status_t activate(void* ident, bool enabled);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return true; }
+    virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override;
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp
index 238845b..7b00f4d 100644
--- a/services/sensorservice/RotationVectorSensor.cpp
+++ b/services/sensorservice/RotationVectorSensor.cpp
@@ -27,11 +27,20 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-RotationVectorSensor::RotationVectorSensor(int mode)
-    : mSensorDevice(SensorDevice::getInstance()),
-      mSensorFusion(SensorFusion::getInstance()),
-      mMode(mode)
-{
+RotationVectorSensor::RotationVectorSensor(int mode) :
+      mMode(mode) {
+    const sensor_t sensor = {
+        .name       = getSensorName(),
+        .vendor     = "AOSP",
+        .version    = 3,
+        .handle     = getSensorToken(),
+        .type       = getSensorType(),
+        .maxRange   = 1,
+        .resolution = 1.0f / (1<<24),
+        .power      = mSensorFusion.getPowerUsage(),
+        .minDelay   = mSensorFusion.getMinDelay(),
+    };
+    mSensor = Sensor(&sensor);
 }
 
 bool RotationVectorSensor::process(sensors_event_t* outEvent,
@@ -61,21 +70,6 @@
     return mSensorFusion.setDelay(mMode, ident, ns);
 }
 
-Sensor RotationVectorSensor::getSensor() const {
-    sensor_t hwSensor;
-    hwSensor.name       = getSensorName();
-    hwSensor.vendor     = "AOSP";
-    hwSensor.version    = 3;
-    hwSensor.handle     = getSensorToken();
-    hwSensor.type       = getSensorType();
-    hwSensor.maxRange   = 1;
-    hwSensor.resolution = 1.0f / (1<<24);
-    hwSensor.power      = mSensorFusion.getPowerUsage();
-    hwSensor.minDelay   = mSensorFusion.getMinDelay();
-    Sensor sensor(&hwSensor);
-    return sensor;
-}
-
 int RotationVectorSensor::getSensorType() const {
     switch(mMode) {
         case FUSION_9AXIS:
@@ -120,10 +114,19 @@
 
 // ---------------------------------------------------------------------------
 
-GyroDriftSensor::GyroDriftSensor()
-    : mSensorDevice(SensorDevice::getInstance()),
-      mSensorFusion(SensorFusion::getInstance())
-{
+GyroDriftSensor::GyroDriftSensor() {
+    const sensor_t sensor = {
+        .name       = "Gyroscope Bias (debug)",
+        .vendor     = "AOSP",
+        .version    = 1,
+        .handle     = '_gbs',
+        .type       = SENSOR_TYPE_ACCELEROMETER,
+        .maxRange   = 1,
+        .resolution = 1.0f / (1<<24),
+        .power      = mSensorFusion.getPowerUsage(),
+        .minDelay   = mSensorFusion.getMinDelay(),
+    };
+    mSensor = Sensor(&sensor);
 }
 
 bool GyroDriftSensor::process(sensors_event_t* outEvent,
@@ -152,21 +155,6 @@
     return mSensorFusion.setDelay(FUSION_9AXIS, ident, ns);
 }
 
-Sensor GyroDriftSensor::getSensor() const {
-    sensor_t hwSensor;
-    hwSensor.name       = "Gyroscope Bias (debug)";
-    hwSensor.vendor     = "AOSP";
-    hwSensor.version    = 1;
-    hwSensor.handle     = '_gbs';
-    hwSensor.type       = SENSOR_TYPE_ACCELEROMETER;
-    hwSensor.maxRange   = 1;
-    hwSensor.resolution = 1.0f / (1<<24);
-    hwSensor.power      = mSensorFusion.getPowerUsage();
-    hwSensor.minDelay   = mSensorFusion.getMinDelay();
-    Sensor sensor(&hwSensor);
-    return sensor;
-}
-
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/RotationVectorSensor.h b/services/sensorservice/RotationVectorSensor.h
index 1fc316b..3cc2248 100644
--- a/services/sensorservice/RotationVectorSensor.h
+++ b/services/sensorservice/RotationVectorSensor.h
@@ -32,23 +32,18 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
-class RotationVectorSensor : public SensorInterface {
-    SensorDevice& mSensorDevice;
-    SensorFusion& mSensorFusion;
-    int mMode;
+class RotationVectorSensor : public VirtualSensor {
+public:
+    RotationVectorSensor(int mode = FUSION_9AXIS);
+    virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override;
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
 
+protected:
+    const int mMode;
     int getSensorType() const;
     const char* getSensorName() const ;
     int getSensorToken() const ;
-
-public:
-    RotationVectorSensor(int mode = FUSION_9AXIS);
-    virtual bool process(sensors_event_t* outEvent,
-            const sensors_event_t& event);
-    virtual status_t activate(void* ident, bool enabled);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return true; }
 };
 
 class GameRotationVectorSensor : public RotationVectorSensor {
@@ -61,18 +56,12 @@
     GeoMagRotationVectorSensor() : RotationVectorSensor(FUSION_NOGYRO) {}
 };
 
-class GyroDriftSensor : public SensorInterface {
-    SensorDevice& mSensorDevice;
-    SensorFusion& mSensorFusion;
-
+class GyroDriftSensor : public VirtualSensor {
 public:
     GyroDriftSensor();
-    virtual bool process(sensors_event_t* outEvent,
-            const sensors_event_t& event);
-    virtual status_t activate(void* ident, bool enabled);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return true; }
+    virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override;
+    virtual status_t activate(void* ident, bool enabled) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index ca26535..c1e1bad 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -21,6 +21,7 @@
 
 #include "vec.h"
 #include "SensorEventConnection.h"
+#include "SensorDevice.h"
 
 namespace android {
 
@@ -88,15 +89,14 @@
 
 bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
     Mutex::Autolock _l(mConnectionLock);
-    if (!canAccessSensor(mService->getSensorFromHandle(handle),
-            "Tried adding", mOpPackageName)) {
+    sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+    if (si == nullptr ||
+        !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
+        mSensorInfo.indexOfKey(handle) >= 0) {
         return false;
     }
-    if (mSensorInfo.indexOfKey(handle) < 0) {
-        mSensorInfo.add(handle, FlushInfo());
-        return true;
-    }
-    return false;
+    mSensorInfo.add(handle, FlushInfo());
+    return true;
 }
 
 bool SensorService::SensorEventConnection::removeSensor(int32_t handle) {
@@ -121,7 +121,8 @@
     Mutex::Autolock _l(mConnectionLock);
     for (size_t i = 0; i < mSensorInfo.size(); ++i) {
         const int handle = mSensorInfo.keyAt(i);
-        if (mService->getSensorFromHandle(handle).getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
+        sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+        if (si != nullptr && si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
             return true;
         }
     }
@@ -164,9 +165,9 @@
     if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
     for (size_t i = 0; i < mSensorInfo.size(); ++i) {
         const int handle = mSensorInfo.keyAt(i);
-        if (mService->getSensorFromHandle(handle).isWakeUpSensor()) {
+        sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+        if (si != nullptr && si->getSensor().isWakeUpSensor()) {
             looper_flags |= ALOOPER_EVENT_INPUT;
-            break;
         }
     }
 
@@ -385,11 +386,16 @@
     // Loop through all the sensors for this connection and check if there are any pending
     // flush complete events to be sent.
     for (size_t i = 0; i < mSensorInfo.size(); ++i) {
+        const int handle = mSensorInfo.keyAt(i);
+        sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+        if (si == nullptr) {
+            continue;
+        }
+
         FlushInfo& flushInfo = mSensorInfo.editValueAt(i);
         while (flushInfo.mPendingFlushEventsToSend > 0) {
-            const int sensor_handle = mSensorInfo.keyAt(i);
-            flushCompleteEvent.meta_data.sensor = sensor_handle;
-            bool wakeUpSensor = mService->getSensorFromHandle(sensor_handle).isWakeUpSensor();
+            flushCompleteEvent.meta_data.sensor = handle;
+            bool wakeUpSensor = si->getSensor().isWakeUpSensor();
             if (wakeUpSensor) {
                ++mWakeLockRefCount;
                flushCompleteEvent.flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
@@ -544,37 +550,41 @@
         unsigned char buf[sizeof(sensors_event_t)];
         ssize_t numBytesRead = ::recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
         {
-           Mutex::Autolock _l(mConnectionLock);
-           if (numBytesRead == sizeof(sensors_event_t)) {
-               if (!mDataInjectionMode) {
-                   ALOGE("Data injected in normal mode, dropping event"
-                         "package=%s uid=%d", mPackageName.string(), mUid);
-                   // Unregister call backs.
-                   return 0;
-               }
-               SensorDevice& dev(SensorDevice::getInstance());
-               sensors_event_t sensor_event;
-               memset(&sensor_event, 0, sizeof(sensor_event));
-               memcpy(&sensor_event, buf, sizeof(sensors_event_t));
-               Sensor sensor = mService->getSensorFromHandle(sensor_event.sensor);
-               sensor_event.type = sensor.getType();
-               dev.injectSensorData(&sensor_event);
+            Mutex::Autolock _l(mConnectionLock);
+            if (numBytesRead == sizeof(sensors_event_t)) {
+                if (!mDataInjectionMode) {
+                    ALOGE("Data injected in normal mode, dropping event"
+                          "package=%s uid=%d", mPackageName.string(), mUid);
+                    // Unregister call backs.
+                    return 0;
+                }
+                sensors_event_t sensor_event;
+                memcpy(&sensor_event, buf, sizeof(sensors_event_t));
+                sp<SensorInterface> si =
+                        mService->getSensorInterfaceFromHandle(sensor_event.sensor);
+                if (si == nullptr) {
+                    return 1;
+                }
+
+                SensorDevice& dev(SensorDevice::getInstance());
+                sensor_event.type = si->getSensor().getType();
+                dev.injectSensorData(&sensor_event);
 #if DEBUG_CONNECTIONS
-               ++mEventsReceived;
+                ++mEventsReceived;
 #endif
-           } else if (numBytesRead == sizeof(uint32_t)) {
-               uint32_t numAcks = 0;
-               memcpy(&numAcks, buf, numBytesRead);
-               // Sanity check to ensure  there are no read errors in recv, numAcks is always
-               // within the range and not zero. If any of the above don't hold reset
-               // mWakeLockRefCount to zero.
-               if (numAcks > 0 && numAcks < mWakeLockRefCount) {
-                   mWakeLockRefCount -= numAcks;
-               } else {
-                   mWakeLockRefCount = 0;
-               }
+            } else if (numBytesRead == sizeof(uint32_t)) {
+                uint32_t numAcks = 0;
+                memcpy(&numAcks, buf, numBytesRead);
+                // Sanity check to ensure  there are no read errors in recv, numAcks is always
+                // within the range and not zero. If any of the above don't hold reset
+                // mWakeLockRefCount to zero.
+                if (numAcks > 0 && numAcks < mWakeLockRefCount) {
+                    mWakeLockRefCount -= numAcks;
+                } else {
+                    mWakeLockRefCount = 0;
+                }
 #if DEBUG_CONNECTIONS
-               mTotalAcksReceived += numAcks;
+                mTotalAcksReceived += numAcks;
 #endif
            } else {
                // Read error, reset wakelock refcount.
@@ -601,7 +611,11 @@
     size_t fifoWakeUpSensors = 0;
     size_t fifoNonWakeUpSensors = 0;
     for (size_t i = 0; i < mSensorInfo.size(); ++i) {
-        const Sensor& sensor = mService->getSensorFromHandle(mSensorInfo.keyAt(i));
+        sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(mSensorInfo.keyAt(i));
+        if (si == nullptr) {
+            continue;
+        }
+        const Sensor& sensor = si->getSensor();
         if (sensor.getFifoReservedEventCount() == sensor.getFifoMaxEventCount()) {
             // Each sensor has a reserved fifo. Sum up the fifo sizes for all wake up sensors and
             // non wake_up sensors.
diff --git a/services/sensorservice/SensorInterface.cpp b/services/sensorservice/SensorInterface.cpp
index 970220b..cb24229 100644
--- a/services/sensorservice/SensorInterface.cpp
+++ b/services/sensorservice/SensorInterface.cpp
@@ -14,24 +14,30 @@
  * limitations under the License.
  */
 
+#include "SensorInterface.h"
+#include "SensorDevice.h"
+#include "SensorFusion.h"
+
 #include <stdint.h>
 #include <sys/types.h>
 
-#include "SensorInterface.h"
-
 namespace android {
 // ---------------------------------------------------------------------------
 
-SensorInterface::~SensorInterface()
-{
+namespace {
+const sensor_t DUMMY_SENSOR = {
+        .name = "", .vendor = "", .stringType = "", .requiredPermission = ""};
+} //unnamed namespace
+
+BaseSensor::BaseSensor(const sensor_t& sensor) :
+        mSensorDevice(SensorDevice::getInstance()),
+        mSensor(&sensor, mSensorDevice.getHalDeviceVersion()) {
 }
 
 // ---------------------------------------------------------------------------
 
-HardwareSensor::HardwareSensor(const sensor_t& sensor)
-    : mSensorDevice(SensorDevice::getInstance()),
-      mSensor(&sensor, mSensorDevice.getHalDeviceVersion())
-{
+HardwareSensor::HardwareSensor(const sensor_t& sensor):
+        BaseSensor(sensor) {
 }
 
 HardwareSensor::~HardwareSensor() {
@@ -65,10 +71,9 @@
     mSensorDevice.autoDisable(ident, handle);
 }
 
-Sensor HardwareSensor::getSensor() const {
-    return mSensor;
+VirtualSensor::VirtualSensor() :
+        BaseSensor(DUMMY_SENSOR), mSensorFusion(SensorFusion::getInstance()) {
 }
 
-
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/sensorservice/SensorInterface.h b/services/sensorservice/SensorInterface.h
index 3e76377..d1cee41 100644
--- a/services/sensorservice/SensorInterface.h
+++ b/services/sensorservice/SensorInterface.h
@@ -17,53 +17,61 @@
 #ifndef ANDROID_SENSOR_INTERFACE_H
 #define ANDROID_SENSOR_INTERFACE_H
 
-#include <stdint.h>
-#include <sys/types.h>
-
 #include <gui/Sensor.h>
-
-#include "SensorDevice.h"
+#include <utils/RefBase.h>
 
 // ---------------------------------------------------------------------------
 
 namespace android {
 // ---------------------------------------------------------------------------
+class SensorDevice;
+class SensorFusion;
 
-class SensorInterface {
+class SensorInterface : public VirtualLightRefBase {
 public:
-    virtual ~SensorInterface();
+    virtual ~SensorInterface() {}
 
-    virtual bool process(sensors_event_t* outEvent,
-            const sensors_event_t& event) = 0;
+    virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) = 0;
 
     virtual status_t activate(void* ident, bool enabled) = 0;
     virtual status_t setDelay(void* ident, int handle, int64_t ns) = 0;
+    virtual status_t batch(void* ident, int handle, int /*flags*/, int64_t samplingPeriodNs,
+                           int64_t maxBatchReportLatencyNs) = 0;
+
+    virtual status_t flush(void* /*ident*/, int /*handle*/) = 0;
+
+    virtual const Sensor& getSensor() const = 0;
+    virtual bool isVirtual() const = 0;
+    virtual void autoDisable(void* /*ident*/, int /*handle*/) = 0;
+};
+
+class BaseSensor : public SensorInterface {
+public:
+    BaseSensor(const sensor_t& sensor);
 
     // Not all sensors need to support batching.
-    virtual status_t batch(void* ident, int handle, int /*flags*/, int64_t samplingPeriodNs,
-                           int64_t maxBatchReportLatencyNs) {
+    virtual status_t batch(void* ident, int handle, int, int64_t samplingPeriodNs,
+                           int64_t maxBatchReportLatencyNs) override {
         if (maxBatchReportLatencyNs == 0) {
             return setDelay(ident, handle, samplingPeriodNs);
         }
         return -EINVAL;
     }
 
-    virtual status_t flush(void* /*ident*/, int /*handle*/) {
+    virtual status_t flush(void* /*ident*/, int /*handle*/) override {
         return -EINVAL;
     }
 
-    virtual Sensor getSensor() const = 0;
-    virtual bool isVirtual() const = 0;
-    virtual void autoDisable(void* /*ident*/, int /*handle*/) { }
+    virtual const Sensor& getSensor() const override { return mSensor; }
+    virtual void autoDisable(void* /*ident*/, int /*handle*/) override { }
+protected:
+    SensorDevice& mSensorDevice;
+    Sensor mSensor;
 };
 
 // ---------------------------------------------------------------------------
 
-class HardwareSensor : public SensorInterface
-{
-    SensorDevice& mSensorDevice;
-    Sensor mSensor;
-
+class HardwareSensor : public BaseSensor {
 public:
     HardwareSensor(const sensor_t& sensor);
 
@@ -72,14 +80,22 @@
     virtual bool process(sensors_event_t* outEvent,
             const sensors_event_t& event);
 
-    virtual status_t activate(void* ident, bool enabled);
+    virtual status_t activate(void* ident, bool enabled) override;
     virtual status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
-                           int64_t maxBatchReportLatencyNs);
-    virtual status_t setDelay(void* ident, int handle, int64_t ns);
-    virtual status_t flush(void* ident, int handle);
-    virtual Sensor getSensor() const;
-    virtual bool isVirtual() const { return false; }
-    virtual void autoDisable(void *ident, int handle);
+                           int64_t maxBatchReportLatencyNs) override;
+    virtual status_t setDelay(void* ident, int handle, int64_t ns) override;
+    virtual status_t flush(void* ident, int handle) override;
+    virtual bool isVirtual() const override { return false; }
+    virtual void autoDisable(void *ident, int handle) override;
+};
+
+class VirtualSensor : public BaseSensor
+{
+public:
+    VirtualSensor();
+    virtual bool isVirtual() const override { return true; }
+protected:
+    SensorFusion& mSensorFusion;
 };
 
 
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
new file mode 100644
index 0000000..c23e21f
--- /dev/null
+++ b/services/sensorservice/SensorList.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 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 "SensorList.h"
+
+#include <hardware/sensors.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace SensorServiceUtil {
+
+const Sensor SensorList::mNonSensor = Sensor("unknown");
+
+bool SensorList::add(
+        int handle, SensorInterface* si, bool isForDebug, bool isVirtual) {
+    std::lock_guard<std::mutex> lk(mLock);
+    if (handle == si->getSensor().getHandle() &&
+        mUsedHandle.insert(handle).second) {
+        // will succeed as the mUsedHandle does not have this handle
+        mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual));
+        return true;
+    }
+    // handle exist already or handle mismatch
+    return false;
+}
+
+bool SensorList::remove(int handle) {
+    std::lock_guard<std::mutex> lk(mLock);
+    auto entry = mHandleMap.find(handle);
+    if (entry != mHandleMap.end()) {
+        mHandleMap.erase(entry);
+        return true;
+    }
+    return false;
+}
+
+String8 SensorList::getName(int handle) const {
+    return getOne<String8>(
+            handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getName();},
+            mNonSensor.getName());
+}
+
+sp<SensorInterface> SensorList::getInterface(int handle) const {
+    return getOne<sp<SensorInterface>>(
+            handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
+}
+
+
+bool SensorList::isNewHandle(int handle) const {
+    std::lock_guard<std::mutex> lk(mLock);
+    return mUsedHandle.find(handle) == mUsedHandle.end();
+}
+
+const Vector<Sensor> SensorList::getUserSensors() const {
+    // lock in forEachEntry
+    Vector<Sensor> sensors;
+    forEachEntry(
+            [&sensors] (const Entry& e) -> bool {
+                if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) {
+                    sensors.add(e.si->getSensor());
+                }
+                return true;
+            });
+    return sensors;
+}
+
+const Vector<Sensor> SensorList::getUserDebugSensors() const {
+    // lock in forEachEntry
+    Vector<Sensor> sensors;
+    forEachEntry(
+            [&sensors] (const Entry& e) -> bool {
+                if (!e.si->getSensor().isDynamicSensor()) {
+                    sensors.add(e.si->getSensor());
+                }
+                return true;
+            });
+    return sensors;
+}
+
+const Vector<Sensor> SensorList::getDynamicSensors() const {
+    // lock in forEachEntry
+    Vector<Sensor> sensors;
+    forEachEntry(
+            [&sensors] (const Entry& e) -> bool {
+                if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) {
+                    sensors.add(e.si->getSensor());
+                }
+                return true;
+            });
+    return sensors;
+}
+
+const Vector<Sensor> SensorList::getVirtualSensors() const {
+    // lock in forEachEntry
+    Vector<Sensor> sensors;
+    forEachEntry(
+            [&sensors] (const Entry& e) -> bool {
+                if (e.isVirtual) {
+                    sensors.add(e.si->getSensor());
+                }
+                return true;
+            });
+    return sensors;
+}
+
+std::string SensorList::dump() const {
+    String8 result;
+
+    result.append("Sensor List:\n");
+    forEachSensor([&result] (const Sensor& s) -> bool {
+            result.appendFormat(
+                    "%-15s| %-10s| version=%d |%-20s| 0x%08x | \"%s\" | type=%d |",
+                    s.getName().string(),
+                    s.getVendor().string(),
+                    s.getVersion(),
+                    s.getStringType().string(),
+                    s.getHandle(),
+                    s.getRequiredPermission().string(),
+                    s.getType());
+
+            const int reportingMode = s.getReportingMode();
+            if (reportingMode == AREPORTING_MODE_CONTINUOUS) {
+                result.append(" continuous | ");
+            } else if (reportingMode == AREPORTING_MODE_ON_CHANGE) {
+                result.append(" on-change | ");
+            } else if (reportingMode == AREPORTING_MODE_ONE_SHOT) {
+                result.append(" one-shot | ");
+            } else if (reportingMode == AREPORTING_MODE_SPECIAL_TRIGGER) {
+                result.append(" special-trigger | ");
+            } else {
+                result.append(" unknown-mode | ");
+            }
+
+            if (s.getMaxDelay() > 0) {
+                result.appendFormat("minRate=%.2fHz | ", 1e6f / s.getMaxDelay());
+            } else {
+                result.appendFormat("maxDelay=%dus | ", s.getMaxDelay());
+            }
+
+            if (s.getMinDelay() > 0) {
+                result.appendFormat("maxRate=%.2fHz | ", 1e6f / s.getMinDelay());
+            } else {
+                result.appendFormat("minDelay=%dus | ", s.getMinDelay());
+            }
+
+            if (s.getFifoMaxEventCount() > 0) {
+                result.appendFormat("FifoMax=%d events | ",
+                        s.getFifoMaxEventCount());
+            } else {
+                result.append("no batching | ");
+            }
+
+            if (s.isWakeUpSensor()) {
+                result.appendFormat("wakeUp | ");
+            } else {
+                result.appendFormat("non-wakeUp | ");
+            }
+
+            result.append("\n");
+            return true;
+        });
+    return std::string(result.string());
+}
+
+SensorList::~SensorList() {
+}
+
+} // namespace SensorServiceUtil
+} // namespace android
+
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
new file mode 100644
index 0000000..ffde619
--- /dev/null
+++ b/services/sensorservice/SensorList.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2016 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_LIST_H
+#define ANDROID_SENSOR_LIST_H
+
+#include "SensorInterface.h"
+
+#include <gui/Sensor.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <mutex>
+#include <map>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+class SensorInterface;
+
+namespace SensorServiceUtil {
+
+class Dumpable {
+public:
+    virtual std::string dump() const;
+    virtual void setFormat(std::string ) {}
+    virtual ~Dumpable() {}
+};
+
+class SensorList : public Dumpable {
+public:
+    // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
+    // object it pointed to and the object should not be released elsewhere.
+    bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false);
+
+    // After a handle is removed, the object that SensorInterface * pointing to may get deleted if
+    // no more sp<> of the same object exist.
+    bool remove(int handle);
+
+    inline bool hasAnySensor() const { return mHandleMap.size() > 0;}
+
+    //helper functions
+    const Vector<Sensor> getUserSensors() const;
+    const Vector<Sensor> getUserDebugSensors() const;
+    const Vector<Sensor> getDynamicSensors() const;
+    const Vector<Sensor> getVirtualSensors() const;
+
+    String8 getName(int handle) const;
+    sp<SensorInterface> getInterface(int handle) const;
+    bool isNewHandle(int handle) const;
+
+    // Iterate through Sensor in sensor list and perform operation f on each Sensor object.
+    //
+    // TF is a function with the signature:
+    //    bool f(const Sensor &);
+    // A return value of 'false' stops the iteration immediately.
+    //
+    // Note: in the function f, it is illegal to make calls to member functions of the same
+    // SensorList object on which forEachSensor is invoked.
+    template <typename TF>
+    void forEachSensor(const TF& f) const;
+
+    const Sensor& getNonSensor() const { return mNonSensor;}
+
+    // Dumpable interface
+    virtual std::string dump() const override;
+
+    virtual ~SensorList();
+private:
+    struct Entry {
+        sp<SensorInterface> si;
+        const bool isForDebug;
+        const bool isVirtual;
+        Entry(SensorInterface* si_, bool debug_, bool virtual_) :
+            si(si_), isForDebug(debug_), isVirtual(virtual_) {
+        }
+    };
+
+    const static Sensor mNonSensor; //.getName() == "unknown",
+
+    // Iterate through Entry in sensor list and perform operation f on each Entry.
+    //
+    // TF is a function with the signature:
+    //    bool f(const Entry &);
+    // A return value of 'false' stops the iteration over entries immediately.
+    //
+    // Note: in the function being passed in, it is illegal to make calls to member functions of the
+    // same SensorList object on which forEachSensor is invoked.
+    template <typename TF>
+    void forEachEntry(const TF& f) const;
+
+    template <typename T, typename TF>
+    T getOne(int handle, const TF& accessor, T def = T()) const;
+
+    mutable std::mutex mLock;
+    std::map<int, Entry> mHandleMap;
+    std::unordered_set<int> mUsedHandle;
+};
+
+template <typename TF>
+void SensorList::forEachSensor(const TF& f) const {
+    // lock happens in forEachEntry
+    forEachEntry([&f] (const Entry& e) -> bool { return f(e.si->getSensor());});
+}
+
+template <typename TF>
+void SensorList::forEachEntry(const TF& f) const {
+    std::lock_guard<std::mutex> lk(mLock);
+
+    for (auto&& i : mHandleMap) {
+        if (!f(i.second)){
+            break;
+        }
+    }
+}
+
+template <typename T, typename TF>
+T SensorList::getOne(int handle, const TF& accessor, T def) const {
+    std::lock_guard<std::mutex> lk(mLock);
+    auto i = mHandleMap.find(handle);
+    if (i != mHandleMap.end()) {
+        return accessor(i->second);
+    } else {
+        return def;
+    }
+}
+
+} // namespace SensorServiceUtil
+} // namespace android
+
+#endif // ANDROID_SENSOR_LIST_H
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 66ef4eb..b7a8740 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -33,6 +33,7 @@
 #include "OrientationSensor.h"
 #include "RotationVectorSensor.h"
 #include "SensorFusion.h"
+#include "SensorInterface.h"
 
 #include "SensorService.h"
 #include "SensorEventConnection.h"
@@ -65,12 +66,10 @@
 
 SensorService::SensorService()
     : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
-      mWakeLockAcquired(false)
-{
+      mWakeLockAcquired(false) {
 }
 
-void SensorService::onFirstRef()
-{
+void SensorService::onFirstRef() {
     ALOGD("nuSensorService starting...");
     SensorDevice& dev(SensorDevice::getInstance());
 
@@ -127,78 +126,51 @@
             // registered)
             SensorFusion::getInstance();
 
-            // build the sensor list returned to users
-            mUserSensorList = mSensorList;
-
             if (hasGyro && hasAccel && hasMag) {
                 // Add Android virtual sensors if they're not already
                 // available in the HAL
-                Sensor aSensor;
+                bool needRotationVector =
+                        (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) != 0;
 
-                aSensor = registerVirtualSensor( new RotationVectorSensor() );
-                if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
-                    mUserSensorList.add(aSensor);
-                }
+                registerSensor(new RotationVectorSensor(), !needRotationVector, true);
+                registerSensor(new OrientationSensor(), !needRotationVector, true);
 
-                aSensor = registerVirtualSensor( new OrientationSensor() );
-                if (virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) {
-                    // if we are doing our own rotation-vector, also add
-                    // the orientation sensor and remove the HAL provided one.
-                    mUserSensorList.replaceAt(aSensor, orientationIndex);
-                }
+                bool needLinearAcceleration =
+                        (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
 
-                aSensor = registerVirtualSensor(
-                                new LinearAccelerationSensor(list, count) );
-                if (virtualSensorsNeeds &
-                            (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) {
-                    mUserSensorList.add(aSensor);
-                }
+                registerSensor(new LinearAccelerationSensor(list, count),
+                               !needLinearAcceleration, true);
 
-                // virtual debugging sensors are not added to mUserSensorList
-                registerVirtualSensor( new CorrectedGyroSensor(list, count) );
-                registerVirtualSensor( new GyroDriftSensor() );
+                // virtual debugging sensors are not for user
+                registerSensor( new CorrectedGyroSensor(list, count), true, true);
+                registerSensor( new GyroDriftSensor(), true, true);
             }
 
             if (hasAccel && hasGyro) {
-                Sensor aSensor;
+                bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
+                registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
 
-                aSensor = registerVirtualSensor(
-                                new GravitySensor(list, count) );
-                if (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) {
-                    mUserSensorList.add(aSensor);
-                }
-
-                aSensor = registerVirtualSensor(
-                                new GameRotationVectorSensor() );
-                if (virtualSensorsNeeds &
-                            (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) {
-                    mUserSensorList.add(aSensor);
-                }
+                bool needGameRotationVector =
+                        (virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
+                registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
             }
 
             if (hasAccel && hasMag) {
-                Sensor aSensor;
-
-                aSensor = registerVirtualSensor(
-                                new GeoMagRotationVectorSensor() );
-                if (virtualSensorsNeeds &
-                        (1<<SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR)) {
-                    mUserSensorList.add(aSensor);
-                }
+                bool needGeoMagRotationVector =
+                        (virtualSensorsNeeds & (1<<SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR)) != 0;
+                registerSensor(new GeoMagRotationVectorSensor(), !needGeoMagRotationVector, true);
             }
 
-            // debugging sensor list
-            mUserSensorListDebug = mSensorList;
-
             // Check if the device really supports batching by looking at the FIFO event
             // counts for each sensor.
             bool batchingSupported = false;
-            for (size_t i = 0; i < mSensorList.size(); ++i) {
-                if (mSensorList[i].getFifoMaxEventCount() > 0) {
-                    batchingSupported = true;
-                    break;
-                }
-            }
+            mSensors.forEachSensor(
+                    [&batchingSupported] (const Sensor& s) -> bool {
+                        if (s.getFifoMaxEventCount() > 0) {
+                            batchingSupported = true;
+                        }
+                        return !batchingSupported;
+                    });
 
             if (batchingSupported) {
                 // Increase socket buffer size to a max of 100 KB for batching capabilities.
@@ -244,65 +216,38 @@
     }
 }
 
-Sensor SensorService::registerSensor(SensorInterface* s)
-{
-    const Sensor sensor(s->getSensor());
-    // add to the sensor list (returned to clients)
-    mSensorList.add(sensor);
-    // add to our handle->SensorInterface mapping
-    mSensorMap.add(sensor.getHandle(), s);
-    // create an entry in the mLastEventSeen array
-    mLastEventSeen.add(sensor.getHandle(), NULL);
-
-    return sensor;
+const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) {
+    int handle = s->getSensor().getHandle();
+    if (mSensors.add(handle, s, isDebug, isVirtual)){
+        mLastEventSeen.add(handle, nullptr);
+        return s->getSensor();
+    } else {
+        return mSensors.getNonSensor();
+    }
 }
 
-Sensor SensorService::registerDynamicSensor(SensorInterface* s)
-{
-    Sensor sensor = registerSensor(s);
-    mDynamicSensorList.add(sensor);
-    return sensor;
+const Sensor& SensorService::registerDynamicSensor(SensorInterface* s, bool isDebug) {
+    return registerSensor(s, isDebug);
 }
 
 bool SensorService::unregisterDynamicSensor(int handle) {
-    bool found = false;
-
-    for (size_t i=0 ; i<mSensorList.size() ; i++) {
-        if (mSensorList[i].getHandle() == handle) {
-            mSensorList.removeAt(i);
-            found = true;
-            break;
-        }
+    bool ret = mSensors.remove(handle);
+    MostRecentEventLogger *buf = mLastEventSeen.valueFor(handle);
+    if (buf) {
+        delete buf;
     }
-
-    if (found) {
-        for (size_t i=0 ; i<mDynamicSensorList.size() ; i++) {
-            if (mDynamicSensorList[i].getHandle() == handle) {
-                mDynamicSensorList.removeAt(i);
-            }
-        }
-
-        mSensorMap.removeItem(handle);
-        mLastEventSeen.removeItem(handle);
-    }
-    return found;
+    mLastEventSeen.removeItem(handle);
+    return ret;
 }
 
-Sensor SensorService::registerVirtualSensor(SensorInterface* s)
-{
-    Sensor sensor = registerSensor(s);
-    mVirtualSensorList.add( s );
-    return sensor;
+const Sensor& SensorService::registerVirtualSensor(SensorInterface* s, bool isDebug) {
+    return registerSensor(s, isDebug, true);
 }
 
-SensorService::~SensorService()
-{
-    for (size_t i=0 ; i<mSensorMap.size() ; i++)
-        delete mSensorMap.valueAt(i);
+SensorService::~SensorService() {
 }
 
-status_t SensorService::dump(int fd, const Vector<String16>& args)
-{
+status_t SensorService::dump(int fd, const Vector<String16>& args) {
     String8 result;
     if (!PermissionCache::checkCallingPermission(sDump)) {
         result.appendFormat("Permission Denial: can't dump SensorService from pid=%d, uid=%d\n",
@@ -363,73 +308,31 @@
                 // Transition to data injection mode supported only from NORMAL mode.
                 return INVALID_OPERATION;
             }
-        } else if (mSensorList.size() == 0) {
+        } else if (!mSensors.hasAnySensor()) {
             result.append("No Sensors on the device\n");
         } else {
             // Default dump the sensor list and debugging information.
-            result.append("Sensor List:\n");
-            for (size_t i=0 ; i<mSensorList.size() ; i++) {
-                const Sensor& s(mSensorList[i]);
-                result.appendFormat(
-                        "%-15s| %-10s| version=%d |%-20s| 0x%08x | \"%s\" | type=%d |",
-                        s.getName().string(),
-                        s.getVendor().string(),
-                        s.getVersion(),
-                        s.getStringType().string(),
-                        s.getHandle(),
-                        s.getRequiredPermission().string(),
-                        s.getType());
+            //
+            result.append(mSensors.dump().c_str());
 
-                const int reportingMode = s.getReportingMode();
-                if (reportingMode == AREPORTING_MODE_CONTINUOUS) {
-                    result.append(" continuous | ");
-                } else if (reportingMode == AREPORTING_MODE_ON_CHANGE) {
-                    result.append(" on-change | ");
-                } else if (reportingMode == AREPORTING_MODE_ONE_SHOT) {
-                    result.append(" one-shot | ");
-                } else {
-                    result.append(" special-trigger | ");
-                }
-
-                if (s.getMaxDelay() > 0) {
-                    result.appendFormat("minRate=%.2fHz | ", 1e6f / s.getMaxDelay());
-                } else {
-                    result.appendFormat("maxDelay=%dus |", s.getMaxDelay());
-                }
-
-                if (s.getMinDelay() > 0) {
-                    result.appendFormat("maxRate=%.2fHz | ", 1e6f / s.getMinDelay());
-                } else {
-                    result.appendFormat("minDelay=%dus |", s.getMinDelay());
-                }
-
-                if (s.getFifoMaxEventCount() > 0) {
-                    result.appendFormat("FifoMax=%d events | ",
-                            s.getFifoMaxEventCount());
-                } else {
-                    result.append("no batching | ");
-                }
-
-                if (s.isWakeUpSensor()) {
-                    result.appendFormat("wakeUp | ");
-                } else {
-                    result.appendFormat("non-wakeUp | ");
-                }
-
-                int bufIndex = mLastEventSeen.indexOfKey(s.getHandle());
-                if (bufIndex >= 0) {
-                    const MostRecentEventLogger* buf = mLastEventSeen.valueAt(bufIndex);
-                    if (buf != NULL && s.getRequiredPermission().isEmpty()) {
-                        buf->printBuffer(result);
-                    } else {
-                        result.append("last=<> \n");
-                    }
-                }
-                result.append("\n");
-            }
             SensorFusion::getInstance().dump(result);
             SensorDevice::getInstance().dump(result);
 
+            result.append("Recent Sensor events:\n");
+            auto& lastEvents = mLastEventSeen;
+            mSensors.forEachSensor([&result, &lastEvents] (const Sensor& s) -> bool {
+                    int bufIndex = lastEvents.indexOfKey(s.getHandle());
+                    if (bufIndex >= 0) {
+                        const MostRecentEventLogger* buf = lastEvents.valueAt(bufIndex);
+                        if (buf != nullptr && s.getRequiredPermission().isEmpty()) {
+                            result.appendFormat("%s (handle:0x%08x): ",
+                                          s.getName().string(), s.getHandle());
+                            buf->printBuffer(result);
+                        }
+                    }
+                    return true;
+                });
+
             result.append("Active sensors:\n");
             for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
                 int handle = mActiveSensors.keyAt(i);
@@ -497,6 +400,7 @@
     return NO_ERROR;
 }
 
+//TODO: move to SensorEventConnection later
 void SensorService::cleanupAutoDisabledSensorLocked(const sp<SensorEventConnection>& connection,
         sensors_event_t const* buffer, const int count) {
     for (int i=0 ; i<count ; i++) {
@@ -505,12 +409,12 @@
             handle = buffer[i].meta_data.sensor;
         }
         if (connection->hasSensor(handle)) {
-            SensorInterface* sensor = mSensorMap.valueFor(handle);
+            sp<SensorInterface> si = getSensorInterfaceFromHandle(handle);
             // If this buffer has an event from a one_shot sensor and this connection is registered
             // for this particular one_shot sensor, try cleaning up the connection.
-            if (sensor != NULL &&
-                sensor->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
-                sensor->autoDisable(connection.get(), handle);
+            if (si != nullptr &&
+                si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
+                si->autoDisable(connection.get(), handle);
                 cleanupWithoutDisableLocked(connection, handle);
             }
 
@@ -518,18 +422,17 @@
    }
 }
 
-bool SensorService::threadLoop()
-{
+bool SensorService::threadLoop() {
     ALOGD("nuSensorService thread starting...");
 
     // each virtual sensor could generate an event per "real" event, that's why we need to size
     // numEventMax much smaller than MAX_RECEIVE_BUFFER_EVENT_COUNT.  in practice, this is too
     // aggressive, but guaranteed to be enough.
+    const size_t vcount = mSensors.getVirtualSensors().size();
     const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT;
-    const size_t numEventMax = minBufferSize / (1 + mVirtualSensorList.size());
+    const size_t numEventMax = minBufferSize / (1 + vcount);
 
     SensorDevice& device(SensorDevice::getInstance());
-    const size_t vcount = mVirtualSensorList.size();
 
     const int halVersion = device.getHalDeviceVersion();
     do {
@@ -575,8 +478,7 @@
         // handle virtual sensors
         if (count && vcount) {
             sensors_event_t const * const event = mSensorEventBuffer;
-            const size_t activeVirtualSensorCount = mActiveVirtualSensors.size();
-            if (activeVirtualSensorCount) {
+            if (!mActiveVirtualSensors.empty()) {
                 size_t k = 0;
                 SensorFusion& fusion(SensorFusion::getInstance());
                 if (fusion.isEnabled()) {
@@ -585,7 +487,7 @@
                     }
                 }
                 for (size_t i=0 ; i<size_t(count) && k<minBufferSize ; i++) {
-                    for (size_t j=0 ; j<activeVirtualSensorCount ; j++) {
+                    for (int handle : mActiveVirtualSensors) {
                         if (count + k >= minBufferSize) {
                             ALOGE("buffer too small to hold all events: "
                                     "count=%zd, k=%zu, size=%zu",
@@ -593,7 +495,12 @@
                             break;
                         }
                         sensors_event_t out;
-                        SensorInterface* si = mActiveVirtualSensors.valueAt(j);
+                        sp<SensorInterface> si = mSensors.getInterface(handle);
+                        if (si == nullptr) {
+                            ALOGE("handle %d is not an valid virtual sensor", handle);
+                            continue;
+                        }
+
                         if (si->process(&out, event[i])) {
                             mSensorEventBuffer[count + k] = out;
                             k++;
@@ -622,10 +529,10 @@
         }
 
         for (int i = 0; i < count; ++i) {
-            // Map flush_complete_events in the buffer to SensorEventConnections which called flush on
-            // the hardware sensor. mapFlushEventsToConnections[i] will be the SensorEventConnection
-            // mapped to the corresponding flush_complete_event in mSensorEventBuffer[i] if such a
-            // mapping exists (NULL otherwise).
+            // Map flush_complete_events in the buffer to SensorEventConnections which called flush
+            // on the hardware sensor. mapFlushEventsToConnections[i] will be the
+            // SensorEventConnection mapped to the corresponding flush_complete_event in
+            // mSensorEventBuffer[i] if such a mapping exists (NULL otherwise).
             mMapFlushEventsToConnections[i] = NULL;
             if (mSensorEventBuffer[i].type == SENSOR_TYPE_META_DATA) {
                 const int sensor_handle = mSensorEventBuffer[i].meta_data.sensor;
@@ -646,9 +553,21 @@
                     ALOGI("Dynamic sensor handle 0x%x connected, type %d, name %s",
                           handle, dynamicSensor.type, dynamicSensor.name);
 
-                    device.handleDynamicSensorConnection(handle, true /*connected*/);
-                    registerDynamicSensor(new HardwareSensor(dynamicSensor));
+                    if (mSensors.isNewHandle(handle)) {
+                        sensor_t s = dynamicSensor;
+                        // make sure the dynamic sensor flag is set
+                        s.flags |= DYNAMIC_SENSOR_MASK;
+                        // force the handle to be consistent
+                        s.handle = handle;
+                        SensorInterface *si = new HardwareSensor(s);
 
+                        // This will release hold on dynamic sensor meta, so it should be called
+                        // after Sensor object is created.
+                        device.handleDynamicSensorConnection(handle, true /*connected*/);
+                        registerDynamicSensor(si);
+                    } else {
+                        ALOGE("Handle %d has been used, cannot use again before reboot.", handle);
+                    }
                 } else {
                     int handle = mSensorEventBuffer[i].dynamic_sensor_meta.handle;
                     ALOGI("Dynamic sensor handle 0x%x disconnected", handle);
@@ -768,8 +687,7 @@
     }
 }
 
-void SensorService::sortEventBuffer(sensors_event_t* buffer, size_t count)
-{
+void SensorService::sortEventBuffer(sensors_event_t* buffer, size_t count) {
     struct compar {
         static int cmp(void const* lhs, void const* rhs) {
             sensors_event_t const* l = static_cast<sensors_event_t const*>(lhs);
@@ -781,20 +699,12 @@
 }
 
 String8 SensorService::getSensorName(int handle) const {
-    size_t count = mUserSensorList.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const Sensor& sensor(mUserSensorList[i]);
-        if (sensor.getHandle() == handle) {
-            return sensor.getName();
-        }
-    }
-    String8 result("unknown");
-    return result;
+    return mSensors.getName(handle);
 }
 
 bool SensorService::isVirtualSensor(int handle) const {
-    SensorInterface* sensor = mSensorMap.valueFor(handle);
-    return sensor != NULL && sensor->isVirtual();
+    sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+    return sensor != nullptr && sensor->isVirtual();
 }
 
 bool SensorService::isWakeUpSensorEvent(const sensors_event_t& event) const {
@@ -802,20 +712,15 @@
     if (event.type == SENSOR_TYPE_META_DATA) {
         handle = event.meta_data.sensor;
     }
-    SensorInterface* sensor = mSensorMap.valueFor(handle);
-    return sensor != NULL && sensor->getSensor().isWakeUpSensor();
+    sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+    return sensor != nullptr && sensor->getSensor().isWakeUpSensor();
 }
 
-SensorService::SensorRecord * SensorService::getSensorRecord(int handle) {
-     return mActiveSensors.valueFor(handle);
-}
-
-Vector<Sensor> SensorService::getSensorList(const String16& opPackageName)
-{
+Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sensors", value, "0");
     const Vector<Sensor>& initialSensorList = (atoi(value)) ?
-            mUserSensorListDebug : mUserSensorList;
+            mSensors.getUserDebugSensors() : mSensors.getUserSensors();
     Vector<Sensor> accessibleSensorList;
     for (size_t i = 0; i < initialSensorList.size(); i++) {
         Sensor sensor = initialSensorList[i];
@@ -831,20 +736,22 @@
     return accessibleSensorList;
 }
 
-Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName)
-{
+Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) {
     Vector<Sensor> accessibleSensorList;
-    for (size_t i = 0; i < mDynamicSensorList.size(); i++) {
-        Sensor sensor = mDynamicSensorList[i];
-        if (canAccessSensor(sensor, "getDynamicSensorList", opPackageName)) {
-            accessibleSensorList.add(sensor);
-        } else {
-            ALOGI("Skipped sensor %s because it requires permission %s and app op %d",
-                  sensor.getName().string(),
-                  sensor.getRequiredPermission().string(),
-                  sensor.getRequiredAppOp());
-        }
-    }
+    mSensors.forEachSensor(
+            [&opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool {
+                if (sensor.isDynamicSensor()) {
+                    if (canAccessSensor(sensor, "getDynamicSensorList", opPackageName)) {
+                        accessibleSensorList.add(sensor);
+                    } else {
+                        ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
+                              sensor.getName().string(),
+                              sensor.getRequiredPermission().string(),
+                              sensor.getRequiredAppOp());
+                    }
+                }
+                return true;
+            });
     return accessibleSensorList;
 }
 
@@ -895,8 +802,7 @@
     return err;
 }
 
-void SensorService::cleanupConnection(SensorEventConnection* c)
-{
+void SensorService::cleanupConnection(SensorEventConnection* c) {
     Mutex::Autolock _l(mLock);
     const wp<SensorEventConnection> connection(c);
     size_t size = mActiveSensors.size();
@@ -905,10 +811,11 @@
         int handle = mActiveSensors.keyAt(i);
         if (c->hasSensor(handle)) {
             ALOGD_IF(DEBUG_CONNECTIONS, "%zu: disabling handle=0x%08x", i, handle);
-            SensorInterface* sensor = mSensorMap.valueFor( handle );
-            ALOGE_IF(!sensor, "mSensorMap[handle=0x%08x] is null!", handle);
-            if (sensor) {
+            sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+            if (sensor != nullptr) {
                 sensor->activate(c, false);
+            } else {
+                ALOGE("sensor interface of handle=0x%08x is null!", handle);
             }
             c->removeSensor(handle);
         }
@@ -921,7 +828,7 @@
         if (rec && rec->removeConnection(connection)) {
             ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection");
             mActiveSensors.removeItemsAt(i, 1);
-            mActiveVirtualSensors.removeItem(handle);
+            mActiveVirtualSensors.erase(handle);
             delete rec;
             size--;
         } else {
@@ -936,23 +843,20 @@
     }
 }
 
-Sensor SensorService::getSensorFromHandle(int handle) const {
-    return mSensorMap.valueFor(handle)->getSensor();
+sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
+    return mSensors.getInterface(handle);
 }
 
+
 status_t SensorService::enable(const sp<SensorEventConnection>& connection,
         int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,
-        const String16& opPackageName)
-{
+        const String16& opPackageName) {
     if (mInitCheck != NO_ERROR)
         return mInitCheck;
 
-    SensorInterface* sensor = mSensorMap.valueFor(handle);
-    if (sensor == NULL) {
-        return BAD_VALUE;
-    }
-
-    if (!canAccessSensor(sensor->getSensor(), "Tried enabling", opPackageName)) {
+    sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+    if (sensor == nullptr ||
+        !canAccessSensor(sensor->getSensor(), "Tried enabling", opPackageName)) {
         return BAD_VALUE;
     }
 
@@ -967,7 +871,7 @@
         rec = new SensorRecord(connection);
         mActiveSensors.add(handle, rec);
         if (sensor->isVirtual()) {
-            mActiveVirtualSensors.add(handle, sensor);
+            mActiveVirtualSensors.emplace(handle);
         }
     } else {
         if (rec->addConnection(connection)) {
@@ -1073,17 +977,15 @@
     return err;
 }
 
-status_t SensorService::disable(const sp<SensorEventConnection>& connection,
-        int handle)
-{
+status_t SensorService::disable(const sp<SensorEventConnection>& connection, int handle) {
     if (mInitCheck != NO_ERROR)
         return mInitCheck;
 
     Mutex::Autolock _l(mLock);
     status_t err = cleanupWithoutDisableLocked(connection, handle);
     if (err == NO_ERROR) {
-        SensorInterface* sensor = mSensorMap.valueFor(handle);
-        err = sensor ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
+        sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+        err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
 
     }
     if (err == NO_ERROR) {
@@ -1123,7 +1025,7 @@
         // see if this sensor becomes inactive
         if (rec->removeConnection(connection)) {
             mActiveSensors.removeItem(handle);
-            mActiveVirtualSensors.removeItem(handle);
+            mActiveVirtualSensors.erase(handle);
             delete rec;
         }
         return NO_ERROR;
@@ -1132,16 +1034,13 @@
 }
 
 status_t SensorService::setEventRate(const sp<SensorEventConnection>& connection,
-        int handle, nsecs_t ns, const String16& opPackageName)
-{
+        int handle, nsecs_t ns, const String16& opPackageName) {
     if (mInitCheck != NO_ERROR)
         return mInitCheck;
 
-    SensorInterface* sensor = mSensorMap.valueFor(handle);
-    if (!sensor)
-        return BAD_VALUE;
-
-    if (!canAccessSensor(sensor->getSensor(), "Tried configuring", opPackageName)) {
+    sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+    if (sensor == nullptr ||
+        !canAccessSensor(sensor->getSensor(), "Tried configuring", opPackageName)) {
         return BAD_VALUE;
     }
 
@@ -1166,7 +1065,10 @@
     // Loop through all sensors for this connection and call flush on each of them.
     for (size_t i = 0; i < connection->mSensorInfo.size(); ++i) {
         const int handle = connection->mSensorInfo.keyAt(i);
-        SensorInterface* sensor = mSensorMap.valueFor(handle);
+        sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+        if (sensor == nullptr) {
+            continue;
+        }
         if (sensor->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
             ALOGE("flush called on a one-shot sensor");
             err = INVALID_OPERATION;
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index ef4516b..6473edb 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -17,26 +17,25 @@
 #ifndef ANDROID_SENSOR_SERVICE_H
 #define ANDROID_SENSOR_SERVICE_H
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Vector.h>
-#include <utils/SortedVector.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <utils/AndroidThreads.h>
-#include <utils/RefBase.h>
-#include <utils/Looper.h>
-#include <utils/String8.h>
+#include "SensorList.h"
 
 #include <binder/BinderService.h>
-
-#include <gui/Sensor.h>
-#include <gui/BitTube.h>
+#include <cutils/compiler.h>
 #include <gui/ISensorServer.h>
 #include <gui/ISensorEventConnection.h>
+#include <gui/Sensor.h>
 
-#include "SensorInterface.h"
+#include <utils/AndroidThreads.h>
+#include <utils/KeyedVector.h>
+#include <utils/Looper.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unordered_set>
 
 #if __clang__
 // Clang warns about SensorEventConnection::dump hiding BBinder::dump. The cause isn't fixable
@@ -56,6 +55,7 @@
 
 namespace android {
 // ---------------------------------------------------------------------------
+class SensorInterface;
 
 class SensorService :
         public BinderService<SensorService>,
@@ -137,7 +137,6 @@
     };
 
     static const char* WAKE_LOCK_NAME;
-
     static char const* getServiceName() ANDROID_API { return "sensorservice"; }
     SensorService() ANDROID_API;
     virtual ~SensorService();
@@ -156,17 +155,17 @@
     virtual int isDataInjectionEnabled();
     virtual status_t dump(int fd, const Vector<String16>& args);
 
-
     static int getNumEventsForSensorType(int sensor_event_type);
     String8 getSensorName(int handle) const;
     bool isVirtualSensor(int handle) const;
-    Sensor getSensorFromHandle(int handle) const;
+    sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
     bool isWakeUpSensor(int type) const;
     void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
     static void sortEventBuffer(sensors_event_t* buffer, size_t count);
-    Sensor registerSensor(SensorInterface* sensor);
-    Sensor registerVirtualSensor(SensorInterface* sensor);
-    Sensor registerDynamicSensor(SensorInterface* sensor);
+    const Sensor& registerSensor(SensorInterface* sensor,
+                                 bool isDebug = false, bool isVirtual = false);
+    const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false);
+    const Sensor& registerDynamicSensor(SensorInterface* sensor, bool isDebug = false);
     bool unregisterDynamicSensor(int handle);
     status_t cleanupWithoutDisable(const sp<SensorEventConnection>& connection, int handle);
     status_t cleanupWithoutDisableLocked(const sp<SensorEventConnection>& connection, int handle);
@@ -182,8 +181,6 @@
     bool isWakeLockAcquired();
     bool isWakeUpSensorEvent(const sensors_event_t& event) const;
 
-    SensorRecord * getSensorRecord(int handle);
-
     sp<Looper> getLooper() const;
 
     // Reset mWakeLockRefCounts for all SensorEventConnections to zero. This may happen if
@@ -211,27 +208,24 @@
     status_t resetToNormalMode();
     status_t resetToNormalModeLocked();
 
-    // constants
-    Vector<Sensor> mSensorList;
-    Vector<Sensor> mUserSensorListDebug;
-    Vector<Sensor> mUserSensorList;
-    Vector<Sensor> mDynamicSensorList;
-    DefaultKeyedVector<int, SensorInterface*> mSensorMap;
-    Vector<SensorInterface *> mVirtualSensorList;
+    SensorServiceUtil::SensorList mSensors;
     status_t mInitCheck;
 
     // Socket buffersize used to initialize BitTube. This size depends on whether batching is
     // supported or not.
-    uint32_t mSocketBufferSize; sp<Looper> mLooper; sp<SensorEventAckReceiver> mAckReceiver;
+    uint32_t mSocketBufferSize;
+    sp<Looper> mLooper;
+    sp<SensorEventAckReceiver> mAckReceiver;
 
     // protected by mLock
     mutable Mutex mLock;
     DefaultKeyedVector<int, SensorRecord*> mActiveSensors;
-    DefaultKeyedVector<int, SensorInterface*> mActiveVirtualSensors;
+    std::unordered_set<int> mActiveVirtualSensors;
     SortedVector< wp<SensorEventConnection> > mActiveConnections;
     bool mWakeLockAcquired;
     sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
     SensorEventConnection const **mMapFlushEventsToConnections;
+    KeyedVector<int32_t, MostRecentEventLogger*> mLastEventSeen;
     Mode mCurrentOperatingMode;
 
     // This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only
@@ -240,9 +234,6 @@
     // sensors.
     String8 mWhiteListedPackage;
 
-    // The size of this vector is constant, only the items are mutable
-    KeyedVector<int32_t, MostRecentEventLogger *> mLastEventSeen;
-
     int mNextSensorRegIndex;
     Vector<SensorRegistrationInfo> mLastNSensorRegistrations;
 };
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index d0a0401..fb6307e 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -12,6 +12,7 @@
     EventThread.cpp \
     FenceTracker.cpp \
     FrameTracker.cpp \
+    GpuService.cpp \
     Layer.cpp \
     LayerDim.cpp \
     MessageQueue.cpp \
@@ -37,10 +38,13 @@
     RenderEngine/GLES11RenderEngine.cpp \
     RenderEngine/GLES20RenderEngine.cpp
 
+LOCAL_C_INCLUDES := \
+	frameworks/native/vulkan/include \
+	external/vulkan-validation-layers/libs/vkjson
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-
+#LOCAL_CFLAGS += -DENABLE_FENCE_TRACKING
 
 USE_HWC2 := false
 ifeq ($(USE_HWC2),true)
@@ -106,6 +110,7 @@
 LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
 LOCAL_CFLAGS += -std=c++14
 
+LOCAL_STATIC_LIBRARIES := libvkjson
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     liblog \
@@ -118,7 +123,8 @@
     libbinder \
     libui \
     libgui \
-    libpowermanager
+    libpowermanager \
+    libvulkan
 
 LOCAL_MODULE := libsurfaceflinger
 
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 5ba387d..37b6420 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -15,6 +15,7 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
 
 // This is needed for stdint.h to define INT64_MAX in C++
 #define __STDC_LIMIT_MACROS
@@ -33,12 +34,21 @@
 #include "DispSync.h"
 #include "EventLog/EventLog.h"
 
+#include <algorithm>
+
+using std::max;
+using std::min;
+
 namespace android {
 
 // Setting this to true enables verbose tracing that can be used to debug
 // vsync event model or phase issues.
 static const bool kTraceDetailedInfo = false;
 
+// Setting this to true adds a zero-phase tracer for correlating with hardware
+// vsync events
+static const bool kEnableZeroPhaseTracer = false;
+
 // This is the threshold used to determine when hardware vsync events are
 // needed to re-synchronize the software vsync model with the hardware.  The
 // error metric used is the mean of the squared difference between each
@@ -49,28 +59,36 @@
 // vsync event.
 static const int64_t kPresentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
 
+#undef LOG_TAG
+#define LOG_TAG "DispSyncThread"
 class DispSyncThread: public Thread {
 public:
 
-    DispSyncThread():
+    DispSyncThread(const char* name):
+            mName(name),
             mStop(false),
             mPeriod(0),
             mPhase(0),
             mReferenceTime(0),
-            mWakeupLatency(0) {
-    }
+            mWakeupLatency(0),
+            mFrameNumber(0) {}
 
     virtual ~DispSyncThread() {}
 
     void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
         mPeriod = period;
         mPhase = phase;
         mReferenceTime = referenceTime;
+        ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
+                " mReferenceTime = %" PRId64, mName, ns2us(mPeriod),
+                ns2us(mPhase), ns2us(mReferenceTime));
         mCond.signal();
     }
 
     void stop() {
+        if (kTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
         mStop = true;
         mCond.signal();
@@ -79,7 +97,6 @@
     virtual bool threadLoop() {
         status_t err;
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        nsecs_t nextEventTime = 0;
 
         while (true) {
             Vector<CallbackInvocation> callbackInvocations;
@@ -89,6 +106,12 @@
             { // Scope for lock
                 Mutex::Autolock lock(mMutex);
 
+                if (kTraceDetailedInfo) {
+                    ATRACE_INT64("DispSync:Frame", mFrameNumber);
+                }
+                ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
+                ++mFrameNumber;
+
                 if (mStop) {
                     return false;
                 }
@@ -103,13 +126,21 @@
                     continue;
                 }
 
-                nextEventTime = computeNextEventTimeLocked(now);
-                targetTime = nextEventTime;
+                targetTime = computeNextEventTimeLocked(now);
 
                 bool isWakeup = false;
 
                 if (now < targetTime) {
-                    err = mCond.waitRelative(mMutex, targetTime - now);
+                    if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
+
+                    if (targetTime == INT64_MAX) {
+                        ALOGV("[%s] Waiting forever", mName);
+                        err = mCond.wait(mMutex);
+                    } else {
+                        ALOGV("[%s] Waiting until %" PRId64, mName,
+                                ns2us(targetTime));
+                        err = mCond.waitRelative(mMutex, targetTime - now);
+                    }
 
                     if (err == TIMED_OUT) {
                         isWakeup = true;
@@ -122,15 +153,15 @@
 
                 now = systemTime(SYSTEM_TIME_MONOTONIC);
 
+                // Don't correct by more than 1.5 ms
+                static const nsecs_t kMaxWakeupLatency = us2ns(1500);
+
                 if (isWakeup) {
                     mWakeupLatency = ((mWakeupLatency * 63) +
                             (now - targetTime)) / 64;
-                    if (mWakeupLatency > 500000) {
-                        // Don't correct by more than 500 us
-                        mWakeupLatency = 500000;
-                    }
+                    mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
                     if (kTraceDetailedInfo) {
-                        ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
+                        ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
                         ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
                     }
                 }
@@ -146,7 +177,9 @@
         return false;
     }
 
-    status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
+    status_t addEventListener(const char* name, nsecs_t phase,
+            const sp<DispSync::Callback>& callback) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
 
         for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -156,15 +189,14 @@
         }
 
         EventListener listener;
+        listener.mName = name;
         listener.mPhase = phase;
         listener.mCallback = callback;
 
         // We want to allow the firstmost future event to fire without
-        // allowing any past events to fire.  Because
-        // computeListenerNextEventTimeLocked filters out events within a half
-        // a period of the last event time, we need to initialize the last
-        // event time to a half a period in the past.
-        listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC) - mPeriod / 2;
+        // allowing any past events to fire
+        listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase -
+                mWakeupLatency;
 
         mEventListeners.push(listener);
 
@@ -174,6 +206,7 @@
     }
 
     status_t removeEventListener(const sp<DispSync::Callback>& callback) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
 
         for (size_t i = 0; i < mEventListeners.size(); i++) {
@@ -189,6 +222,7 @@
 
     // This method is only here to handle the kIgnorePresentFences case.
     bool hasAnyEventListeners() {
+        if (kTraceDetailedInfo) ATRACE_CALL();
         Mutex::Autolock lock(mMutex);
         return !mEventListeners.empty();
     }
@@ -196,6 +230,7 @@
 private:
 
     struct EventListener {
+        const char* mName;
         nsecs_t mPhase;
         nsecs_t mLastEventTime;
         sp<DispSync::Callback> mCallback;
@@ -207,6 +242,8 @@
     };
 
     nsecs_t computeNextEventTimeLocked(nsecs_t now) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
+        ALOGV("[%s] computeNextEventTimeLocked", mName);
         nsecs_t nextEventTime = INT64_MAX;
         for (size_t i = 0; i < mEventListeners.size(); i++) {
             nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
@@ -217,21 +254,28 @@
             }
         }
 
+        ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime));
         return nextEventTime;
     }
 
     Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
+        ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName,
+                ns2us(now));
+
         Vector<CallbackInvocation> callbackInvocations;
-        nsecs_t ref = now - mPeriod;
+        nsecs_t onePeriodAgo = now - mPeriod;
 
         for (size_t i = 0; i < mEventListeners.size(); i++) {
             nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
-                    ref);
+                    onePeriodAgo);
 
             if (t < now) {
                 CallbackInvocation ci;
                 ci.mCallback = mEventListeners[i].mCallback;
                 ci.mEventTime = t;
+                ALOGV("[%s] [%s] Preparing to fire", mName,
+                        mEventListeners[i].mName);
                 callbackInvocations.push(ci);
                 mEventListeners.editItemAt(i).mLastEventTime = t;
             }
@@ -241,29 +285,67 @@
     }
 
     nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
-            nsecs_t ref) {
+            nsecs_t baseTime) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
+        ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")",
+                mName, listener.mName, ns2us(baseTime));
 
-        nsecs_t lastEventTime = listener.mLastEventTime;
-        if (ref < lastEventTime) {
-            ref = lastEventTime;
+        nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
+        ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime));
+        if (baseTime < lastEventTime) {
+            baseTime = lastEventTime;
+            ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName,
+                    ns2us(baseTime));
         }
 
-        nsecs_t phase = mReferenceTime + mPhase + listener.mPhase;
-        nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase;
+        baseTime -= mReferenceTime;
+        ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime));
+        nsecs_t phase = mPhase + listener.mPhase;
+        ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase));
+        baseTime -= phase;
+        ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime));
 
-        if (t - listener.mLastEventTime < mPeriod / 2) {
+        // If our previous time is before the reference (because the reference
+        // has since been updated), the division by mPeriod will truncate
+        // towards zero instead of computing the floor. Since in all cases
+        // before the reference we want the next time to be effectively now, we
+        // set baseTime to -mPeriod so that numPeriods will be -1.
+        // When we add 1 and the phase, we will be at the correct event time for
+        // this period.
+        if (baseTime < 0) {
+            ALOGV("[%s] Correcting negative baseTime", mName);
+            baseTime = -mPeriod;
+        }
+
+        nsecs_t numPeriods = baseTime / mPeriod;
+        ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods);
+        nsecs_t t = (numPeriods + 1) * mPeriod + phase;
+        ALOGV("[%s] t = %" PRId64, mName, ns2us(t));
+        t += mReferenceTime;
+        ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t));
+
+        // Check that it's been slightly more than half a period since the last
+        // event so that we don't accidentally fall into double-rate vsyncs
+        if (t - listener.mLastEventTime < (3 * mPeriod / 5)) {
             t += mPeriod;
+            ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
         }
 
+        t -= mWakeupLatency;
+        ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t));
+
         return t;
     }
 
     void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
+        if (kTraceDetailedInfo) ATRACE_CALL();
         for (size_t i = 0; i < callbacks.size(); i++) {
             callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
         }
     }
 
+    const char* const mName;
+
     bool mStop;
 
     nsecs_t mPeriod;
@@ -271,12 +353,17 @@
     nsecs_t mReferenceTime;
     nsecs_t mWakeupLatency;
 
+    int64_t mFrameNumber;
+
     Vector<EventListener> mEventListeners;
 
     Mutex mMutex;
     Condition mCond;
 };
 
+#undef LOG_TAG
+#define LOG_TAG "DispSync"
+
 class ZeroPhaseTracer : public DispSync::Callback {
 public:
     ZeroPhaseTracer() : mParity(false) {}
@@ -290,9 +377,10 @@
     bool mParity;
 };
 
-DispSync::DispSync() :
+DispSync::DispSync(const char* name) :
+        mName(name),
         mRefreshSkipCount(0),
-        mThread(new DispSyncThread()) {
+        mThread(new DispSyncThread(name)) {
 
     mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
 
@@ -305,8 +393,8 @@
         // Even if we're just ignoring the fences, the zero-phase tracing is
         // not needed because any time there is an event registered we will
         // turn on the HW vsync events.
-        if (!kIgnorePresentFences) {
-            addEventListener(0, new ZeroPhaseTracer());
+        if (!kIgnorePresentFences && kEnableZeroPhaseTracer) {
+            addEventListener("ZeroPhaseTracer", 0, new ZeroPhaseTracer());
         }
     }
 }
@@ -351,7 +439,7 @@
 
 void DispSync::beginResync() {
     Mutex::Autolock lock(mMutex);
-
+    ALOGV("[%s] beginResync", mName);
     mModelUpdated = false;
     mNumResyncSamples = 0;
 }
@@ -359,11 +447,17 @@
 bool DispSync::addResyncSample(nsecs_t timestamp) {
     Mutex::Autolock lock(mMutex);
 
+    ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
+
     size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
     mResyncSamples[idx] = timestamp;
     if (mNumResyncSamples == 0) {
         mPhase = 0;
         mReferenceTime = timestamp;
+        ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
+                "mReferenceTime = %" PRId64, mName, ns2us(mPeriod),
+                ns2us(mReferenceTime));
+        mThread->updateModel(mPeriod, mPhase, mReferenceTime);
     }
 
     if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
@@ -387,17 +481,21 @@
         return mThread->hasAnyEventListeners();
     }
 
-    return !mModelUpdated || mError > kErrorThreshold;
+    // Check against kErrorThreshold / 2 to add some hysteresis before having to
+    // resync again
+    bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
+    ALOGV("[%s] addResyncSample returning %s", mName,
+            modelLocked ? "locked" : "unlocked");
+    return !modelLocked;
 }
 
 void DispSync::endResync() {
 }
 
-status_t DispSync::addEventListener(nsecs_t phase,
+status_t DispSync::addEventListener(const char* name, nsecs_t phase,
         const sp<Callback>& callback) {
-
     Mutex::Autolock lock(mMutex);
-    return mThread->addEventListener(phase, callback);
+    return mThread->addEventListener(name, phase, callback);
 }
 
 void DispSync::setRefreshSkipCount(int count) {
@@ -427,20 +525,32 @@
 }
 
 void DispSync::updateModelLocked() {
+    ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples);
     if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
+        ALOGV("[%s] Computing...", mName);
         nsecs_t durationSum = 0;
+        nsecs_t minDuration = INT64_MAX;
+        nsecs_t maxDuration = 0;
         for (size_t i = 1; i < mNumResyncSamples; i++) {
             size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
             size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
-            durationSum += mResyncSamples[idx] - mResyncSamples[prev];
+            nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
+            durationSum += duration;
+            minDuration = min(minDuration, duration);
+            maxDuration = max(maxDuration, duration);
         }
 
-        mPeriod = durationSum / (mNumResyncSamples - 1);
+        // Exclude the min and max from the average
+        durationSum -= minDuration + maxDuration;
+        mPeriod = durationSum / (mNumResyncSamples - 3);
+
+        ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
 
         double sampleAvgX = 0;
         double sampleAvgY = 0;
         double scale = 2.0 * M_PI / double(mPeriod);
-        for (size_t i = 0; i < mNumResyncSamples; i++) {
+        // Intentionally skip the first sample
+        for (size_t i = 1; i < mNumResyncSamples; i++) {
             size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
             nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
             double samplePhase = double(sample % mPeriod) * scale;
@@ -448,18 +558,21 @@
             sampleAvgY += sin(samplePhase);
         }
 
-        sampleAvgX /= double(mNumResyncSamples);
-        sampleAvgY /= double(mNumResyncSamples);
+        sampleAvgX /= double(mNumResyncSamples - 1);
+        sampleAvgY /= double(mNumResyncSamples - 1);
 
         mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
 
-        if (mPhase < 0) {
+        ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));
+
+        if (mPhase < -(mPeriod / 2)) {
             mPhase += mPeriod;
+            ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
         }
 
         if (kTraceDetailedInfo) {
             ATRACE_INT64("DispSync:Period", mPeriod);
-            ATRACE_INT64("DispSync:Phase", mPhase);
+            ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
         }
 
         // Artificially inflate the period if requested.
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index a8524b9..537c81b 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -26,11 +26,8 @@
 namespace android {
 
 // Ignore present (retire) fences if the device doesn't have support for the
-// sync framework, or if all phase offsets are zero.  The latter is useful
-// because it allows us to avoid resync bursts on devices that don't need
-// phase-offset VSYNC events.
-#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK) || \
-        (VSYNC_EVENT_PHASE_OFFSET_NS == 0 && SF_VSYNC_EVENT_PHASE_OFFSET_NS == 0)
+// sync framework
+#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK)
 static const bool kIgnorePresentFences = true;
 #else
 static const bool kIgnorePresentFences = false;
@@ -64,7 +61,7 @@
         virtual void onDispSyncEvent(nsecs_t when) = 0;
     };
 
-    DispSync();
+    DispSync(const char* name);
     ~DispSync();
 
     // reset clears the resync samples and error value.
@@ -114,7 +111,8 @@
     // given phase offset from the hardware vsync events.  The callback is
     // called from a separate thread and it should return reasonably quickly
     // (i.e. within a few hundred microseconds).
-    status_t addEventListener(nsecs_t phase, const sp<Callback>& callback);
+    status_t addEventListener(const char* name, nsecs_t phase,
+            const sp<Callback>& callback);
 
     // removeEventListener removes an already-registered event callback.  Once
     // this method returns that callback will no longer be called by the
@@ -137,10 +135,12 @@
     void resetErrorLocked();
 
     enum { MAX_RESYNC_SAMPLES = 32 };
-    enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 };
+    enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };
     enum { NUM_PRESENT_SAMPLES = 8 };
     enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
 
+    const char* const mName;
+
     // mPeriod is the computed period of the modeled vsync events in
     // nanoseconds.
     nsecs_t mPeriod;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index f7c8473..a67b3ff 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -68,6 +68,8 @@
  *
  */
 
+uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
+
 DisplayDevice::DisplayDevice(
         const sp<SurfaceFlinger>& flinger,
         DisplayType type,
@@ -550,10 +552,32 @@
     }
 
     mOrientation = orientation;
+    if (mType == DisplayType::DISPLAY_PRIMARY) {
+        uint32_t transform = 0;
+        switch (mOrientation) {
+            case DisplayState::eOrientationDefault:
+                transform = Transform::ROT_0;
+                break;
+            case DisplayState::eOrientation90:
+                transform = Transform::ROT_90;
+                break;
+            case DisplayState::eOrientation180:
+                transform = Transform::ROT_180;
+                break;
+            case DisplayState::eOrientation270:
+                transform = Transform::ROT_270;
+                break;
+        }
+        sPrimaryDisplayOrientation = transform;
+    }
     mViewport = viewport;
     mFrame = frame;
 }
 
+uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
+    return sPrimaryDisplayOrientation;
+}
+
 void DisplayDevice::dump(String8& result) const {
     const Transform& tr(mGlobalTransform);
     result.appendFormat(
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 9ac8a97..dd9b104 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -129,6 +129,7 @@
 
     int                     getOrientation() const { return mOrientation; }
     uint32_t                getOrientationTransform() const;
+    static uint32_t         getPrimaryDisplayOrientationTransform();
     const Transform&        getTransform() const { return mGlobalTransform; }
     const Rect              getViewport() const { return mViewport; }
     const Rect              getFrame() const { return mFrame; }
@@ -238,6 +239,7 @@
 
     uint32_t mLayerStack;
     int mOrientation;
+    static uint32_t sPrimaryDisplayOrientation;
     // user-provided visible area of the layer stack
     Rect mViewport;
     // user-provided rectangle where mViewport gets mapped to
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 0e97a53..f7678e4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -30,6 +30,7 @@
 
 #include <android/configuration.h>
 
+#include <algorithm>
 #include <inttypes.h>
 
 extern "C" {
@@ -74,6 +75,7 @@
 using android::Fence;
 using android::FloatRect;
 using android::GraphicBuffer;
+using android::HdrCapabilities;
 using android::Rect;
 using android::Region;
 using android::sp;
@@ -100,6 +102,7 @@
     mGetDisplayRequests(nullptr),
     mGetDisplayType(nullptr),
     mGetDozeSupport(nullptr),
+    mGetHdrCapabilities(nullptr),
     mGetReleaseFences(nullptr),
     mPresentDisplay(nullptr),
     mSetActiveConfig(nullptr),
@@ -301,88 +304,98 @@
     mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
 }
 
+bool Device::hasCapability(HWC2::Capability capability) const
+{
+    return std::find(mCapabilities.cbegin(), mCapabilities.cend(),
+            capability) != mCapabilities.cend();
+}
+
 void Device::loadFunctionPointers()
 {
     // For all of these early returns, we log an error message inside
     // loadFunctionPointer specifying which function failed to load
 
     // Display function pointers
-    if(!loadFunctionPointer(FunctionDescriptor::CreateVirtualDisplay,
+    if (!loadFunctionPointer(FunctionDescriptor::CreateVirtualDisplay,
             mCreateVirtualDisplay)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::DestroyVirtualDisplay,
+    if (!loadFunctionPointer(FunctionDescriptor::DestroyVirtualDisplay,
             mDestroyVirtualDisplay)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::Dump, mDump)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetMaxVirtualDisplayCount,
+    if (!loadFunctionPointer(FunctionDescriptor::Dump, mDump)) return;
+    if (!loadFunctionPointer(FunctionDescriptor::GetMaxVirtualDisplayCount,
             mGetMaxVirtualDisplayCount)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::RegisterCallback,
+    if (!loadFunctionPointer(FunctionDescriptor::RegisterCallback,
             mRegisterCallback)) return;
 
     // Device function pointers
-    if(!loadFunctionPointer(FunctionDescriptor::AcceptDisplayChanges,
+    if (!loadFunctionPointer(FunctionDescriptor::AcceptDisplayChanges,
             mAcceptDisplayChanges)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::CreateLayer,
+    if (!loadFunctionPointer(FunctionDescriptor::CreateLayer,
             mCreateLayer)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::DestroyLayer,
+    if (!loadFunctionPointer(FunctionDescriptor::DestroyLayer,
             mDestroyLayer)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetActiveConfig,
+    if (!loadFunctionPointer(FunctionDescriptor::GetActiveConfig,
             mGetActiveConfig)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetChangedCompositionTypes,
+    if (!loadFunctionPointer(FunctionDescriptor::GetChangedCompositionTypes,
             mGetChangedCompositionTypes)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayAttribute,
+    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayAttribute,
             mGetDisplayAttribute)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayConfigs,
+    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayConfigs,
             mGetDisplayConfigs)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayName,
+    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayName,
             mGetDisplayName)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayRequests,
+    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayRequests,
             mGetDisplayRequests)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayType,
+    if (!loadFunctionPointer(FunctionDescriptor::GetDisplayType,
             mGetDisplayType)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetDozeSupport,
+    if (!loadFunctionPointer(FunctionDescriptor::GetDozeSupport,
             mGetDozeSupport)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::GetReleaseFences,
+    if (!loadFunctionPointer(FunctionDescriptor::GetHdrCapabilities,
+            mGetHdrCapabilities)) return;
+    if (!loadFunctionPointer(FunctionDescriptor::GetReleaseFences,
             mGetReleaseFences)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::PresentDisplay,
+    if (!loadFunctionPointer(FunctionDescriptor::PresentDisplay,
             mPresentDisplay)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetActiveConfig,
+    if (!loadFunctionPointer(FunctionDescriptor::SetActiveConfig,
             mSetActiveConfig)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetClientTarget,
+    if (!loadFunctionPointer(FunctionDescriptor::SetClientTarget,
             mSetClientTarget)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetOutputBuffer,
+    if (!loadFunctionPointer(FunctionDescriptor::SetOutputBuffer,
             mSetOutputBuffer)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetPowerMode,
+    if (!loadFunctionPointer(FunctionDescriptor::SetPowerMode,
             mSetPowerMode)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetVsyncEnabled,
+    if (!loadFunctionPointer(FunctionDescriptor::SetVsyncEnabled,
             mSetVsyncEnabled)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::ValidateDisplay,
+    if (!loadFunctionPointer(FunctionDescriptor::ValidateDisplay,
             mValidateDisplay)) return;
 
     // Layer function pointers
-    if(!loadFunctionPointer(FunctionDescriptor::SetCursorPosition,
+    if (!loadFunctionPointer(FunctionDescriptor::SetCursorPosition,
             mSetCursorPosition)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerBuffer,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerBuffer,
             mSetLayerBuffer)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerSurfaceDamage,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerSurfaceDamage,
             mSetLayerSurfaceDamage)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerBlendMode,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerBlendMode,
             mSetLayerBlendMode)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerColor,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerColor,
             mSetLayerColor)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerCompositionType,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerCompositionType,
             mSetLayerCompositionType)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerDisplayFrame,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerDisplayFrame,
             mSetLayerDisplayFrame)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
             mSetLayerPlaneAlpha)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
-            mSetLayerSidebandStream)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop,
+    if (hasCapability(Capability::SidebandStream)) {
+        if (!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
+                mSetLayerSidebandStream)) return;
+    }
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop,
             mSetLayerSourceCrop)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerTransform,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerTransform,
             mSetLayerTransform)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerVisibleRegion,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerVisibleRegion,
             mSetLayerVisibleRegion)) return;
-    if(!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
+    if (!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
             mSetLayerZOrder)) return;
 }
 
@@ -637,6 +650,34 @@
     return Error::None;
 }
 
+Error Display::getHdrCapabilities(
+        std::unique_ptr<HdrCapabilities>* outCapabilities) const
+{
+    uint32_t numTypes = 0;
+    float maxLuminance = -1.0f;
+    float maxAverageLuminance = -1.0f;
+    float minLuminance = -1.0f;
+    int32_t intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId,
+            &numTypes, nullptr, &maxLuminance, &maxAverageLuminance,
+            &minLuminance);
+    auto error = static_cast<HWC2::Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::vector<int32_t> types(numTypes);
+    intError = mDevice.mGetHdrCapabilities(mDevice.mHwcDevice, mId, &numTypes,
+            types.data(), &maxLuminance, &maxAverageLuminance, &minLuminance);
+    error = static_cast<HWC2::Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    *outCapabilities = std::make_unique<HdrCapabilities>(std::move(types),
+            maxLuminance, maxAverageLuminance, minLuminance);
+    return Error::None;
+}
+
 Error Display::getReleaseFences(
         std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const
 {
@@ -941,6 +982,11 @@
 
 Error Layer::setSidebandStream(const native_handle_t* stream)
 {
+    if (!mDevice.hasCapability(Capability::SidebandStream)) {
+        ALOGE("Attempted to call setSidebandStream without checking that the "
+                "device supports sideband streams");
+        return Error::Unsupported;
+    }
     int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
             mDisplayId, mId, stream);
     return static_cast<Error>(intError);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a7bd28c..967add0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -23,6 +23,8 @@
 #undef HWC2_INCLUDE_STRINGIFICATION
 #undef HWC2_USE_CPP11
 
+#include <ui/HdrCapabilities.h>
+
 #include <utils/Log.h>
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
@@ -87,6 +89,8 @@
     // as connected
     std::shared_ptr<Display> getDisplayById(hwc2_display_t id);
 
+    bool hasCapability(HWC2::Capability capability) const;
+
 private:
     // Initialization methods
 
@@ -145,6 +149,7 @@
     HWC2_PFN_GET_DISPLAY_REQUESTS mGetDisplayRequests;
     HWC2_PFN_GET_DISPLAY_TYPE mGetDisplayType;
     HWC2_PFN_GET_DOZE_SUPPORT mGetDozeSupport;
+    HWC2_PFN_GET_HDR_CAPABILITIES mGetHdrCapabilities;
     HWC2_PFN_GET_RELEASE_FENCES mGetReleaseFences;
     HWC2_PFN_PRESENT_DISPLAY mPresentDisplay;
     HWC2_PFN_SET_ACTIVE_CONFIG mSetActiveConfig;
@@ -279,6 +284,8 @@
                     outLayerRequests);
     [[clang::warn_unused_result]] Error getType(DisplayType* outType) const;
     [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
+    [[clang::warn_unused_result]] Error getHdrCapabilities(
+            std::unique_ptr<android::HdrCapabilities>* outCapabilities) const;
     [[clang::warn_unused_result]] Error getReleaseFences(
             std::unordered_map<std::shared_ptr<Layer>,
                     android::sp<android::Fence>>* outFences) const;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index dabc77f..6ebcdfe 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -238,6 +238,11 @@
             return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(
                     displayHook<decltype(&Display::getDozeSupport),
                     &Display::getDozeSupport, int32_t*>);
+        case FunctionDescriptor::GetHdrCapabilities:
+            return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(
+                    displayHook<decltype(&Display::getHdrCapabilities),
+                    &Display::getHdrCapabilities, uint32_t*, int32_t*, float*,
+                    float*, float*>);
         case FunctionDescriptor::GetReleaseFences:
             return asFP<HWC2_PFN_GET_RELEASE_FENCES>(
                     displayHook<decltype(&Display::getReleaseFences),
@@ -709,6 +714,15 @@
     return Error::None;
 }
 
+Error HWC2On1Adapter::Display::getHdrCapabilities(uint32_t* outNumTypes,
+        int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
+        float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/)
+{
+    // This isn't supported on HWC1, so per the HWC2 header, return numTypes = 0
+    *outNumTypes = 0;
+    return Error::None;
+}
+
 Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName)
 {
     std::unique_lock<std::recursive_mutex> lock(mStateMutex);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index bffeefe..6fdb184 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -194,6 +194,9 @@
             HWC2::Error getConfigs(uint32_t* outNumConfigs,
                     hwc2_config_t* outConfigIds);
             HWC2::Error getDozeSupport(int32_t* outSupport);
+            HWC2::Error getHdrCapabilities(uint32_t* outNumTypes,
+                    int32_t* outTypes, float* outMaxLuminance,
+                    float* outMaxAverageLuminance, float* outMinLuminance);
             HWC2::Error getName(uint32_t* outSize, char* outName);
             HWC2::Error getReleaseFences(uint32_t* outNumElements,
                     hwc2_layer_t* outLayers, int32_t* outFences);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 26f9519..0bec0b8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -116,22 +116,20 @@
         abort();
     }
 
-    if (module->module_api_version >= 0x0200) {
-        hwc2_device_t* hwc2device = nullptr;
-        int error = hwc2_open(module, &hwc2device);
-        if (error != 0) {
-            ALOGE("Failed to open HWC2 device (%s), aborting", strerror(-error));
-            abort();
-        }
-        mHwcDevice = std::make_unique<HWC2::Device>(hwc2device);
+    hw_device_t* device = nullptr;
+    int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
+    if (error != 0) {
+        ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));
+        abort();
+    }
+
+    uint32_t majorVersion = (device->version >> 24) & 0xF;
+    if (majorVersion == 2) {
+        mHwcDevice = std::make_unique<HWC2::Device>(
+                reinterpret_cast<hwc2_device_t*>(device));
     } else {
-        hwc_composer_device_1_t* hwc1device = nullptr;
-        int error = hwc_open_1(module, &hwc1device);
-        if (error) {
-            ALOGE("Failed to open HWC1 device (%s), aborting", strerror(-error));
-            abort();
-        }
-        mAdapter = std::make_unique<HWC2On1Adapter>(hwc1device);
+        mAdapter = std::make_unique<HWC2On1Adapter>(
+                reinterpret_cast<hwc_composer_device_1_t*>(device));
         uint8_t minorVersion = mAdapter->getHwc1MinorVersion();
         if (minorVersion < 1) {
             ALOGE("Cannot adapt to HWC version %d.%d",
@@ -736,6 +734,26 @@
     mDisplayData[displayId].releaseFences.clear();
 }
 
+std::unique_ptr<HdrCapabilities> HWComposer::getHdrCapabilities(
+        int32_t displayId) {
+    if (!isValidDisplay(displayId)) {
+        ALOGE("getHdrCapabilities: Display %d is not valid", displayId);
+        return nullptr;
+    }
+
+    auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+    std::unique_ptr<HdrCapabilities> capabilities;
+    auto error = hwcDisplay->getHdrCapabilities(&capabilities);
+    if (error != HWC2::Error::None) {
+        ALOGE("getOutputCapabilities: Failed to get capabilities on display %d:"
+                " %s (%d)", displayId, to_string(error).c_str(),
+                static_cast<int32_t>(error));
+        return nullptr;
+    }
+
+    return capabilities;
+}
+
 // Converts a PixelFormat to a human-readable string.  Max 11 chars.
 // (Could use a table of prefab String8 objects.)
 /*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 30c8f67..d407877 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -130,6 +130,9 @@
     // it can call this to clear the shared pointers in the release fence map
     void clearReleaseFences(int32_t displayId);
 
+    // Returns the HDR capabilities of the given display
+    std::unique_ptr<HdrCapabilities> getHdrCapabilities(int32_t displayId);
+
     // Events handling ---------------------------------------------------------
 
     void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index d37fcb2..4afd8a2 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -926,16 +926,19 @@
 protected:
     HWCTYPE* const mLayerList;
     HWCTYPE* mCurrentLayer;
-    Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer) { }
+    Iterable(HWCTYPE* layer) : mLayerList(layer), mCurrentLayer(layer),
+            mIndex(0) { }
     inline HWCTYPE const * getLayer() const { return mCurrentLayer; }
     inline HWCTYPE* getLayer() { return mCurrentLayer; }
     virtual ~Iterable() { }
+    size_t mIndex;
 private:
     // returns a copy of ourselves
     virtual HWComposer::HWCLayer* dup() {
         return new CONCRETE( static_cast<const CONCRETE&>(*this) );
     }
     virtual status_t setLayer(size_t index) {
+        mIndex = index;
         mCurrentLayer = &mLayerList[index];
         return NO_ERROR;
     }
@@ -948,8 +951,12 @@
 class HWCLayerVersion1 : public Iterable<HWCLayerVersion1, hwc_layer_1_t> {
     struct hwc_composer_device_1* mHwc;
 public:
-    HWCLayerVersion1(struct hwc_composer_device_1* hwc, hwc_layer_1_t* layer)
-        : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer), mHwc(hwc) { }
+    HWCLayerVersion1(struct hwc_composer_device_1* hwc, hwc_layer_1_t* layer,
+            Vector<Region>* visibleRegions,
+            Vector<Region>* surfaceDamageRegions)
+        : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer), mHwc(hwc),
+          mVisibleRegions(visibleRegions),
+          mSurfaceDamageRegions(surfaceDamageRegions) {}
 
     virtual int32_t getCompositionType() const {
         return getLayer()->compositionType;
@@ -1037,9 +1044,10 @@
     }
     virtual void setVisibleRegionScreen(const Region& reg) {
         hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen;
-        mVisibleRegion = reg;
+        mVisibleRegions->editItemAt(mIndex) = reg;
         visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>(
-                mVisibleRegion.getArray(&visibleRegion.numRects));
+                mVisibleRegions->itemAt(mIndex).getArray(
+                &visibleRegion.numRects));
     }
     virtual void setSurfaceDamage(const Region& reg) {
         if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) {
@@ -1053,9 +1061,10 @@
             surfaceDamage.rects = NULL;
             return;
         }
-        mSurfaceDamage = reg;
+        mSurfaceDamageRegions->editItemAt(mIndex) = reg;
         surfaceDamage.rects = reinterpret_cast<hwc_rect_t const *>(
-                mSurfaceDamage.getArray(&surfaceDamage.numRects));
+                mSurfaceDamageRegions->itemAt(mIndex).getArray(
+                &surfaceDamage.numRects));
     }
     virtual void setSidebandStream(const sp<NativeHandle>& stream) {
         ALOG_ASSERT(stream->handle() != NULL);
@@ -1081,11 +1090,10 @@
     }
 
 protected:
-    // We need to hold "copies" of these for memory management purposes. The
-    // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
-    // internally doesn't copy the memory unless one of the copies is modified.
-    Region mVisibleRegion;
-    Region mSurfaceDamage;
+    // Pointers to the vectors of Region backing-memory held in DisplayData.
+    // Only the Region at mIndex corresponds to this Layer.
+    Vector<Region>* mVisibleRegions;
+    Vector<Region>* mSurfaceDamageRegions;
 };
 
 /*
@@ -1095,11 +1103,18 @@
     if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
         return LayerListIterator();
     }
-    const DisplayData& disp(mDisplayData[id]);
+    DisplayData& disp(mDisplayData[id]);
     if (!mHwc || !disp.list || index > disp.list->numHwLayers) {
         return LayerListIterator();
     }
-    return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers), index);
+    if (disp.visibleRegions.size() < disp.list->numHwLayers) {
+        disp.visibleRegions.resize(disp.list->numHwLayers);
+    }
+    if (disp.surfaceDamageRegions.size() < disp.list->numHwLayers) {
+        disp.surfaceDamageRegions.resize(disp.list->numHwLayers);
+    }
+    return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers,
+            &disp.visibleRegions, &disp.surfaceDamageRegions), index);
 }
 
 /*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index f5f7d77..c861817 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -346,6 +346,13 @@
 
         // protected by mEventControlLock
         int32_t events;
+
+        // We need to hold "copies" of these for memory management purposes. The
+        // actual hwc_layer_1_t holds pointers to the memory within. Vector<>
+        // internally doesn't copy the memory unless one of the copies is
+        // modified.
+        Vector<Region> visibleRegions;
+        Vector<Region> surfaceDamageRegions;
     };
 
     sp<SurfaceFlinger>              mFlinger;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 37b7aba..7e4e797 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -605,6 +605,12 @@
     return INVALID_OPERATION;
 }
 
+status_t VirtualDisplaySurface::getLastQueuedBuffer(
+        sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/) {
+    ALOGE("getLastQueuedBuffer not supported on VirtualDisplaySurface");
+    return INVALID_OPERATION;
+}
+
 void VirtualDisplaySurface::updateQueueBufferOutput(
         const QueueBufferOutput& qbo) {
     uint32_t w, h, transformHint, numPendingBuffers;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 818d3eb..bd1cfb1 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -126,6 +126,8 @@
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence) override;
 
     //
     // Utility methods
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index f760200..dd88adb 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -44,8 +44,9 @@
     return;
 }
 
-EventThread::EventThread(const sp<VSyncSource>& src)
+EventThread::EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger)
     : mVSyncSource(src),
+      mFlinger(flinger),
       mUseSoftwareVSync(false),
       mVsyncEnabled(false),
       mDebugVsyncEnabled(false),
@@ -126,6 +127,9 @@
 void EventThread::requestNextVsync(
         const sp<EventThread::Connection>& connection) {
     Mutex::Autolock _l(mLock);
+
+    mFlinger.resyncWithRateLimit();
+
     if (connection->count < 0) {
         connection->count = 0;
         mCondition.broadcast();
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 9ba179a..34654fa 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -77,7 +77,7 @@
 
 public:
 
-    EventThread(const sp<VSyncSource>& src);
+    EventThread(const sp<VSyncSource>& src, SurfaceFlinger& flinger);
 
     sp<Connection> createEventConnection() const;
     status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -116,6 +116,7 @@
     // constants
     sp<VSyncSource> mVSyncSource;
     PowerHAL mPowerHAL;
+    SurfaceFlinger& mFlinger;
 
     mutable Mutex mLock;
     mutable Condition mCondition;
diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
index 7da1d93..885d712 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include <inttypes.h>
 #include "FenceTracker.h"
 #include "Layer.h"
+#include <utils/Trace.h>
 
 namespace android {
 
@@ -78,6 +81,7 @@
 }
 
 void FenceTracker::checkFencesForCompletion() {
+    ATRACE_CALL();
     for (auto& frame : mFrames) {
         if (frame.retireFence != Fence::NO_FENCE) {
             nsecs_t time = frame.retireFence->getSignalTime();
@@ -115,6 +119,7 @@
 
 void FenceTracker::addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
         const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence) {
+    ATRACE_CALL();
     Mutex::Autolock lock(mMutex);
     FrameRecord& frame = mFrames[mOffset];
     FrameRecord& prevFrame = mFrames[(mOffset + MAX_FRAME_HISTORY - 1) %
diff --git a/services/surfaceflinger/GpuService.cpp b/services/surfaceflinger/GpuService.cpp
new file mode 100644
index 0000000..0c29971
--- /dev/null
+++ b/services/surfaceflinger/GpuService.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2016 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 "GpuService.h"
+
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <vkjson.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class BpGpuService : public BpInterface<IGpuService>
+{
+public:
+    BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {}
+};
+
+IMPLEMENT_META_INTERFACE(GpuService, "android.ui.IGpuService");
+
+status_t BnGpuService::onTransact(uint32_t code, const Parcel& data,
+        Parcel* reply, uint32_t flags)
+{
+    switch (code) {
+    case SHELL_COMMAND_TRANSACTION: {
+        int in = data.readFileDescriptor();
+        int out = data.readFileDescriptor();
+        int err = data.readFileDescriptor();
+        int argc = data.readInt32();
+        Vector<String16> args;
+        for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+           args.add(data.readString16());
+        }
+        return shellCommand(in, out, err, args);
+    }
+
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+namespace {
+    status_t cmd_help(int out);
+    status_t cmd_vkjson(int out, int err);
+}
+
+const char* const GpuService::SERVICE_NAME = "gpu";
+
+GpuService::GpuService() {}
+
+status_t GpuService::shellCommand(int /*in*/, int out, int err,
+        Vector<String16>& args)
+{
+    ALOGV("GpuService::shellCommand");
+    for (size_t i = 0, n = args.size(); i < n; i++)
+        ALOGV("  arg[%zu]: '%s'", i, String8(args[i]).string());
+
+    if (args[0] == String16("vkjson"))
+        return cmd_vkjson(out, err);
+    else if (args[0] == String16("help"))
+        return cmd_help(out);
+
+    return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+namespace {
+
+status_t cmd_help(int out) {
+    FILE* outs = fdopen(out, "w");
+    if (!outs) {
+        ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno),
+            errno);
+        return BAD_VALUE;
+    }
+    fprintf(outs,
+        "GPU Service commands:\n"
+        "  vkjson   dump Vulkan device capabilities as JSON\n");
+    fclose(outs);
+    return NO_ERROR;
+}
+
+VkResult vkjsonPrint(FILE* out, FILE* err) {
+    VkResult result;
+
+    const VkApplicationInfo app_info = {
+        VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr,
+        "vkjson", 1,    /* app name, version */
+        "", 0,          /* engine name, version */
+        VK_API_VERSION
+    };
+    const VkInstanceCreateInfo instance_info = {
+        VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr,
+        0,              /* flags */
+        &app_info,
+        0, nullptr,     /* layers */
+        0, nullptr,     /* extensions */
+    };
+    VkInstance instance;
+    result = vkCreateInstance(&instance_info, nullptr, &instance);
+    if (result != VK_SUCCESS) {
+        fprintf(err, "vkCreateInstance failed: %d\n", result);
+        return result;
+    }
+
+    uint32_t ngpu = 0;
+    result = vkEnumeratePhysicalDevices(instance, &ngpu, nullptr);
+    if (result != VK_SUCCESS) {
+        fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
+        return result;
+    }
+    std::vector<VkPhysicalDevice> gpus(ngpu, VK_NULL_HANDLE);
+    result = vkEnumeratePhysicalDevices(instance, &ngpu, gpus.data());
+    if (result != VK_SUCCESS) {
+        fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
+        return result;
+    }
+
+    for (size_t i = 0, n = gpus.size(); i < n; i++) {
+        auto props = VkJsonGetAllProperties(gpus[i]);
+        std::string json = VkJsonAllPropertiesToJson(props);
+        fwrite(json.data(), 1, json.size(), out);
+        if (i < n - 1)
+            fputc(',', out);
+        fputc('\n', out);
+    }
+
+    vkDestroyInstance(instance, nullptr);
+
+    return VK_SUCCESS;
+}
+
+status_t cmd_vkjson(int out, int err) {
+    int errnum;
+    FILE* outs = fdopen(out, "w");
+    if (!outs) {
+        errnum = errno;
+        ALOGE("vkjson: failed to create output stream: %s", strerror(errnum));
+        return -errnum;
+    }
+    FILE* errs = fdopen(err, "w");
+    if (!errs) {
+        errnum = errno;
+        ALOGE("vkjson: failed to create error stream: %s", strerror(errnum));
+        fclose(outs);
+        return -errnum;
+    }
+    fprintf(outs, "[\n");
+    VkResult result = vkjsonPrint(outs, errs);
+    fprintf(outs, "]\n");
+    fclose(errs);
+    fclose(outs);
+    return result >= 0 ? NO_ERROR : UNKNOWN_ERROR;
+}
+
+} // anonymous namespace
+
+} // namespace android
diff --git a/services/surfaceflinger/GpuService.h b/services/surfaceflinger/GpuService.h
new file mode 100644
index 0000000..b8c28d2
--- /dev/null
+++ b/services/surfaceflinger/GpuService.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 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_GPUSERVICE_H
+#define ANDROID_GPUSERVICE_H
+
+#include <binder/IInterface.h>
+#include <cutils/compiler.h>
+
+namespace android {
+
+/*
+ * This class defines the Binder IPC interface for GPU-related queries and
+ * control.
+ */
+class IGpuService : public IInterface {
+public:
+    DECLARE_META_INTERFACE(GpuService);
+};
+
+class BnGpuService: public BnInterface<IGpuService> {
+protected:
+    virtual status_t shellCommand(int in, int out, int err,
+        Vector<String16>& args) = 0;
+
+    virtual status_t onTransact(uint32_t code, const Parcel& data,
+            Parcel* reply, uint32_t flags = 0) override;
+};
+
+class GpuService : public BnGpuService
+{
+public:
+    static const char* const SERVICE_NAME ANDROID_API;
+
+    GpuService() ANDROID_API;
+
+protected:
+    virtual status_t shellCommand(int in, int out, int err,
+        Vector<String16>& args) override;
+};
+
+} // namespace android
+
+#endif // ANDROID_GPUSERVICE_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a2c0462..c640f58 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -75,6 +75,7 @@
         mSidebandStreamChanged(false),
         mCurrentTransform(0),
         mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mOverrideScalingMode(-1),
         mCurrentOpacity(true),
         mCurrentFrameNumber(0),
         mRefreshPending(false),
@@ -250,7 +251,7 @@
             ALOGE("Can't replace a frame on an empty queue");
             return;
         }
-        mQueueItems.editItemAt(0) = item;
+        mQueueItems.editItemAt(mQueueItems.size() - 1) = item;
 
         // Wake up any pending callbacks
         mLastFrameNumberReceived = item.mFrameNumber;
@@ -435,9 +436,11 @@
     uint32_t invTransform = mCurrentTransform;
     if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
         /*
-         * the code below applies the display's inverse transform to the buffer
+         * the code below applies the primary display's inverse transform to the
+         * buffer
          */
-        uint32_t invTransformOrient = hw->getOrientationTransform();
+        uint32_t invTransformOrient =
+                DisplayDevice::getPrimaryDisplayOrientationTransform();
         // calculate the inverse transform
         if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) {
             invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
@@ -634,13 +637,12 @@
 
     if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
         /*
-         * the code below applies the display's inverse transform to the buffer
+         * the code below applies the primary display's inverse transform to the
+         * buffer
          */
-#ifdef USE_HWC2
-        uint32_t invTransform = displayDevice->getOrientationTransform();
-#else
-        uint32_t invTransform = hw->getOrientationTransform();
-#endif
+        uint32_t invTransform =
+                DisplayDevice::getPrimaryDisplayOrientationTransform();
+
         uint32_t t_orientation = transform.getOrientation();
         // calculate the inverse transform
         if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
@@ -941,7 +943,8 @@
         if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
 
             /*
-             * the code below applies the display's inverse transform to the texture transform
+             * the code below applies the primary display's inverse transform to
+             * the texture transform
              */
 
             // create a 4x4 transform matrix from the display transform flags
@@ -950,7 +953,8 @@
             const mat4 rot90( 0,1,0,0, -1,0,0,0, 0,0,1,0, 1,0,0,1);
 
             mat4 tr;
-            uint32_t transform = hw->getOrientationTransform();
+            uint32_t transform =
+                    DisplayDevice::getPrimaryDisplayOrientationTransform();
             if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90)
                 tr = tr * rot90;
             if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H)
@@ -1244,7 +1248,7 @@
 }
 
 bool Layer::isFixedSize() const {
-    return mCurrentScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+    return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE;
 }
 
 bool Layer::isCropped() const {
@@ -1309,16 +1313,16 @@
     mPendingStates.push_back(mCurrentState);
 }
 
-void Layer::popPendingState() {
-    auto oldFlags = mCurrentState.flags;
-    mCurrentState = mPendingStates[0];
-    mCurrentState.flags = (oldFlags & ~mCurrentState.mask) |
-            (mCurrentState.flags & mCurrentState.mask);
+void Layer::popPendingState(State* stateToCommit) {
+    auto oldFlags = stateToCommit->flags;
+    *stateToCommit = mPendingStates[0];
+    stateToCommit->flags = (oldFlags & ~stateToCommit->mask) |
+            (stateToCommit->flags & stateToCommit->mask);
 
     mPendingStates.removeAt(0);
 }
 
-bool Layer::applyPendingStates() {
+bool Layer::applyPendingStates(State* stateToCommit) {
     bool stateUpdateAvailable = false;
     while (!mPendingStates.empty()) {
         if (mPendingStates[0].handle != nullptr) {
@@ -1327,7 +1331,7 @@
                 // will be visually wrong, but it should keep us from getting
                 // into too much trouble.
                 ALOGE("[%s] No local sync point found", mName.string());
-                popPendingState();
+                popPendingState(stateToCommit);
                 stateUpdateAvailable = true;
                 continue;
             }
@@ -1345,7 +1349,7 @@
 
             if (mRemoteSyncPoints.front()->frameIsAvailable()) {
                 // Apply the state update
-                popPendingState();
+                popPendingState(stateToCommit);
                 stateUpdateAvailable = true;
 
                 // Signal our end of the sync point and then dispose of it
@@ -1355,7 +1359,7 @@
                 break;
             }
         } else {
-            popPendingState();
+            popPendingState(stateToCommit);
             stateUpdateAvailable = true;
         }
     }
@@ -1385,12 +1389,12 @@
     ATRACE_CALL();
 
     pushPendingState();
-    if (!applyPendingStates()) {
+    Layer::State c = getCurrentState();
+    if (!applyPendingStates(&c)) {
         return 0;
     }
 
     const Layer::State& s(getDrawingState());
-    const Layer::State& c(getCurrentState());
 
     const bool sizeChanged = (c.requested.w != s.requested.w) ||
                              (c.requested.h != s.requested.h);
@@ -1403,7 +1407,8 @@
                 "            requested={ wh={%4u,%4u} }}\n"
                 "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
                 "            requested={ wh={%4u,%4u} }}\n",
-                this, getName().string(), mCurrentTransform, mCurrentScalingMode,
+                this, getName().string(), mCurrentTransform,
+                getEffectiveScalingMode(),
                 c.active.w, c.active.h,
                 c.crop.left,
                 c.crop.top,
@@ -1454,8 +1459,7 @@
     // this is used by Layer, which special cases resizes.
     if (flags & eDontUpdateGeometryState)  {
     } else {
-        Layer::State& editCurrentState(getCurrentState());
-        editCurrentState.active = c.requested;
+        c.active = c.requested;
     }
 
     if (s.active != c.active) {
@@ -1475,12 +1479,12 @@
     }
 
     // Commit the transaction
-    commitTransaction();
+    commitTransaction(c);
     return flags;
 }
 
-void Layer::commitTransaction() {
-    mDrawingState = mCurrentState;
+void Layer::commitTransaction(const State& stateToCommit) {
+    mDrawingState = stateToCommit;
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
@@ -1581,6 +1585,20 @@
     return true;
 }
 
+bool Layer::setOverrideScalingMode(int32_t scalingMode) {
+    if (scalingMode == mOverrideScalingMode)
+        return false;
+    mOverrideScalingMode = scalingMode;
+    return true;
+}
+
+uint32_t Layer::getEffectiveScalingMode() const {
+    if (mOverrideScalingMode >= 0) {
+      return mOverrideScalingMode;
+    }
+    return mCurrentScalingMode;
+}
+
 bool Layer::setLayerStack(uint32_t layerStack) {
     if (mCurrentState.layerStack == layerStack)
         return false;
@@ -1739,14 +1757,17 @@
             bool& recomputeVisibleRegions;
             bool stickyTransformSet;
             const char* name;
+            int32_t overrideScalingMode;
 
             Reject(Layer::State& front, Layer::State& current,
                     bool& recomputeVisibleRegions, bool stickySet,
-                    const char* name)
+                    const char* name,
+                    int32_t overrideScalingMode)
                 : front(front), current(current),
                   recomputeVisibleRegions(recomputeVisibleRegions),
                   stickyTransformSet(stickySet),
-                  name(name) {
+                  name(name),
+                  overrideScalingMode(overrideScalingMode) {
             }
 
             virtual bool reject(const sp<GraphicBuffer>& buf,
@@ -1764,7 +1785,9 @@
                     swap(bufWidth, bufHeight);
                 }
 
-                bool isFixedSize = item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+                int actualScalingMode = overrideScalingMode >= 0 ?
+                        overrideScalingMode : item.mScalingMode;
+                bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
                 if (front.active != front.requested) {
 
                     if (isFixedSize ||
@@ -1840,7 +1863,8 @@
         };
 
         Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                getProducerStickyTransform() != 0, mName.string());
+                getProducerStickyTransform() != 0, mName.string(),
+                mOverrideScalingMode);
 
 
         // Check all of our local sync points to ensure that all transactions
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1d73b43..7d085a4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -160,6 +160,7 @@
     bool setFinalCrop(const Rect& crop);
     bool setLayerStack(uint32_t layerStack);
     void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
+    bool setOverrideScalingMode(int32_t overrideScalingMode);
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
@@ -431,7 +432,7 @@
     virtual void onFrameReplaced(const BufferItem& item) override;
     virtual void onSidebandStreamChanged() override;
 
-    void commitTransaction();
+    void commitTransaction(const State& stateToCommit);
 
     // needsLinearFiltering - true if this surface's state requires filtering
     bool needsFiltering(const sp<const DisplayDevice>& hw) const;
@@ -500,8 +501,13 @@
     bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
 
     void pushPendingState();
-    void popPendingState();
-    bool applyPendingStates();
+    void popPendingState(State* stateToCommit);
+    bool applyPendingStates(State* stateToCommit);
+
+    // Returns mCurrentScaling mode (originating from the
+    // Client) or mOverrideScalingMode mode (originating from
+    // the Surface Controller) if set.
+    uint32_t getEffectiveScalingMode() const;
 public:
     void notifyAvailableFrames();
 private:
@@ -536,6 +542,8 @@
     Rect mCurrentCrop;
     uint32_t mCurrentTransform;
     uint32_t mCurrentScalingMode;
+    // We encode unset as -1.
+    int32_t mOverrideScalingMode;
     bool mCurrentOpacity;
     std::atomic<uint64_t> mCurrentFrameNumber;
     bool mRefreshPending;
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 75559da..e689127 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -143,6 +143,11 @@
     return mProducer->setDequeueTimeout(timeout);
 }
 
+status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+        sp<Fence>* outFence) {
+    return mProducer->getLastQueuedBuffer(outBuffer, outFence);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return IInterface::asBinder(mProducer).get();
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 441cf50..d2b4b43 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -59,6 +59,8 @@
     virtual String8 getConsumerName() const override;
     virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
+            sp<Fence>* outFence) override;
     virtual IBinder* onAsBinder();
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9b43849..a69b11b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -149,6 +149,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
         mDaltonize(false),
@@ -331,11 +332,12 @@
 class DispSyncSource : public VSyncSource, private DispSync::Callback {
 public:
     DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
-        const char* label) :
+        const char* name) :
+            mName(name),
             mValue(0),
             mTraceVsync(traceVsync),
-            mVsyncOnLabel(String8::format("VsyncOn-%s", label)),
-            mVsyncEventLabel(String8::format("VSYNC-%s", label)),
+            mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
+            mVsyncEventLabel(String8::format("VSYNC-%s", name)),
             mDispSync(dispSync),
             mCallbackMutex(),
             mCallback(),
@@ -348,7 +350,7 @@
     virtual void setVSyncEnabled(bool enable) {
         Mutex::Autolock lock(mVsyncMutex);
         if (enable) {
-            status_t err = mDispSync->addEventListener(mPhaseOffset,
+            status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                     static_cast<DispSync::Callback*>(this));
             if (err != NO_ERROR) {
                 ALOGE("error registering vsync callback: %s (%d)",
@@ -399,7 +401,7 @@
         }
 
         // Add a listener with the new offset
-        err = mDispSync->addEventListener(mPhaseOffset,
+        err = mDispSync->addEventListener(mName, mPhaseOffset,
                 static_cast<DispSync::Callback*>(this));
         if (err != NO_ERROR) {
             ALOGE("error registering vsync callback: %s (%d)",
@@ -425,6 +427,8 @@
         }
     }
 
+    const char* const mName;
+
     int mValue;
 
     const bool mTraceVsync;
@@ -455,10 +459,10 @@
         // start the EventThread
         sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
                 vsyncPhaseOffsetNs, true, "app");
-        mEventThread = new EventThread(vsyncSrc);
+        mEventThread = new EventThread(vsyncSrc, *this);
         sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
                 sfVsyncPhaseOffsetNs, true, "sf");
-        mSFEventThread = new EventThread(sfVsyncSrc);
+        mSFEventThread = new EventThread(sfVsyncSrc, *this);
         mEventQueue.setEventThread(mSFEventThread);
 
         // Get a RenderEngine for the given display / config (can't fail)
@@ -715,6 +719,27 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& display,
+        HdrCapabilities* outCapabilities) const {
+    Mutex::Autolock _l(mStateLock);
+
+    sp<const DisplayDevice> displayDevice(getDisplayDevice(display));
+    if (displayDevice == nullptr) {
+        ALOGE("getHdrCapabilities: Invalid display %p", displayDevice.get());
+        return BAD_VALUE;
+    }
+
+    std::unique_ptr<HdrCapabilities> capabilities =
+            mHwc->getHdrCapabilities(displayDevice->getHwcDisplayId());
+    if (capabilities) {
+        std::swap(*outCapabilities, *capabilities);
+    } else {
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -775,7 +800,8 @@
     if (makeAvailable) {
         mHWVsyncAvailable = true;
     } else if (!mHWVsyncAvailable) {
-        ALOGE("resyncToHardwareVsync called when HW vsync unavailable");
+        // Hardware vsync is not currently available, so abort the resync
+        // attempt for now
         return;
     }
 
@@ -806,6 +832,13 @@
     }
 }
 
+void SurfaceFlinger::resyncWithRateLimit() {
+    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
+    if (systemTime() - mLastSwapTime > kIgnoreDelay) {
+        resyncToHardwareVsync(false);
+    }
+}
+
 void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) {
     bool needsHwVsync = false;
 
@@ -910,7 +943,11 @@
 void SurfaceFlinger::handleMessageRefresh() {
     ATRACE_CALL();
 
+#ifdef ENABLE_FENCE_TRACKING
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#else
+    nsecs_t refreshStartTime = 0;
+#endif
     static nsecs_t previousExpectedPresent = 0;
     nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
     static bool previousFrameMissed = false;
@@ -1000,7 +1037,11 @@
     }
 }
 
+#ifdef ENABLE_FENCE_TRACKING
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+#else
+void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
+#endif
 {
     ATRACE_CALL();
     ALOGV("postComposition");
@@ -1028,8 +1069,10 @@
         }
     }
 
+#ifdef ENABLE_FENCE_TRACKING
     mFenceTracker.addFrame(refreshStartTime, presentFence,
             hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
+#endif
 
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
@@ -1238,6 +1281,7 @@
         if (hwcId >= 0) {
             mHwc->commit(hwcId);
         }
+        displayDevice->onSwapBuffersCompleted();
         if (displayId == 0) {
             // Make the default display current because the VirtualDisplayDevice
             // code cannot deal with dequeueBuffer() being called outside of the
@@ -1245,7 +1289,6 @@
             // is allowed to (and does in some case) call dequeueBuffer().
             displayDevice->makeCurrent(mEGLDisplay, mEGLContext);
         }
-        displayDevice->onSwapBuffersCompleted();
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
             if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) {
@@ -2260,6 +2303,11 @@
             // We don't trigger a traversal here because if no other state is
             // changed, we don't want this to cause any more work
         }
+        if (what & layer_state_t::eOverrideScalingModeChanged) {
+            layer->setOverrideScalingMode(s.overrideScalingMode);
+            // We don't trigger a traversal here because if no other state is
+            // changed, we don't want this to cause any more work
+        }
     }
     return flags;
 }
@@ -2548,12 +2596,14 @@
                 dumpAll = false;
             }
 
+#ifdef ENABLE_FENCE_TRACKING
             if ((index < numArgs) &&
                     (args[index] == String16("--fences"))) {
                 index++;
                 mFenceTracker.dump(&result);
                 dumpAll = false;
             }
+#endif
         }
 
         if (dumpAll) {
@@ -2869,6 +2919,7 @@
         case CLEAR_ANIMATION_FRAME_STATS:
         case GET_ANIMATION_FRAME_STATS:
         case SET_POWER_MODE:
+        case GET_HDR_CAPABILITIES:
         {
             // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 37110b9..633e956 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -222,6 +222,8 @@
     virtual status_t setActiveConfig(const sp<IBinder>& display, int id);
     virtual status_t clearAnimationFrameStats();
     virtual status_t getAnimationFrameStats(FrameStats* outStats) const;
+    virtual status_t getHdrCapabilities(const sp<IBinder>& display,
+            HdrCapabilities* outCapabilities) const;
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
@@ -407,8 +409,11 @@
      * VSync
      */
      void enableHardwareVsync();
-     void disableHardwareVsync(bool makeUnavailable);
      void resyncToHardwareVsync(bool makeAvailable);
+     void disableHardwareVsync(bool makeUnavailable);
+public:
+     void resyncWithRateLimit();
+private:
 
     /* ------------------------------------------------------------------------
      * Debugging & dumpsys
@@ -520,7 +525,7 @@
     static const size_t NUM_BUCKETS = 8; // < 1-7, 7+
     nsecs_t mFrameBuckets[NUM_BUCKETS];
     nsecs_t mTotalTime;
-    nsecs_t mLastSwapTime;
+    std::atomic<nsecs_t> mLastSwapTime;
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 11f19d0..00700ab 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -45,6 +45,7 @@
 #include <gui/GraphicBufferAlloc.h>
 
 #include <ui/GraphicBufferAllocator.h>
+#include <ui/HdrCapabilities.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
 
@@ -147,6 +148,7 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
+        mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
         mDaltonize(false),
@@ -328,11 +330,12 @@
 class DispSyncSource : public VSyncSource, private DispSync::Callback {
 public:
     DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
-        const char* label) :
+        const char* name) :
+            mName(name),
             mValue(0),
             mTraceVsync(traceVsync),
-            mVsyncOnLabel(String8::format("VsyncOn-%s", label)),
-            mVsyncEventLabel(String8::format("VSYNC-%s", label)),
+            mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
+            mVsyncEventLabel(String8::format("VSYNC-%s", name)),
             mDispSync(dispSync),
             mCallbackMutex(),
             mCallback(),
@@ -345,7 +348,7 @@
     virtual void setVSyncEnabled(bool enable) {
         Mutex::Autolock lock(mVsyncMutex);
         if (enable) {
-            status_t err = mDispSync->addEventListener(mPhaseOffset,
+            status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                     static_cast<DispSync::Callback*>(this));
             if (err != NO_ERROR) {
                 ALOGE("error registering vsync callback: %s (%d)",
@@ -396,7 +399,7 @@
         }
 
         // Add a listener with the new offset
-        err = mDispSync->addEventListener(mPhaseOffset,
+        err = mDispSync->addEventListener(mName, mPhaseOffset,
                 static_cast<DispSync::Callback*>(this));
         if (err != NO_ERROR) {
             ALOGE("error registering vsync callback: %s (%d)",
@@ -422,6 +425,8 @@
         }
     }
 
+    const char* const mName;
+
     int mValue;
 
     const bool mTraceVsync;
@@ -451,10 +456,10 @@
     // start the EventThread
     sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
             vsyncPhaseOffsetNs, true, "app");
-    mEventThread = new EventThread(vsyncSrc);
+    mEventThread = new EventThread(vsyncSrc, *this);
     sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
             sfVsyncPhaseOffsetNs, true, "sf");
-    mSFEventThread = new EventThread(sfVsyncSrc);
+    mSFEventThread = new EventThread(sfVsyncSrc, *this);
     mEventQueue.setEventThread(mSFEventThread);
 
     // Initialize the H/W composer object.  There may or may not be an
@@ -748,6 +753,13 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& /*display*/,
+        HdrCapabilities* outCapabilities) const {
+    // HWC1 does not provide HDR capabilities
+    *outCapabilities = HdrCapabilities();
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
@@ -808,7 +820,8 @@
     if (makeAvailable) {
         mHWVsyncAvailable = true;
     } else if (!mHWVsyncAvailable) {
-        ALOGE("resyncToHardwareVsync called when HW vsync unavailable");
+        // Hardware vsync is not currently available, so abort the resync
+        // attempt for now
         return;
     }
 
@@ -839,6 +852,13 @@
     }
 }
 
+void SurfaceFlinger::resyncWithRateLimit() {
+    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
+    if (systemTime() - mLastSwapTime > kIgnoreDelay) {
+        resyncToHardwareVsync(false);
+    }
+}
+
 void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
     bool needsHwVsync = false;
 
@@ -927,7 +947,11 @@
 void SurfaceFlinger::handleMessageRefresh() {
     ATRACE_CALL();
 
+#ifdef ENABLE_FENCE_TRACKING
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#else
+    nsecs_t refreshStartTime = 0;
+#endif
     static nsecs_t previousExpectedPresent = 0;
     nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
     static bool previousFrameMissed = false;
@@ -1009,7 +1033,11 @@
     }
 }
 
+#ifdef ENABLE_FENCE_TRACKING
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
+#else
+void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
+#endif
 {
     const LayerVector& layers(mDrawingState.layersSortedByZ);
     const size_t count = layers.size();
@@ -1035,8 +1063,10 @@
         }
     }
 
+#ifdef ENABLE_FENCE_TRACKING
     mFenceTracker.addFrame(refreshStartTime, presentFence,
             hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
+#endif
 
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
@@ -2298,6 +2328,11 @@
             // We don't trigger a traversal here because if no other state is
             // changed, we don't want this to cause any more work
         }
+        if (what & layer_state_t::eOverrideScalingModeChanged) {
+            layer->setOverrideScalingMode(s.overrideScalingMode);
+            // We don't trigger a traversal here because if no other state is
+            // changed, we don't want this to cause any more work
+        }
     }
     return flags;
 }
@@ -2586,12 +2621,14 @@
                 dumpAll = false;
             }
 
+#ifdef ENABLE_FENCE_TRACKING
             if ((index < numArgs) &&
                     (args[index] == String16("--fences"))) {
                 index++;
                 mFenceTracker.dump(&result);
                 dumpAll = false;
             }
+#endif
         }
 
         if (dumpAll) {
@@ -2905,6 +2942,7 @@
         case CLEAR_ANIMATION_FRAME_STATS:
         case GET_ANIMATION_FRAME_STATS:
         case SET_POWER_MODE:
+        case GET_HDR_CAPABILITIES:
         {
             // codes that require permission check
             IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 4cd7aeb..97a1e8b 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -21,6 +21,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
+#include "GpuService.h"
 #include "SurfaceFlinger.h"
 
 using namespace android;
@@ -56,7 +57,11 @@
     sp<IServiceManager> sm(defaultServiceManager());
     sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
 
-    // run in this thread
+    // publish GpuService
+    sp<GpuService> gpuservice = new GpuService();
+    sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);
+
+    // run surface flinger in this thread
     flinger->run();
 
     return 0;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index d7cb899..320fddb 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -459,4 +459,61 @@
     }
 }
 
+TEST_F(LayerUpdateTest, DeferredTransactionTest) {
+    sp<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before anything");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 32,  32,  63,  63, 195);
+        sc->checkPixel( 96,  96, 195,  63,  63);
+        sc->checkPixel(160, 160,  63,  63, 195);
+    }
+
+    // set up two deferred transactions on different frames
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75));
+    mFGSurfaceControl->deferTransactionUntil(mSyncSurfaceControl->getHandle(),
+            mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128,128));
+    mFGSurfaceControl->deferTransactionUntil(mSyncSurfaceControl->getHandle(),
+            mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    {
+        SCOPED_TRACE("before any trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 32,  32,  63,  63, 195);
+        sc->checkPixel( 96,  96, 195,  63,  63);
+        sc->checkPixel(160, 160,  63,  63, 195);
+    }
+
+    // should trigger the first deferred transaction, but not the second one
+    fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    {
+        SCOPED_TRACE("after first trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 32,  32,  63,  63, 195);
+        sc->checkPixel( 96,  96, 162,  63,  96);
+        sc->checkPixel(160, 160,  63,  63, 195);
+    }
+
+    // should show up immediately since it's not deferred
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+
+    // trigger the second deferred transaction
+    fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    {
+        SCOPED_TRACE("after second trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 32,  32,  63,  63, 195);
+        sc->checkPixel( 96,  96,  63,  63, 195);
+        sc->checkPixel(160, 160, 195,  63,  63);
+    }
+}
+
 }
diff --git a/vulkan/README.md b/vulkan/README.md
index 44747fd..9fba728 100644
--- a/vulkan/README.md
+++ b/vulkan/README.md
@@ -22,7 +22,7 @@
 - You should now have `$GOPATH/bin/apic`. You might want to add `$GOPATH/bin` to your `$PATH`.
 
 ### Generating code
-To generate `libvulkan/dispatch_gen.*`,
+To generate `libvulkan/*_gen.*`,
 - `$ cd libvulkan`
-- `$ apic template ../api/vulkan.api dispatch.tmpl`
-Similar for `nulldrv/null_driver_gen.*`.
\ No newline at end of file
+- `$ apic template ../api/vulkan.api code-generator.tmpl`
+Similar for `nulldrv/null_driver_gen.*`.
diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api
index 980722d..7aa19e7 100644
--- a/vulkan/api/platform.api
+++ b/vulkan/api/platform.api
@@ -43,6 +43,7 @@
 
 // VK_USE_PLATFORM_ANDROID_KHR
 @internal class ANativeWindow {}
+@internal type void* buffer_handle_t
 
 // VK_USE_PLATFORM_WIN32_KHR
 @internal type void* HINSTANCE
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 10565ab..ae690a3 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -75,6 +75,9 @@
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION     5
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
 
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     5
+@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME             "VK_ANDROID_native_buffer"
+
 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       1
 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME               "VK_EXT_debug_report"
 
@@ -646,6 +649,9 @@
     //@extension("VK_KHR_win32_surface")
     VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR             = 1000009000,
 
+    //@extension("VK_ANDROID_native_buffer")
+    VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID                     = 1000010000,
+
     //@extension("VK_EXT_debug_report")
     VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT              = 1000011000,
 }
@@ -2591,6 +2597,16 @@
     platform.HWND                               hwnd
 }
 
+@extension("VK_ANDROID_native_buffer")
+class VkNativeBufferANDROID {
+    VkStructureType                             sType
+    const void*                                 pNext
+    platform.buffer_handle_t                    handle
+    int                                         stride
+    int                                         format
+    int                                         usage
+}
+
 @extension("VK_EXT_debug_report")
 class VkDebugReportCallbackCreateInfoEXT {
     VkStructureType                             sType
@@ -5134,6 +5150,35 @@
     return ?
 }
 
+@extension("VK_ANDROID_native_buffer")
+cmd VkResult vkGetSwapchainGrallocUsageANDROID(
+        VkDevice                                device,
+        VkFormat                                format,
+        VkImageUsageFlags                       imageUsage,
+        int*                                    grallocUsage) {
+    return ?
+}
+
+@extension("VK_ANDROID_native_buffer")
+cmd VkResult vkAcquireImageANDROID(
+        VkDevice                                device,
+        VkImage                                 image,
+        int                                     nativeFenceFd,
+        VkSemaphore                             semaphore,
+        VkFence                                 fence) {
+    return ?
+}
+
+@extension("VK_ANDROID_native_buffer")
+cmd VkResult vkQueueSignalReleaseImageANDROID(
+        VkQueue                                 queue,
+        u32                                     waitSemaphoreCount,
+        const VkSemaphore*                      pWaitSemaphores,
+        VkImage                                 image,
+        int*                                    pNativeFenceFd) {
+    return ?
+}
+
 @extension("VK_EXT_debug_report")
 @external type void* PFN_vkDebugReportCallbackEXT
 @extension("VK_EXT_debug_report")
diff --git a/vulkan/include/vulkan/vk_layer_interface.h b/vulkan/include/vulkan/vk_layer_interface.h
index 5aae51e..f2a5232 100644
--- a/vulkan/include/vulkan/vk_layer_interface.h
+++ b/vulkan/include/vulkan/vk_layer_interface.h
@@ -14,10 +14,6 @@
  * The above copyright notice(s) and this permission notice shall be included in
  * all copies or substantial portions of the Materials.
  *
- * The Materials are Confidential Information as defined by the Khronos
- * Membership Agreement until designated non-confidential by Khronos, at which
- * point this condition clause shall be removed.
- *
  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -37,38 +33,18 @@
 
 typedef enum VkLayerFunction_ {
     VK_LAYER_FUNCTION_LINK = 0,
-    VK_LAYER_FUNCTION_DEVICE = 1,
-    VK_LAYER_FUNCTION_INSTANCE = 2
+    VK_LAYER_FUNCTION_DATA_CALLBACK = 1
 } VkLayerFunction;
 
-/*
- * When creating the device chain the loader needs to pass
- * down information about it's device structure needed at
- * the end of the chain. Passing the data via the
- * VkLayerInstanceInfo avoids issues with finding the
- * exact instance being used.
- */
-typedef struct VkLayerInstanceInfo_ {
-    void* instance_info;
-    PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
-} VkLayerInstanceInfo;
-
 typedef struct VkLayerInstanceLink_ {
     struct VkLayerInstanceLink_* pNext;
     PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
 } VkLayerInstanceLink;
 
-/*
- * When creating the device chain the loader needs to pass
- * down information about it's device structure needed at
- * the end of the chain. Passing the data via the
- * VkLayerDeviceInfo avoids issues with finding the
- * exact instance being used.
- */
-typedef struct VkLayerDeviceInfo_ {
-    void* device_info;
-    PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr;
-} VkLayerDeviceInfo;
+typedef VkResult(VKAPI_PTR* PFN_vkSetInstanceLoaderData)(VkInstance instance,
+                                                         void* object);
+typedef VkResult(VKAPI_PTR* PFN_vkSetDeviceLoaderData)(VkDevice device,
+                                                       void* object);
 
 typedef struct {
     VkStructureType sType;  // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
@@ -76,7 +52,7 @@
     VkLayerFunction function;
     union {
         VkLayerInstanceLink* pLayerInfo;
-        VkLayerInstanceInfo instanceInfo;
+        PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData;
     } u;
 } VkLayerInstanceCreateInfo;
 
@@ -92,6 +68,6 @@
     VkLayerFunction function;
     union {
         VkLayerDeviceLink* pLayerInfo;
-        VkLayerDeviceInfo deviceInfo;
+        PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData;
     } u;
 } VkLayerDeviceCreateInfo;
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
index 85b8ab2..71788a9 100644
--- a/vulkan/libvulkan/Android.mk
+++ b/vulkan/libvulkan/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_SANITIZE := integer
 
 LOCAL_CFLAGS := -DLOG_TAG=\"vulkan\" \
+	-DVK_USE_PLATFORM_ANDROID_KHR \
 	-std=c99 -fvisibility=hidden -fstrict-aliasing \
 	-Weverything -Werror \
 	-Wno-padded \
@@ -26,7 +27,6 @@
 	-Wno-undef
 #LOCAL_CFLAGS += -DLOG_NDEBUG=0
 LOCAL_CPPFLAGS := -std=c++14 \
-	-fexceptions \
 	-Wno-c99-extensions \
 	-Wno-c++98-compat-pedantic \
 	-Wno-exit-time-destructors \
@@ -38,10 +38,13 @@
 	system/core/libsync/include
 
 LOCAL_SRC_FILES := \
+	api.cpp \
+	api_gen.cpp \
 	debug_report.cpp \
-	dispatch_gen.cpp \
+	driver.cpp \
+	driver_gen.cpp \
 	layers_extensions.cpp \
-	loader.cpp \
+	stubhal.cpp \
 	swapchain.cpp \
 	vulkan_loader_data.cpp
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
new file mode 100644
index 0000000..e7f10b3
--- /dev/null
+++ b/vulkan/libvulkan/api.cpp
@@ -0,0 +1,1174 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// The API layer of the loader defines Vulkan API and manages layers.  The
+// entrypoints are generated and defined in api_dispatch.cpp.  Most of them
+// simply find the dispatch table and jump.
+//
+// There are a few of them requiring manual code for things such as layer
+// discovery or chaining.  They call into functions defined in this file.
+
+#include <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <mutex>
+#include <new>
+#include <utility>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <vulkan/vk_layer_interface.h>
+#include "api.h"
+#include "driver.h"
+#include "layers_extensions.h"
+
+namespace vulkan {
+namespace api {
+
+namespace {
+
+// Provide overridden layer names when there are implicit layers.  No effect
+// otherwise.
+class OverrideLayerNames {
+   public:
+    OverrideLayerNames(bool is_instance, const VkAllocationCallbacks& allocator)
+        : is_instance_(is_instance),
+          allocator_(allocator),
+          scope_(VK_SYSTEM_ALLOCATION_SCOPE_COMMAND),
+          names_(nullptr),
+          name_count_(0),
+          implicit_layers_() {
+        implicit_layers_.result = VK_SUCCESS;
+    }
+
+    ~OverrideLayerNames() {
+        allocator_.pfnFree(allocator_.pUserData, names_);
+        allocator_.pfnFree(allocator_.pUserData, implicit_layers_.elements);
+        allocator_.pfnFree(allocator_.pUserData, implicit_layers_.name_pool);
+    }
+
+    VkResult Parse(const char* const* names, uint32_t count) {
+        AddImplicitLayers();
+
+        const auto& arr = implicit_layers_;
+        if (arr.result != VK_SUCCESS)
+            return arr.result;
+
+        // no need to override when there is no implicit layer
+        if (!arr.count)
+            return VK_SUCCESS;
+
+        names_ = AllocateNameArray(arr.count + count);
+        if (!names_)
+            return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+        // add implicit layer names
+        for (uint32_t i = 0; i < arr.count; i++)
+            names_[i] = GetImplicitLayerName(i);
+
+        name_count_ = arr.count;
+
+        // add explicit layer names
+        for (uint32_t i = 0; i < count; i++) {
+            // ignore explicit layers that are also implicit
+            if (IsImplicitLayer(names[i]))
+                continue;
+
+            names_[name_count_++] = names[i];
+        }
+
+        return VK_SUCCESS;
+    }
+
+    const char* const* Names() const { return names_; }
+
+    uint32_t Count() const { return name_count_; }
+
+   private:
+    struct ImplicitLayer {
+        int priority;
+        size_t name_offset;
+    };
+
+    struct ImplicitLayerArray {
+        ImplicitLayer* elements;
+        uint32_t max_count;
+        uint32_t count;
+
+        char* name_pool;
+        size_t max_pool_size;
+        size_t pool_size;
+
+        VkResult result;
+    };
+
+    void AddImplicitLayers() {
+        if (!driver::Debuggable())
+            return;
+
+        ParseDebugVulkanLayers();
+        property_list(ParseDebugVulkanLayer, this);
+
+        // sort by priorities
+        auto& arr = implicit_layers_;
+        std::sort(arr.elements, arr.elements + arr.count,
+                  [](const ImplicitLayer& a, const ImplicitLayer& b) {
+                      return (a.priority < b.priority);
+                  });
+    }
+
+    void ParseDebugVulkanLayers() {
+        // debug.vulkan.layers specifies colon-separated layer names
+        char prop[PROPERTY_VALUE_MAX];
+        if (!property_get("debug.vulkan.layers", prop, ""))
+            return;
+
+        // assign negative/high priorities to them
+        int prio = -PROPERTY_VALUE_MAX;
+
+        const char* p = prop;
+        const char* delim;
+        while ((delim = strchr(p, ':'))) {
+            if (delim > p)
+                AddImplicitLayer(prio, p, static_cast<size_t>(delim - p));
+
+            prio++;
+            p = delim + 1;
+        }
+
+        if (p[0] != '\0')
+            AddImplicitLayer(prio, p, strlen(p));
+    }
+
+    static void ParseDebugVulkanLayer(const char* key,
+                                      const char* val,
+                                      void* user_data) {
+        static const char prefix[] = "debug.vulkan.layer.";
+        const size_t prefix_len = sizeof(prefix) - 1;
+
+        if (strncmp(key, prefix, prefix_len) || val[0] == '\0')
+            return;
+        key += prefix_len;
+
+        // debug.vulkan.layer.<priority>
+        int priority = -1;
+        if (key[0] >= '0' && key[0] <= '9')
+            priority = atoi(key);
+
+        if (priority < 0) {
+            ALOGW("Ignored implicit layer %s with invalid priority %s", val,
+                  key);
+            return;
+        }
+
+        OverrideLayerNames& override_layers =
+            *reinterpret_cast<OverrideLayerNames*>(user_data);
+        override_layers.AddImplicitLayer(priority, val, strlen(val));
+    }
+
+    void AddImplicitLayer(int priority, const char* name, size_t len) {
+        if (!GrowImplicitLayerArray(1, 0))
+            return;
+
+        auto& arr = implicit_layers_;
+        auto& layer = arr.elements[arr.count++];
+
+        layer.priority = priority;
+        layer.name_offset = AddImplicitLayerName(name, len);
+
+        ALOGV("Added implicit layer %s", GetImplicitLayerName(arr.count - 1));
+    }
+
+    size_t AddImplicitLayerName(const char* name, size_t len) {
+        if (!GrowImplicitLayerArray(0, len + 1))
+            return 0;
+
+        // add the name to the pool
+        auto& arr = implicit_layers_;
+        size_t offset = arr.pool_size;
+        char* dst = arr.name_pool + offset;
+
+        std::copy(name, name + len, dst);
+        dst[len] = '\0';
+
+        arr.pool_size += len + 1;
+
+        return offset;
+    }
+
+    bool GrowImplicitLayerArray(uint32_t layer_count, size_t name_size) {
+        const uint32_t initial_max_count = 16;
+        const size_t initial_max_pool_size = 512;
+
+        auto& arr = implicit_layers_;
+
+        // grow the element array if needed
+        while (arr.count + layer_count > arr.max_count) {
+            uint32_t new_max_count =
+                (arr.max_count) ? (arr.max_count << 1) : initial_max_count;
+            void* new_mem = nullptr;
+
+            if (new_max_count > arr.max_count) {
+                new_mem = allocator_.pfnReallocation(
+                    allocator_.pUserData, arr.elements,
+                    sizeof(ImplicitLayer) * new_max_count,
+                    alignof(ImplicitLayer), scope_);
+            }
+
+            if (!new_mem) {
+                arr.result = VK_ERROR_OUT_OF_HOST_MEMORY;
+                arr.count = 0;
+                return false;
+            }
+
+            arr.elements = reinterpret_cast<ImplicitLayer*>(new_mem);
+            arr.max_count = new_max_count;
+        }
+
+        // grow the name pool if needed
+        while (arr.pool_size + name_size > arr.max_pool_size) {
+            size_t new_max_pool_size = (arr.max_pool_size)
+                                           ? (arr.max_pool_size << 1)
+                                           : initial_max_pool_size;
+            void* new_mem = nullptr;
+
+            if (new_max_pool_size > arr.max_pool_size) {
+                new_mem = allocator_.pfnReallocation(
+                    allocator_.pUserData, arr.name_pool, new_max_pool_size,
+                    alignof(char), scope_);
+            }
+
+            if (!new_mem) {
+                arr.result = VK_ERROR_OUT_OF_HOST_MEMORY;
+                arr.pool_size = 0;
+                return false;
+            }
+
+            arr.name_pool = reinterpret_cast<char*>(new_mem);
+            arr.max_pool_size = new_max_pool_size;
+        }
+
+        return true;
+    }
+
+    const char* GetImplicitLayerName(uint32_t index) const {
+        const auto& arr = implicit_layers_;
+
+        // this may return nullptr when arr.result is not VK_SUCCESS
+        return implicit_layers_.name_pool + arr.elements[index].name_offset;
+    }
+
+    bool IsImplicitLayer(const char* name) const {
+        const auto& arr = implicit_layers_;
+
+        for (uint32_t i = 0; i < arr.count; i++) {
+            if (strcmp(name, GetImplicitLayerName(i)) == 0)
+                return true;
+        }
+
+        return false;
+    }
+
+    const char** AllocateNameArray(uint32_t count) const {
+        return reinterpret_cast<const char**>(allocator_.pfnAllocation(
+            allocator_.pUserData, sizeof(const char*) * count,
+            alignof(const char*), scope_));
+    }
+
+    const bool is_instance_;
+    const VkAllocationCallbacks& allocator_;
+    const VkSystemAllocationScope scope_;
+
+    const char** names_;
+    uint32_t name_count_;
+
+    ImplicitLayerArray implicit_layers_;
+};
+
+// Provide overridden extension names when there are implicit extensions.
+// No effect otherwise.
+//
+// This is used only to enable VK_EXT_debug_report.
+class OverrideExtensionNames {
+   public:
+    OverrideExtensionNames(bool is_instance,
+                           const VkAllocationCallbacks& allocator)
+        : is_instance_(is_instance),
+          allocator_(allocator),
+          scope_(VK_SYSTEM_ALLOCATION_SCOPE_COMMAND),
+          names_(nullptr),
+          name_count_(0),
+          install_debug_callback_(false) {}
+
+    ~OverrideExtensionNames() {
+        allocator_.pfnFree(allocator_.pUserData, names_);
+    }
+
+    VkResult Parse(const char* const* names, uint32_t count) {
+        // this is only for debug.vulkan.enable_callback
+        if (!EnableDebugCallback())
+            return VK_SUCCESS;
+
+        names_ = AllocateNameArray(count + 1);
+        if (!names_)
+            return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+        std::copy(names, names + count, names_);
+
+        name_count_ = count;
+        names_[name_count_++] = "VK_EXT_debug_report";
+
+        install_debug_callback_ = true;
+
+        return VK_SUCCESS;
+    }
+
+    const char* const* Names() const { return names_; }
+
+    uint32_t Count() const { return name_count_; }
+
+    bool InstallDebugCallback() const { return install_debug_callback_; }
+
+   private:
+    bool EnableDebugCallback() const {
+        return (is_instance_ && driver::Debuggable() &&
+                property_get_bool("debug.vulkan.enable_callback", false));
+    }
+
+    const char** AllocateNameArray(uint32_t count) const {
+        return reinterpret_cast<const char**>(allocator_.pfnAllocation(
+            allocator_.pUserData, sizeof(const char*) * count,
+            alignof(const char*), scope_));
+    }
+
+    const bool is_instance_;
+    const VkAllocationCallbacks& allocator_;
+    const VkSystemAllocationScope scope_;
+
+    const char** names_;
+    uint32_t name_count_;
+    bool install_debug_callback_;
+};
+
+// vkCreateInstance and vkCreateDevice helpers with support for layer
+// chaining.
+class LayerChain {
+   public:
+    static VkResult CreateInstance(const VkInstanceCreateInfo* create_info,
+                                   const VkAllocationCallbacks* allocator,
+                                   VkInstance* instance_out);
+
+    static VkResult CreateDevice(VkPhysicalDevice physical_dev,
+                                 const VkDeviceCreateInfo* create_info,
+                                 const VkAllocationCallbacks* allocator,
+                                 VkDevice* dev_out);
+
+    static void DestroyInstance(VkInstance instance,
+                                const VkAllocationCallbacks* allocator);
+
+    static void DestroyDevice(VkDevice dev,
+                              const VkAllocationCallbacks* allocator);
+
+   private:
+    struct ActiveLayer {
+        LayerRef ref;
+        union {
+            VkLayerInstanceLink instance_link;
+            VkLayerDeviceLink device_link;
+        };
+    };
+
+    LayerChain(bool is_instance, const VkAllocationCallbacks& allocator);
+    ~LayerChain();
+
+    VkResult ActivateLayers(const char* const* layer_names,
+                            uint32_t layer_count,
+                            const char* const* extension_names,
+                            uint32_t extension_count);
+    ActiveLayer* AllocateLayerArray(uint32_t count) const;
+    VkResult LoadLayer(ActiveLayer& layer, const char* name);
+    void SetupLayerLinks();
+
+    bool Empty() const;
+    void ModifyCreateInfo(VkInstanceCreateInfo& info);
+    void ModifyCreateInfo(VkDeviceCreateInfo& info);
+
+    VkResult Create(const VkInstanceCreateInfo* create_info,
+                    const VkAllocationCallbacks* allocator,
+                    VkInstance* instance_out);
+
+    VkResult Create(VkPhysicalDevice physical_dev,
+                    const VkDeviceCreateInfo* create_info,
+                    const VkAllocationCallbacks* allocator,
+                    VkDevice* dev_out);
+
+    VkResult ValidateExtensions(const char* const* extension_names,
+                                uint32_t extension_count);
+    VkResult ValidateExtensions(VkPhysicalDevice physical_dev,
+                                const char* const* extension_names,
+                                uint32_t extension_count);
+    VkExtensionProperties* AllocateDriverExtensionArray(uint32_t count) const;
+    bool IsLayerExtension(const char* name) const;
+    bool IsDriverExtension(const char* name) const;
+
+    template <typename DataType>
+    void StealLayers(DataType& data);
+
+    static void DestroyLayers(ActiveLayer* layers,
+                              uint32_t count,
+                              const VkAllocationCallbacks& allocator);
+
+    static VKAPI_ATTR VkResult SetInstanceLoaderData(VkInstance instance,
+                                                     void* object);
+    static VKAPI_ATTR VkResult SetDeviceLoaderData(VkDevice device,
+                                                   void* object);
+
+    static VKAPI_ATTR VkBool32
+    DebugReportCallback(VkDebugReportFlagsEXT flags,
+                        VkDebugReportObjectTypeEXT obj_type,
+                        uint64_t obj,
+                        size_t location,
+                        int32_t msg_code,
+                        const char* layer_prefix,
+                        const char* msg,
+                        void* user_data);
+
+    const bool is_instance_;
+    const VkAllocationCallbacks& allocator_;
+
+    OverrideLayerNames override_layers_;
+    OverrideExtensionNames override_extensions_;
+
+    ActiveLayer* layers_;
+    uint32_t layer_count_;
+
+    PFN_vkGetInstanceProcAddr get_instance_proc_addr_;
+    PFN_vkGetDeviceProcAddr get_device_proc_addr_;
+
+    union {
+        VkLayerInstanceCreateInfo instance_chain_info_[2];
+        VkLayerDeviceCreateInfo device_chain_info_[2];
+    };
+
+    VkExtensionProperties* driver_extensions_;
+    uint32_t driver_extension_count_;
+    std::bitset<driver::ProcHook::EXTENSION_COUNT> enabled_extensions_;
+};
+
+LayerChain::LayerChain(bool is_instance, const VkAllocationCallbacks& allocator)
+    : is_instance_(is_instance),
+      allocator_(allocator),
+      override_layers_(is_instance, allocator),
+      override_extensions_(is_instance, allocator),
+      layers_(nullptr),
+      layer_count_(0),
+      get_instance_proc_addr_(nullptr),
+      get_device_proc_addr_(nullptr),
+      driver_extensions_(nullptr),
+      driver_extension_count_(0) {
+    enabled_extensions_.set(driver::ProcHook::EXTENSION_CORE);
+}
+
+LayerChain::~LayerChain() {
+    allocator_.pfnFree(allocator_.pUserData, driver_extensions_);
+    DestroyLayers(layers_, layer_count_, allocator_);
+}
+
+VkResult LayerChain::ActivateLayers(const char* const* layer_names,
+                                    uint32_t layer_count,
+                                    const char* const* extension_names,
+                                    uint32_t extension_count) {
+    VkResult result = override_layers_.Parse(layer_names, layer_count);
+    if (result != VK_SUCCESS)
+        return result;
+
+    result = override_extensions_.Parse(extension_names, extension_count);
+    if (result != VK_SUCCESS)
+        return result;
+
+    if (override_layers_.Count()) {
+        layer_names = override_layers_.Names();
+        layer_count = override_layers_.Count();
+    }
+
+    if (!layer_count) {
+        // point head of chain to the driver
+        get_instance_proc_addr_ = driver::GetInstanceProcAddr;
+        if (!is_instance_)
+            get_device_proc_addr_ = driver::GetDeviceProcAddr;
+
+        return VK_SUCCESS;
+    }
+
+    layers_ = AllocateLayerArray(layer_count);
+    if (!layers_)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+    // load layers
+    for (uint32_t i = 0; i < layer_count; i++) {
+        result = LoadLayer(layers_[i], layer_names[i]);
+        if (result != VK_SUCCESS)
+            return result;
+
+        // count loaded layers for proper destructions on errors
+        layer_count_++;
+    }
+
+    SetupLayerLinks();
+
+    return VK_SUCCESS;
+}
+
+LayerChain::ActiveLayer* LayerChain::AllocateLayerArray(uint32_t count) const {
+    VkSystemAllocationScope scope = (is_instance_)
+                                        ? VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE
+                                        : VK_SYSTEM_ALLOCATION_SCOPE_DEVICE;
+
+    return reinterpret_cast<ActiveLayer*>(allocator_.pfnAllocation(
+        allocator_.pUserData, sizeof(ActiveLayer) * count, alignof(ActiveLayer),
+        scope));
+}
+
+VkResult LayerChain::LoadLayer(ActiveLayer& layer, const char* name) {
+    if (is_instance_)
+        new (&layer) ActiveLayer{GetInstanceLayerRef(name), {}};
+    else
+        new (&layer) ActiveLayer{GetDeviceLayerRef(name), {}};
+
+    if (!layer.ref) {
+        ALOGE("Failed to load layer %s", name);
+        layer.ref.~LayerRef();
+        return VK_ERROR_LAYER_NOT_PRESENT;
+    }
+
+    ALOGI("Loaded %s layer %s", (is_instance_) ? "instance" : "device", name);
+
+    return VK_SUCCESS;
+}
+
+void LayerChain::SetupLayerLinks() {
+    if (is_instance_) {
+        for (uint32_t i = 0; i < layer_count_; i++) {
+            ActiveLayer& layer = layers_[i];
+
+            // point head of chain to the first layer
+            if (i == 0)
+                get_instance_proc_addr_ = layer.ref.GetGetInstanceProcAddr();
+
+            // point tail of chain to the driver
+            if (i == layer_count_ - 1) {
+                layer.instance_link.pNext = nullptr;
+                layer.instance_link.pfnNextGetInstanceProcAddr =
+                    driver::GetInstanceProcAddr;
+                break;
+            }
+
+            const ActiveLayer& next = layers_[i + 1];
+
+            // const_cast as some naughty layers want to modify our links!
+            layer.instance_link.pNext =
+                const_cast<VkLayerInstanceLink*>(&next.instance_link);
+            layer.instance_link.pfnNextGetInstanceProcAddr =
+                next.ref.GetGetInstanceProcAddr();
+        }
+    } else {
+        for (uint32_t i = 0; i < layer_count_; i++) {
+            ActiveLayer& layer = layers_[i];
+
+            // point head of chain to the first layer
+            if (i == 0) {
+                get_instance_proc_addr_ = layer.ref.GetGetInstanceProcAddr();
+                get_device_proc_addr_ = layer.ref.GetGetDeviceProcAddr();
+            }
+
+            // point tail of chain to the driver
+            if (i == layer_count_ - 1) {
+                layer.device_link.pNext = nullptr;
+                layer.device_link.pfnNextGetInstanceProcAddr =
+                    driver::GetInstanceProcAddr;
+                layer.device_link.pfnNextGetDeviceProcAddr =
+                    driver::GetDeviceProcAddr;
+                break;
+            }
+
+            const ActiveLayer& next = layers_[i + 1];
+
+            // const_cast as some naughty layers want to modify our links!
+            layer.device_link.pNext =
+                const_cast<VkLayerDeviceLink*>(&next.device_link);
+            layer.device_link.pfnNextGetInstanceProcAddr =
+                next.ref.GetGetInstanceProcAddr();
+            layer.device_link.pfnNextGetDeviceProcAddr =
+                next.ref.GetGetDeviceProcAddr();
+        }
+    }
+}
+
+bool LayerChain::Empty() const {
+    return (!layer_count_ && !override_layers_.Count() &&
+            !override_extensions_.Count());
+}
+
+void LayerChain::ModifyCreateInfo(VkInstanceCreateInfo& info) {
+    if (layer_count_) {
+        auto& link_info = instance_chain_info_[1];
+        link_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
+        link_info.pNext = info.pNext;
+        link_info.function = VK_LAYER_FUNCTION_LINK;
+        link_info.u.pLayerInfo = &layers_[0].instance_link;
+
+        auto& cb_info = instance_chain_info_[0];
+        cb_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
+        cb_info.pNext = &link_info;
+        cb_info.function = VK_LAYER_FUNCTION_DATA_CALLBACK;
+        cb_info.u.pfnSetInstanceLoaderData = SetInstanceLoaderData;
+
+        info.pNext = &cb_info;
+    }
+
+    if (override_layers_.Count()) {
+        info.enabledLayerCount = override_layers_.Count();
+        info.ppEnabledLayerNames = override_layers_.Names();
+    }
+
+    if (override_extensions_.Count()) {
+        info.enabledExtensionCount = override_extensions_.Count();
+        info.ppEnabledExtensionNames = override_extensions_.Names();
+    }
+}
+
+void LayerChain::ModifyCreateInfo(VkDeviceCreateInfo& info) {
+    if (layer_count_) {
+        auto& link_info = device_chain_info_[1];
+        link_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
+        link_info.pNext = info.pNext;
+        link_info.function = VK_LAYER_FUNCTION_LINK;
+        link_info.u.pLayerInfo = &layers_[0].device_link;
+
+        auto& cb_info = device_chain_info_[0];
+        cb_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
+        cb_info.pNext = &link_info;
+        cb_info.function = VK_LAYER_FUNCTION_DATA_CALLBACK;
+        cb_info.u.pfnSetDeviceLoaderData = SetDeviceLoaderData;
+
+        info.pNext = &cb_info;
+    }
+
+    if (override_layers_.Count()) {
+        info.enabledLayerCount = override_layers_.Count();
+        info.ppEnabledLayerNames = override_layers_.Names();
+    }
+
+    if (override_extensions_.Count()) {
+        info.enabledExtensionCount = override_extensions_.Count();
+        info.ppEnabledExtensionNames = override_extensions_.Names();
+    }
+}
+
+VkResult LayerChain::Create(const VkInstanceCreateInfo* create_info,
+                            const VkAllocationCallbacks* allocator,
+                            VkInstance* instance_out) {
+    VkResult result = ValidateExtensions(create_info->ppEnabledExtensionNames,
+                                         create_info->enabledExtensionCount);
+    if (result != VK_SUCCESS)
+        return result;
+
+    // call down the chain
+    PFN_vkCreateInstance create_instance =
+        reinterpret_cast<PFN_vkCreateInstance>(
+            get_instance_proc_addr_(VK_NULL_HANDLE, "vkCreateInstance"));
+    VkInstance instance;
+    result = create_instance(create_info, allocator, &instance);
+    if (result != VK_SUCCESS)
+        return result;
+
+    // initialize InstanceData
+    InstanceData& data = GetData(instance);
+
+    data.instance = instance;
+
+    if (!InitDispatchTable(instance, get_instance_proc_addr_,
+                           enabled_extensions_)) {
+        if (data.dispatch.DestroyInstance)
+            data.dispatch.DestroyInstance(instance, allocator);
+
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    // install debug report callback
+    if (override_extensions_.InstallDebugCallback()) {
+        PFN_vkCreateDebugReportCallbackEXT create_debug_report_callback =
+            reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
+                get_instance_proc_addr_(instance,
+                                        "vkCreateDebugReportCallbackEXT"));
+        data.destroy_debug_callback =
+            reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
+                get_instance_proc_addr_(instance,
+                                        "vkDestroyDebugReportCallbackEXT"));
+        if (!create_debug_report_callback || !data.destroy_debug_callback) {
+            ALOGE("Broken VK_EXT_debug_report support");
+            data.dispatch.DestroyInstance(instance, allocator);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        VkDebugReportCallbackCreateInfoEXT debug_callback_info = {};
+        debug_callback_info.sType =
+            VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
+        debug_callback_info.flags =
+            VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
+        debug_callback_info.pfnCallback = DebugReportCallback;
+
+        VkDebugReportCallbackEXT debug_callback;
+        result = create_debug_report_callback(instance, &debug_callback_info,
+                                              nullptr, &debug_callback);
+        if (result != VK_SUCCESS) {
+            ALOGE("Failed to install debug report callback");
+            data.dispatch.DestroyInstance(instance, allocator);
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+
+        data.debug_callback = debug_callback;
+
+        ALOGI("Installed debug report callback");
+    }
+
+    StealLayers(data);
+
+    *instance_out = instance;
+
+    return VK_SUCCESS;
+}
+
+VkResult LayerChain::Create(VkPhysicalDevice physical_dev,
+                            const VkDeviceCreateInfo* create_info,
+                            const VkAllocationCallbacks* allocator,
+                            VkDevice* dev_out) {
+    VkResult result =
+        ValidateExtensions(physical_dev, create_info->ppEnabledExtensionNames,
+                           create_info->enabledExtensionCount);
+    if (result != VK_SUCCESS)
+        return result;
+
+    // call down the chain
+    //
+    // TODO Instance call chain available at
+    // GetData(physical_dev).dispatch.CreateDevice is ignored.  Is that
+    // right?
+    VkInstance instance = GetData(physical_dev).instance;
+    PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>(
+        get_instance_proc_addr_(instance, "vkCreateDevice"));
+    VkDevice dev;
+    result = create_device(physical_dev, create_info, allocator, &dev);
+    if (result != VK_SUCCESS)
+        return result;
+
+    // initialize DeviceData
+    DeviceData& data = GetData(dev);
+
+    if (!InitDispatchTable(dev, get_device_proc_addr_, enabled_extensions_)) {
+        if (data.dispatch.DestroyDevice)
+            data.dispatch.DestroyDevice(dev, allocator);
+
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    StealLayers(data);
+
+    *dev_out = dev;
+
+    return VK_SUCCESS;
+}
+
+VkResult LayerChain::ValidateExtensions(const char* const* extension_names,
+                                        uint32_t extension_count) {
+    if (!extension_count)
+        return VK_SUCCESS;
+
+    // query driver instance extensions
+    uint32_t count;
+    VkResult result =
+        EnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
+    if (result == VK_SUCCESS && count) {
+        driver_extensions_ = AllocateDriverExtensionArray(count);
+        result = (driver_extensions_) ? EnumerateInstanceExtensionProperties(
+                                            nullptr, &count, driver_extensions_)
+                                      : VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+    if (result != VK_SUCCESS)
+        return result;
+
+    driver_extension_count_ = count;
+
+    for (uint32_t i = 0; i < extension_count; i++) {
+        const char* name = extension_names[i];
+        if (!IsLayerExtension(name) && !IsDriverExtension(name)) {
+            ALOGE("Failed to enable missing instance extension %s", name);
+            return VK_ERROR_EXTENSION_NOT_PRESENT;
+        }
+
+        auto ext_bit = driver::GetProcHookExtension(name);
+        if (ext_bit != driver::ProcHook::EXTENSION_UNKNOWN)
+            enabled_extensions_.set(ext_bit);
+    }
+
+    return VK_SUCCESS;
+}
+
+VkResult LayerChain::ValidateExtensions(VkPhysicalDevice physical_dev,
+                                        const char* const* extension_names,
+                                        uint32_t extension_count) {
+    if (!extension_count)
+        return VK_SUCCESS;
+
+    // query driver device extensions
+    uint32_t count;
+    VkResult result = EnumerateDeviceExtensionProperties(physical_dev, nullptr,
+                                                         &count, nullptr);
+    if (result == VK_SUCCESS && count) {
+        driver_extensions_ = AllocateDriverExtensionArray(count);
+        result = (driver_extensions_)
+                     ? EnumerateDeviceExtensionProperties(
+                           physical_dev, nullptr, &count, driver_extensions_)
+                     : VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+    if (result != VK_SUCCESS)
+        return result;
+
+    driver_extension_count_ = count;
+
+    for (uint32_t i = 0; i < extension_count; i++) {
+        const char* name = extension_names[i];
+        if (!IsLayerExtension(name) && !IsDriverExtension(name)) {
+            ALOGE("Failed to enable missing device extension %s", name);
+            return VK_ERROR_EXTENSION_NOT_PRESENT;
+        }
+
+        auto ext_bit = driver::GetProcHookExtension(name);
+        if (ext_bit != driver::ProcHook::EXTENSION_UNKNOWN)
+            enabled_extensions_.set(ext_bit);
+    }
+
+    return VK_SUCCESS;
+}
+
+VkExtensionProperties* LayerChain::AllocateDriverExtensionArray(
+    uint32_t count) const {
+    return reinterpret_cast<VkExtensionProperties*>(allocator_.pfnAllocation(
+        allocator_.pUserData, sizeof(VkExtensionProperties) * count,
+        alignof(VkExtensionProperties), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
+}
+
+bool LayerChain::IsLayerExtension(const char* name) const {
+    for (uint32_t i = 0; i < layer_count_; i++) {
+        const ActiveLayer& layer = layers_[i];
+        if (layer.ref.SupportsExtension(name))
+            return true;
+    }
+
+    return false;
+}
+
+bool LayerChain::IsDriverExtension(const char* name) const {
+    for (uint32_t i = 0; i < driver_extension_count_; i++) {
+        if (strcmp(driver_extensions_[i].extensionName, name) == 0)
+            return true;
+    }
+
+    return false;
+}
+
+template <typename DataType>
+void LayerChain::StealLayers(DataType& data) {
+    data.layers = layers_;
+    data.layer_count = layer_count_;
+
+    layers_ = nullptr;
+    layer_count_ = 0;
+}
+
+void LayerChain::DestroyLayers(ActiveLayer* layers,
+                               uint32_t count,
+                               const VkAllocationCallbacks& allocator) {
+    for (uint32_t i = 0; i < count; i++)
+        layers[i].ref.~LayerRef();
+
+    allocator.pfnFree(allocator.pUserData, layers);
+}
+
+VkResult LayerChain::SetInstanceLoaderData(VkInstance instance, void* object) {
+    driver::InstanceDispatchable dispatchable =
+        reinterpret_cast<driver::InstanceDispatchable>(object);
+
+    return (driver::SetDataInternal(dispatchable, &driver::GetData(instance)))
+               ? VK_SUCCESS
+               : VK_ERROR_INITIALIZATION_FAILED;
+}
+
+VkResult LayerChain::SetDeviceLoaderData(VkDevice device, void* object) {
+    driver::DeviceDispatchable dispatchable =
+        reinterpret_cast<driver::DeviceDispatchable>(object);
+
+    return (driver::SetDataInternal(dispatchable, &driver::GetData(device)))
+               ? VK_SUCCESS
+               : VK_ERROR_INITIALIZATION_FAILED;
+}
+
+VkBool32 LayerChain::DebugReportCallback(VkDebugReportFlagsEXT flags,
+                                         VkDebugReportObjectTypeEXT obj_type,
+                                         uint64_t obj,
+                                         size_t location,
+                                         int32_t msg_code,
+                                         const char* layer_prefix,
+                                         const char* msg,
+                                         void* user_data) {
+    int prio;
+
+    if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
+        prio = ANDROID_LOG_ERROR;
+    else if (flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT |
+                      VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT))
+        prio = ANDROID_LOG_WARN;
+    else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
+        prio = ANDROID_LOG_INFO;
+    else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
+        prio = ANDROID_LOG_DEBUG;
+    else
+        prio = ANDROID_LOG_UNKNOWN;
+
+    LOG_PRI(prio, LOG_TAG, "[%s] Code %d : %s", layer_prefix, msg_code, msg);
+
+    (void)obj_type;
+    (void)obj;
+    (void)location;
+    (void)user_data;
+
+    return false;
+}
+
+VkResult LayerChain::CreateInstance(const VkInstanceCreateInfo* create_info,
+                                    const VkAllocationCallbacks* allocator,
+                                    VkInstance* instance_out) {
+    LayerChain chain(true,
+                     (allocator) ? *allocator : driver::GetDefaultAllocator());
+
+    VkResult result = chain.ActivateLayers(create_info->ppEnabledLayerNames,
+                                           create_info->enabledLayerCount,
+                                           create_info->ppEnabledExtensionNames,
+                                           create_info->enabledExtensionCount);
+    if (result != VK_SUCCESS)
+        return result;
+
+    // use a local create info when the chain is not empty
+    VkInstanceCreateInfo local_create_info;
+    if (!chain.Empty()) {
+        local_create_info = *create_info;
+        chain.ModifyCreateInfo(local_create_info);
+        create_info = &local_create_info;
+    }
+
+    return chain.Create(create_info, allocator, instance_out);
+}
+
+VkResult LayerChain::CreateDevice(VkPhysicalDevice physical_dev,
+                                  const VkDeviceCreateInfo* create_info,
+                                  const VkAllocationCallbacks* allocator,
+                                  VkDevice* dev_out) {
+    LayerChain chain(false, (allocator)
+                                ? *allocator
+                                : driver::GetData(physical_dev).allocator);
+
+    VkResult result = chain.ActivateLayers(create_info->ppEnabledLayerNames,
+                                           create_info->enabledLayerCount,
+                                           create_info->ppEnabledExtensionNames,
+                                           create_info->enabledExtensionCount);
+    if (result != VK_SUCCESS)
+        return result;
+
+    // use a local create info when the chain is not empty
+    VkDeviceCreateInfo local_create_info;
+    if (!chain.Empty()) {
+        local_create_info = *create_info;
+        chain.ModifyCreateInfo(local_create_info);
+        create_info = &local_create_info;
+    }
+
+    return chain.Create(physical_dev, create_info, allocator, dev_out);
+}
+
+void LayerChain::DestroyInstance(VkInstance instance,
+                                 const VkAllocationCallbacks* allocator) {
+    InstanceData& data = GetData(instance);
+
+    if (data.debug_callback != VK_NULL_HANDLE)
+        data.destroy_debug_callback(instance, data.debug_callback, allocator);
+
+    ActiveLayer* layers = reinterpret_cast<ActiveLayer*>(data.layers);
+    uint32_t layer_count = data.layer_count;
+
+    VkAllocationCallbacks local_allocator;
+    if (!allocator)
+        local_allocator = driver::GetData(instance).allocator;
+
+    // this also destroys InstanceData
+    data.dispatch.DestroyInstance(instance, allocator);
+
+    DestroyLayers(layers, layer_count,
+                  (allocator) ? *allocator : local_allocator);
+}
+
+void LayerChain::DestroyDevice(VkDevice device,
+                               const VkAllocationCallbacks* allocator) {
+    DeviceData& data = GetData(device);
+
+    ActiveLayer* layers = reinterpret_cast<ActiveLayer*>(data.layers);
+    uint32_t layer_count = data.layer_count;
+
+    VkAllocationCallbacks local_allocator;
+    if (!allocator)
+        local_allocator = driver::GetData(device).allocator;
+
+    // this also destroys DeviceData
+    data.dispatch.DestroyDevice(device, allocator);
+
+    DestroyLayers(layers, layer_count,
+                  (allocator) ? *allocator : local_allocator);
+}
+
+// ----------------------------------------------------------------------------
+
+bool EnsureInitialized() {
+    static std::once_flag once_flag;
+    static bool initialized;
+
+    std::call_once(once_flag, []() {
+        if (driver::OpenHAL()) {
+            DiscoverLayers();
+            initialized = true;
+        }
+    });
+
+    return initialized;
+}
+
+}  // anonymous namespace
+
+VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+                        const VkAllocationCallbacks* pAllocator,
+                        VkInstance* pInstance) {
+    if (!EnsureInitialized())
+        return VK_ERROR_INITIALIZATION_FAILED;
+
+    return LayerChain::CreateInstance(pCreateInfo, pAllocator, pInstance);
+}
+
+void DestroyInstance(VkInstance instance,
+                     const VkAllocationCallbacks* pAllocator) {
+    if (instance != VK_NULL_HANDLE)
+        LayerChain::DestroyInstance(instance, pAllocator);
+}
+
+VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+                      const VkDeviceCreateInfo* pCreateInfo,
+                      const VkAllocationCallbacks* pAllocator,
+                      VkDevice* pDevice) {
+    return LayerChain::CreateDevice(physicalDevice, pCreateInfo, pAllocator,
+                                    pDevice);
+}
+
+void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
+    if (device != VK_NULL_HANDLE)
+        LayerChain::DestroyDevice(device, pAllocator);
+}
+
+VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount,
+                                          VkLayerProperties* pProperties) {
+    if (!EnsureInitialized())
+        return VK_ERROR_INITIALIZATION_FAILED;
+
+    uint32_t count =
+        EnumerateInstanceLayers(pProperties ? *pPropertyCount : 0, pProperties);
+
+    if (!pProperties || *pPropertyCount > count)
+        *pPropertyCount = count;
+
+    return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
+}
+
+VkResult EnumerateInstanceExtensionProperties(
+    const char* pLayerName,
+    uint32_t* pPropertyCount,
+    VkExtensionProperties* pProperties) {
+    if (!EnsureInitialized())
+        return VK_ERROR_INITIALIZATION_FAILED;
+
+    if (pLayerName) {
+        const VkExtensionProperties* props;
+        uint32_t count;
+        GetInstanceLayerExtensions(pLayerName, &props, &count);
+
+        if (!pProperties || *pPropertyCount > count)
+            *pPropertyCount = count;
+        if (pProperties)
+            std::copy(props, props + *pPropertyCount, pProperties);
+
+        return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
+    }
+
+    // TODO how about extensions from implicitly enabled layers?
+    return vulkan::driver::EnumerateInstanceExtensionProperties(
+        nullptr, pPropertyCount, pProperties);
+}
+
+VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
+                                        uint32_t* pPropertyCount,
+                                        VkLayerProperties* pProperties) {
+    (void)physicalDevice;
+
+    uint32_t count =
+        EnumerateDeviceLayers(pProperties ? *pPropertyCount : 0, pProperties);
+
+    if (!pProperties || *pPropertyCount > count)
+        *pPropertyCount = count;
+
+    return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
+}
+
+VkResult EnumerateDeviceExtensionProperties(
+    VkPhysicalDevice physicalDevice,
+    const char* pLayerName,
+    uint32_t* pPropertyCount,
+    VkExtensionProperties* pProperties) {
+    if (pLayerName) {
+        const VkExtensionProperties* props;
+        uint32_t count;
+        GetDeviceLayerExtensions(pLayerName, &props, &count);
+
+        if (!pProperties || *pPropertyCount > count)
+            *pPropertyCount = count;
+        if (pProperties)
+            std::copy(props, props + *pPropertyCount, pProperties);
+
+        return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
+    }
+
+    // TODO how about extensions from implicitly enabled layers?
+    const InstanceData& data = GetData(physicalDevice);
+    return data.dispatch.EnumerateDeviceExtensionProperties(
+        physicalDevice, nullptr, pPropertyCount, pProperties);
+}
+
+}  // namespace api
+}  // namespace vulkan
diff --git a/vulkan/libvulkan/api.h b/vulkan/libvulkan/api.h
new file mode 100644
index 0000000..ded7d17
--- /dev/null
+++ b/vulkan/libvulkan/api.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 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 LIBVULKAN_API_H
+#define LIBVULKAN_API_H 1
+
+#include <vulkan/vulkan.h>
+#include "api_gen.h"
+#include "driver.h"
+
+namespace vulkan {
+namespace api {
+
+// clang-format off
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
+VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+// clang-format on
+
+inline InstanceData& GetData(VkInstance instance) {
+    return driver::GetData(instance).opaque_api_data;
+}
+
+inline InstanceData& GetData(VkPhysicalDevice physical_dev) {
+    return driver::GetData(physical_dev).opaque_api_data;
+}
+
+inline DeviceData& GetData(VkDevice dev) {
+    return driver::GetData(dev).opaque_api_data;
+}
+
+inline DeviceData& GetData(VkQueue queue) {
+    return driver::GetData(queue).opaque_api_data;
+}
+
+inline DeviceData& GetData(VkCommandBuffer cmd) {
+    return driver::GetData(cmd).opaque_api_data;
+}
+
+}  // namespace api
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_API_H
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
new file mode 100644
index 0000000..fe4136f
--- /dev/null
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -0,0 +1,1243 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// WARNING: This file is generated. See ../README.md for instructions.
+
+#include <string.h>
+#include <algorithm>
+#include <log/log.h>
+
+#include "api.h"
+
+namespace vulkan {
+namespace api {
+
+#define UNLIKELY(expr) __builtin_expect((expr), 0)
+
+#define INIT_PROC(obj, proc)                                           \
+    do {                                                               \
+        data.dispatch.proc =                                           \
+            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
+        if (UNLIKELY(!data.dispatch.proc)) {                           \
+            ALOGE("missing " #obj " proc: vk" #proc);                  \
+            success = false;                                           \
+        }                                                              \
+    } while (0)
+
+// Exported extension functions may be invoked even when their extensions
+// are disabled.  Dispatch to stubs when that happens.
+#define INIT_PROC_EXT(ext, obj, proc)            \
+    do {                                         \
+        if (extensions[driver::ProcHook::ext])   \
+            INIT_PROC(obj, proc);                \
+        else                                     \
+            data.dispatch.proc = disabled##proc; \
+    } while (0)
+
+namespace {
+
+// clang-format off
+
+VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks*) {
+    ALOGE("VK_KHR_surface not enabled. vkDestroySurfaceKHR not executed.");
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice, uint32_t, VkSurfaceKHR, VkBool32*) {
+    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceSupportKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR*) {
+    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceCapabilitiesKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkSurfaceFormatKHR*) {
+    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceFormatsKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkPresentModeKHR*) {
+    ALOGE("VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfacePresentModesKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledCreateSwapchainKHR(VkDevice, const VkSwapchainCreateInfoKHR*, const VkAllocationCallbacks*, VkSwapchainKHR*) {
+    ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR void disabledDestroySwapchainKHR(VkDevice, VkSwapchainKHR, const VkAllocationCallbacks*) {
+    ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+}
+
+VKAPI_ATTR VkResult disabledGetSwapchainImagesKHR(VkDevice, VkSwapchainKHR, uint32_t*, VkImage*) {
+    ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledAcquireNextImageKHR(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t*) {
+    ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledQueuePresentKHR(VkQueue, const VkPresentInfoKHR*) {
+    ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
+    ALOGE("VK_KHR_android_surface not enabled. vkCreateAndroidSurfaceKHR not executed.");
+    return VK_SUCCESS;
+}
+
+// clang-format on
+
+}  // anonymous
+
+bool InitDispatchTable(
+    VkInstance instance,
+    PFN_vkGetInstanceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(instance);
+    bool success = true;
+
+    // clang-format off
+    INIT_PROC(instance, DestroyInstance);
+    INIT_PROC(instance, EnumeratePhysicalDevices);
+    INIT_PROC(instance, GetInstanceProcAddr);
+    INIT_PROC(instance, GetPhysicalDeviceProperties);
+    INIT_PROC(instance, GetPhysicalDeviceQueueFamilyProperties);
+    INIT_PROC(instance, GetPhysicalDeviceMemoryProperties);
+    INIT_PROC(instance, GetPhysicalDeviceFeatures);
+    INIT_PROC(instance, GetPhysicalDeviceFormatProperties);
+    INIT_PROC(instance, GetPhysicalDeviceImageFormatProperties);
+    INIT_PROC(instance, CreateDevice);
+    INIT_PROC(instance, EnumerateDeviceLayerProperties);
+    INIT_PROC(instance, EnumerateDeviceExtensionProperties);
+    INIT_PROC(instance, GetPhysicalDeviceSparseImageFormatProperties);
+    INIT_PROC_EXT(KHR_surface, instance, DestroySurfaceKHR);
+    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceSupportKHR);
+    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
+    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfaceFormatsKHR);
+    INIT_PROC_EXT(KHR_surface, instance, GetPhysicalDeviceSurfacePresentModesKHR);
+    INIT_PROC_EXT(KHR_android_surface, instance, CreateAndroidSurfaceKHR);
+    // clang-format on
+
+    return success;
+}
+
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(dev);
+    bool success = true;
+
+    // clang-format off
+    INIT_PROC(dev, GetDeviceProcAddr);
+    INIT_PROC(dev, DestroyDevice);
+    INIT_PROC(dev, GetDeviceQueue);
+    INIT_PROC(dev, QueueSubmit);
+    INIT_PROC(dev, QueueWaitIdle);
+    INIT_PROC(dev, DeviceWaitIdle);
+    INIT_PROC(dev, AllocateMemory);
+    INIT_PROC(dev, FreeMemory);
+    INIT_PROC(dev, MapMemory);
+    INIT_PROC(dev, UnmapMemory);
+    INIT_PROC(dev, FlushMappedMemoryRanges);
+    INIT_PROC(dev, InvalidateMappedMemoryRanges);
+    INIT_PROC(dev, GetDeviceMemoryCommitment);
+    INIT_PROC(dev, GetBufferMemoryRequirements);
+    INIT_PROC(dev, BindBufferMemory);
+    INIT_PROC(dev, GetImageMemoryRequirements);
+    INIT_PROC(dev, BindImageMemory);
+    INIT_PROC(dev, GetImageSparseMemoryRequirements);
+    INIT_PROC(dev, QueueBindSparse);
+    INIT_PROC(dev, CreateFence);
+    INIT_PROC(dev, DestroyFence);
+    INIT_PROC(dev, ResetFences);
+    INIT_PROC(dev, GetFenceStatus);
+    INIT_PROC(dev, WaitForFences);
+    INIT_PROC(dev, CreateSemaphore);
+    INIT_PROC(dev, DestroySemaphore);
+    INIT_PROC(dev, CreateEvent);
+    INIT_PROC(dev, DestroyEvent);
+    INIT_PROC(dev, GetEventStatus);
+    INIT_PROC(dev, SetEvent);
+    INIT_PROC(dev, ResetEvent);
+    INIT_PROC(dev, CreateQueryPool);
+    INIT_PROC(dev, DestroyQueryPool);
+    INIT_PROC(dev, GetQueryPoolResults);
+    INIT_PROC(dev, CreateBuffer);
+    INIT_PROC(dev, DestroyBuffer);
+    INIT_PROC(dev, CreateBufferView);
+    INIT_PROC(dev, DestroyBufferView);
+    INIT_PROC(dev, CreateImage);
+    INIT_PROC(dev, DestroyImage);
+    INIT_PROC(dev, GetImageSubresourceLayout);
+    INIT_PROC(dev, CreateImageView);
+    INIT_PROC(dev, DestroyImageView);
+    INIT_PROC(dev, CreateShaderModule);
+    INIT_PROC(dev, DestroyShaderModule);
+    INIT_PROC(dev, CreatePipelineCache);
+    INIT_PROC(dev, DestroyPipelineCache);
+    INIT_PROC(dev, GetPipelineCacheData);
+    INIT_PROC(dev, MergePipelineCaches);
+    INIT_PROC(dev, CreateGraphicsPipelines);
+    INIT_PROC(dev, CreateComputePipelines);
+    INIT_PROC(dev, DestroyPipeline);
+    INIT_PROC(dev, CreatePipelineLayout);
+    INIT_PROC(dev, DestroyPipelineLayout);
+    INIT_PROC(dev, CreateSampler);
+    INIT_PROC(dev, DestroySampler);
+    INIT_PROC(dev, CreateDescriptorSetLayout);
+    INIT_PROC(dev, DestroyDescriptorSetLayout);
+    INIT_PROC(dev, CreateDescriptorPool);
+    INIT_PROC(dev, DestroyDescriptorPool);
+    INIT_PROC(dev, ResetDescriptorPool);
+    INIT_PROC(dev, AllocateDescriptorSets);
+    INIT_PROC(dev, FreeDescriptorSets);
+    INIT_PROC(dev, UpdateDescriptorSets);
+    INIT_PROC(dev, CreateFramebuffer);
+    INIT_PROC(dev, DestroyFramebuffer);
+    INIT_PROC(dev, CreateRenderPass);
+    INIT_PROC(dev, DestroyRenderPass);
+    INIT_PROC(dev, GetRenderAreaGranularity);
+    INIT_PROC(dev, CreateCommandPool);
+    INIT_PROC(dev, DestroyCommandPool);
+    INIT_PROC(dev, ResetCommandPool);
+    INIT_PROC(dev, AllocateCommandBuffers);
+    INIT_PROC(dev, FreeCommandBuffers);
+    INIT_PROC(dev, BeginCommandBuffer);
+    INIT_PROC(dev, EndCommandBuffer);
+    INIT_PROC(dev, ResetCommandBuffer);
+    INIT_PROC(dev, CmdBindPipeline);
+    INIT_PROC(dev, CmdSetViewport);
+    INIT_PROC(dev, CmdSetScissor);
+    INIT_PROC(dev, CmdSetLineWidth);
+    INIT_PROC(dev, CmdSetDepthBias);
+    INIT_PROC(dev, CmdSetBlendConstants);
+    INIT_PROC(dev, CmdSetDepthBounds);
+    INIT_PROC(dev, CmdSetStencilCompareMask);
+    INIT_PROC(dev, CmdSetStencilWriteMask);
+    INIT_PROC(dev, CmdSetStencilReference);
+    INIT_PROC(dev, CmdBindDescriptorSets);
+    INIT_PROC(dev, CmdBindIndexBuffer);
+    INIT_PROC(dev, CmdBindVertexBuffers);
+    INIT_PROC(dev, CmdDraw);
+    INIT_PROC(dev, CmdDrawIndexed);
+    INIT_PROC(dev, CmdDrawIndirect);
+    INIT_PROC(dev, CmdDrawIndexedIndirect);
+    INIT_PROC(dev, CmdDispatch);
+    INIT_PROC(dev, CmdDispatchIndirect);
+    INIT_PROC(dev, CmdCopyBuffer);
+    INIT_PROC(dev, CmdCopyImage);
+    INIT_PROC(dev, CmdBlitImage);
+    INIT_PROC(dev, CmdCopyBufferToImage);
+    INIT_PROC(dev, CmdCopyImageToBuffer);
+    INIT_PROC(dev, CmdUpdateBuffer);
+    INIT_PROC(dev, CmdFillBuffer);
+    INIT_PROC(dev, CmdClearColorImage);
+    INIT_PROC(dev, CmdClearDepthStencilImage);
+    INIT_PROC(dev, CmdClearAttachments);
+    INIT_PROC(dev, CmdResolveImage);
+    INIT_PROC(dev, CmdSetEvent);
+    INIT_PROC(dev, CmdResetEvent);
+    INIT_PROC(dev, CmdWaitEvents);
+    INIT_PROC(dev, CmdPipelineBarrier);
+    INIT_PROC(dev, CmdBeginQuery);
+    INIT_PROC(dev, CmdEndQuery);
+    INIT_PROC(dev, CmdResetQueryPool);
+    INIT_PROC(dev, CmdWriteTimestamp);
+    INIT_PROC(dev, CmdCopyQueryPoolResults);
+    INIT_PROC(dev, CmdPushConstants);
+    INIT_PROC(dev, CmdBeginRenderPass);
+    INIT_PROC(dev, CmdNextSubpass);
+    INIT_PROC(dev, CmdEndRenderPass);
+    INIT_PROC(dev, CmdExecuteCommands);
+    INIT_PROC_EXT(KHR_swapchain, dev, CreateSwapchainKHR);
+    INIT_PROC_EXT(KHR_swapchain, dev, DestroySwapchainKHR);
+    INIT_PROC_EXT(KHR_swapchain, dev, GetSwapchainImagesKHR);
+    INIT_PROC_EXT(KHR_swapchain, dev, AcquireNextImageKHR);
+    INIT_PROC_EXT(KHR_swapchain, dev, QueuePresentKHR);
+    // clang-format on
+
+    return success;
+}
+
+}  // namespace api
+}  // namespace vulkan
+
+// clang-format off
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) {
+    // call into api.cpp
+    return vulkan::api::CreateInstance(pCreateInfo, pAllocator, pInstance);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) {
+    // call into api.cpp
+    vulkan::api::DestroyInstance(instance, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
+    return vulkan::api::GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR PFN_vkVoidFunction vkGetDeviceProcAddr(VkDevice device, const char* pName) {
+    if (device == VK_NULL_HANDLE) {
+        ALOGE("vkGetDeviceProcAddr called with invalid device");
+        return nullptr;
+    }
+
+    static const char* const known_non_device_names[] = {
+        "vkCreateAndroidSurfaceKHR",
+        "vkCreateDebugReportCallbackEXT",
+        "vkCreateDevice",
+        "vkCreateInstance",
+        "vkDebugReportMessageEXT",
+        "vkDestroyDebugReportCallbackEXT",
+        "vkDestroyInstance",
+        "vkDestroySurfaceKHR",
+        "vkEnumerateDeviceExtensionProperties",
+        "vkEnumerateDeviceLayerProperties",
+        "vkEnumerateInstanceExtensionProperties",
+        "vkEnumerateInstanceLayerProperties",
+        "vkEnumeratePhysicalDevices",
+        "vkGetInstanceProcAddr",
+        "vkGetPhysicalDeviceFeatures",
+        "vkGetPhysicalDeviceFormatProperties",
+        "vkGetPhysicalDeviceImageFormatProperties",
+        "vkGetPhysicalDeviceMemoryProperties",
+        "vkGetPhysicalDeviceProperties",
+        "vkGetPhysicalDeviceQueueFamilyProperties",
+        "vkGetPhysicalDeviceSparseImageFormatProperties",
+        "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
+        "vkGetPhysicalDeviceSurfaceFormatsKHR",
+        "vkGetPhysicalDeviceSurfacePresentModesKHR",
+        "vkGetPhysicalDeviceSurfaceSupportKHR",
+    };
+    // clang-format on
+    constexpr size_t count =
+        sizeof(known_non_device_names) / sizeof(known_non_device_names[0]);
+    if (!pName ||
+        std::binary_search(
+            known_non_device_names, known_non_device_names + count, pName,
+            [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
+        ALOGE("vkGetDeviceProcAddr called with %s", (pName) ? pName : "(null)");
+        return nullptr;
+    }
+    // clang-format off
+
+    if (strcmp(pName, "vkGetDeviceProcAddr") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceProcAddr);
+    if (strcmp(pName, "vkDestroyDevice") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::DestroyDevice);
+
+    return vulkan::api::GetData(device).dispatch.GetDeviceProcAddr(device, pName);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
+    // global functions
+    if (!instance) {
+        if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::CreateInstance);
+        if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::EnumerateInstanceLayerProperties);
+        if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::EnumerateInstanceExtensionProperties);
+
+        ALOGE("vkGetInstanceProcAddr called with %s without instance",  pName);
+        return nullptr;
+    }
+
+    static const struct Hook {
+        const char* name;
+        PFN_vkVoidFunction proc;
+    } hooks[] = {
+        { "vkAcquireNextImageKHR", reinterpret_cast<PFN_vkVoidFunction>(vkAcquireNextImageKHR) },
+        { "vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(vkAllocateCommandBuffers) },
+        { "vkAllocateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkAllocateDescriptorSets) },
+        { "vkAllocateMemory", reinterpret_cast<PFN_vkVoidFunction>(vkAllocateMemory) },
+        { "vkBeginCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkBeginCommandBuffer) },
+        { "vkBindBufferMemory", reinterpret_cast<PFN_vkVoidFunction>(vkBindBufferMemory) },
+        { "vkBindImageMemory", reinterpret_cast<PFN_vkVoidFunction>(vkBindImageMemory) },
+        { "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBeginQuery) },
+        { "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBeginRenderPass) },
+        { "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindDescriptorSets) },
+        { "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindIndexBuffer) },
+        { "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindPipeline) },
+        { "vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindVertexBuffers) },
+        { "vkCmdBlitImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBlitImage) },
+        { "vkCmdClearAttachments", reinterpret_cast<PFN_vkVoidFunction>(vkCmdClearAttachments) },
+        { "vkCmdClearColorImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdClearColorImage) },
+        { "vkCmdClearDepthStencilImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdClearDepthStencilImage) },
+        { "vkCmdCopyBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyBuffer) },
+        { "vkCmdCopyBufferToImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyBufferToImage) },
+        { "vkCmdCopyImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyImage) },
+        { "vkCmdCopyImageToBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyImageToBuffer) },
+        { "vkCmdCopyQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyQueryPoolResults) },
+        { "vkCmdDispatch", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDispatch) },
+        { "vkCmdDispatchIndirect", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDispatchIndirect) },
+        { "vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDraw) },
+        { "vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDrawIndexed) },
+        { "vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDrawIndexedIndirect) },
+        { "vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDrawIndirect) },
+        { "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(vkCmdEndQuery) },
+        { "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkCmdEndRenderPass) },
+        { "vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(vkCmdExecuteCommands) },
+        { "vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdFillBuffer) },
+        { "vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(vkCmdNextSubpass) },
+        { "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(vkCmdPipelineBarrier) },
+        { "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(vkCmdPushConstants) },
+        { "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkCmdResetEvent) },
+        { "vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(vkCmdResetQueryPool) },
+        { "vkCmdResolveImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdResolveImage) },
+        { "vkCmdSetBlendConstants", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetBlendConstants) },
+        { "vkCmdSetDepthBias", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetDepthBias) },
+        { "vkCmdSetDepthBounds", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetDepthBounds) },
+        { "vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetEvent) },
+        { "vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetLineWidth) },
+        { "vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetScissor) },
+        { "vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetStencilCompareMask) },
+        { "vkCmdSetStencilReference", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetStencilReference) },
+        { "vkCmdSetStencilWriteMask", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetStencilWriteMask) },
+        { "vkCmdSetViewport", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetViewport) },
+        { "vkCmdUpdateBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdUpdateBuffer) },
+        { "vkCmdWaitEvents", reinterpret_cast<PFN_vkVoidFunction>(vkCmdWaitEvents) },
+        { "vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(vkCmdWriteTimestamp) },
+        { "vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCreateBuffer) },
+        { "vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(vkCreateBufferView) },
+        { "vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(vkCreateCommandPool) },
+        { "vkCreateComputePipelines", reinterpret_cast<PFN_vkVoidFunction>(vkCreateComputePipelines) },
+        { "vkCreateDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(vkCreateDescriptorPool) },
+        { "vkCreateDescriptorSetLayout", reinterpret_cast<PFN_vkVoidFunction>(vkCreateDescriptorSetLayout) },
+        { "vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::CreateDevice) },
+        { "vkCreateEvent", reinterpret_cast<PFN_vkVoidFunction>(vkCreateEvent) },
+        { "vkCreateFence", reinterpret_cast<PFN_vkVoidFunction>(vkCreateFence) },
+        { "vkCreateFramebuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCreateFramebuffer) },
+        { "vkCreateGraphicsPipelines", reinterpret_cast<PFN_vkVoidFunction>(vkCreateGraphicsPipelines) },
+        { "vkCreateImage", reinterpret_cast<PFN_vkVoidFunction>(vkCreateImage) },
+        { "vkCreateImageView", reinterpret_cast<PFN_vkVoidFunction>(vkCreateImageView) },
+        { "vkCreateInstance", nullptr },
+        { "vkCreatePipelineCache", reinterpret_cast<PFN_vkVoidFunction>(vkCreatePipelineCache) },
+        { "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(vkCreatePipelineLayout) },
+        { "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(vkCreateQueryPool) },
+        { "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkCreateRenderPass) },
+        { "vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(vkCreateSampler) },
+        { "vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(vkCreateSemaphore) },
+        { "vkCreateShaderModule", reinterpret_cast<PFN_vkVoidFunction>(vkCreateShaderModule) },
+        { "vkCreateSwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(vkCreateSwapchainKHR) },
+        { "vkDestroyBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyBuffer) },
+        { "vkDestroyBufferView", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyBufferView) },
+        { "vkDestroyCommandPool", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyCommandPool) },
+        { "vkDestroyDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyDescriptorPool) },
+        { "vkDestroyDescriptorSetLayout", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyDescriptorSetLayout) },
+        { "vkDestroyDevice", reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::DestroyDevice) },
+        { "vkDestroyEvent", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyEvent) },
+        { "vkDestroyFence", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyFence) },
+        { "vkDestroyFramebuffer", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyFramebuffer) },
+        { "vkDestroyImage", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyImage) },
+        { "vkDestroyImageView", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyImageView) },
+        { "vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::DestroyInstance) },
+        { "vkDestroyPipeline", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyPipeline) },
+        { "vkDestroyPipelineCache", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyPipelineCache) },
+        { "vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyPipelineLayout) },
+        { "vkDestroyQueryPool", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyQueryPool) },
+        { "vkDestroyRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyRenderPass) },
+        { "vkDestroySampler", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySampler) },
+        { "vkDestroySemaphore", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySemaphore) },
+        { "vkDestroyShaderModule", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyShaderModule) },
+        { "vkDestroySwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySwapchainKHR) },
+        { "vkDeviceWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(vkDeviceWaitIdle) },
+        { "vkEndCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkEndCommandBuffer) },
+        { "vkEnumerateDeviceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::EnumerateDeviceExtensionProperties) },
+        { "vkEnumerateDeviceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(vulkan::api::EnumerateDeviceLayerProperties) },
+        { "vkEnumerateInstanceExtensionProperties", nullptr },
+        { "vkEnumerateInstanceLayerProperties", nullptr },
+        { "vkFlushMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(vkFlushMappedMemoryRanges) },
+        { "vkFreeCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(vkFreeCommandBuffers) },
+        { "vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkFreeDescriptorSets) },
+        { "vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(vkFreeMemory) },
+        { "vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(vkGetBufferMemoryRequirements) },
+        { "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceMemoryCommitment) },
+        { "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceProcAddr) },
+        { "vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceQueue) },
+        { "vkGetEventStatus", reinterpret_cast<PFN_vkVoidFunction>(vkGetEventStatus) },
+        { "vkGetFenceStatus", reinterpret_cast<PFN_vkVoidFunction>(vkGetFenceStatus) },
+        { "vkGetImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(vkGetImageMemoryRequirements) },
+        { "vkGetImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(vkGetImageSparseMemoryRequirements) },
+        { "vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(vkGetImageSubresourceLayout) },
+        { "vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(vkGetInstanceProcAddr) },
+        { "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(vkGetPipelineCacheData) },
+        { "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(vkGetQueryPoolResults) },
+        { "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(vkGetRenderAreaGranularity) },
+        { "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(vkGetSwapchainImagesKHR) },
+        { "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(vkInvalidateMappedMemoryRanges) },
+        { "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(vkMapMemory) },
+        { "vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(vkMergePipelineCaches) },
+        { "vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(vkQueueBindSparse) },
+        { "vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(vkQueuePresentKHR) },
+        { "vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(vkQueueSubmit) },
+        { "vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(vkQueueWaitIdle) },
+        { "vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkResetCommandBuffer) },
+        { "vkResetCommandPool", reinterpret_cast<PFN_vkVoidFunction>(vkResetCommandPool) },
+        { "vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(vkResetDescriptorPool) },
+        { "vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkResetEvent) },
+        { "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(vkResetFences) },
+        { "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkSetEvent) },
+        { "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(vkUnmapMemory) },
+        { "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkUpdateDescriptorSets) },
+        { "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(vkWaitForFences) },
+    };
+    // clang-format on
+    constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
+    auto hook = std::lower_bound(
+        hooks, hooks + count, pName,
+        [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
+    if (hook < hooks + count && strcmp(hook->name, pName) == 0) {
+        if (!hook->proc)
+            ALOGE("vkGetInstanceProcAddr called with %s with instance", pName);
+        return hook->proc;
+    }
+    // clang-format off
+
+    return vulkan::api::GetData(instance).dispatch.GetInstanceProcAddr(instance, pName);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties) {
+    vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceProperties(physicalDevice, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties) {
+    vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
+    vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures) {
+    vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceFeatures(physicalDevice, pFeatures);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties) {
+    vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceFormatProperties(physicalDevice, format, pFormatProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties) {
+    return vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceImageFormatProperties(physicalDevice, format, type, tiling, usage, flags, pImageFormatProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) {
+    // call into api.cpp
+    return vulkan::api::CreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
+    // call into api.cpp
+    vulkan::api::DestroyDevice(device, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
+    // call into api.cpp
+    return vulkan::api::EnumerateInstanceLayerProperties(pPropertyCount, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
+    // call into api.cpp
+    return vulkan::api::EnumerateInstanceExtensionProperties(pLayerName, pPropertyCount, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
+    // call into api.cpp
+    return vulkan::api::EnumerateDeviceLayerProperties(physicalDevice, pPropertyCount, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
+    // call into api.cpp
+    return vulkan::api::EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pPropertyCount, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) {
+    vulkan::api::GetData(device).dispatch.GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {
+    return vulkan::api::GetData(queue).dispatch.QueueSubmit(queue, submitCount, pSubmits, fence);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkQueueWaitIdle(VkQueue queue) {
+    return vulkan::api::GetData(queue).dispatch.QueueWaitIdle(queue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkDeviceWaitIdle(VkDevice device) {
+    return vulkan::api::GetData(device).dispatch.DeviceWaitIdle(device);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) {
+    return vulkan::api::GetData(device).dispatch.AllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.FreeMemory(device, memory, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) {
+    return vulkan::api::GetData(device).dispatch.MapMemory(device, memory, offset, size, flags, ppData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkUnmapMemory(VkDevice device, VkDeviceMemory memory) {
+    vulkan::api::GetData(device).dispatch.UnmapMemory(device, memory);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) {
+    return vulkan::api::GetData(device).dispatch.FlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) {
+    return vulkan::api::GetData(device).dispatch.InvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceMemoryCommitment(VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes) {
+    vulkan::api::GetData(device).dispatch.GetDeviceMemoryCommitment(device, memory, pCommittedMemoryInBytes);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements) {
+    vulkan::api::GetData(device).dispatch.GetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
+    return vulkan::api::GetData(device).dispatch.BindBufferMemory(device, buffer, memory, memoryOffset);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements) {
+    vulkan::api::GetData(device).dispatch.GetImageMemoryRequirements(device, image, pMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
+    return vulkan::api::GetData(device).dispatch.BindImageMemory(device, image, memory, memoryOffset);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageSparseMemoryRequirements(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements) {
+    vulkan::api::GetData(device).dispatch.GetImageSparseMemoryRequirements(device, image, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceSparseImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties) {
+    vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceSparseImageFormatProperties(physicalDevice, format, type, samples, usage, tiling, pPropertyCount, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkQueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence) {
+    return vulkan::api::GetData(queue).dispatch.QueueBindSparse(queue, bindInfoCount, pBindInfo, fence);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateFence(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence) {
+    return vulkan::api::GetData(device).dispatch.CreateFence(device, pCreateInfo, pAllocator, pFence);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyFence(device, fence, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkResetFences(VkDevice device, uint32_t fenceCount, const VkFence* pFences) {
+    return vulkan::api::GetData(device).dispatch.ResetFences(device, fenceCount, pFences);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetFenceStatus(VkDevice device, VkFence fence) {
+    return vulkan::api::GetData(device).dispatch.GetFenceStatus(device, fence);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkWaitForFences(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout) {
+    return vulkan::api::GetData(device).dispatch.WaitForFences(device, fenceCount, pFences, waitAll, timeout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateSemaphore(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore) {
+    return vulkan::api::GetData(device).dispatch.CreateSemaphore(device, pCreateInfo, pAllocator, pSemaphore);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroySemaphore(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroySemaphore(device, semaphore, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateEvent(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent) {
+    return vulkan::api::GetData(device).dispatch.CreateEvent(device, pCreateInfo, pAllocator, pEvent);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyEvent(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyEvent(device, event, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetEventStatus(VkDevice device, VkEvent event) {
+    return vulkan::api::GetData(device).dispatch.GetEventStatus(device, event);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkSetEvent(VkDevice device, VkEvent event) {
+    return vulkan::api::GetData(device).dispatch.SetEvent(device, event);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkResetEvent(VkDevice device, VkEvent event) {
+    return vulkan::api::GetData(device).dispatch.ResetEvent(device, event);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool) {
+    return vulkan::api::GetData(device).dispatch.CreateQueryPool(device, pCreateInfo, pAllocator, pQueryPool);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyQueryPool(device, queryPool, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags) {
+    return vulkan::api::GetData(device).dispatch.GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
+    return vulkan::api::GetData(device).dispatch.CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyBuffer(device, buffer, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView) {
+    return vulkan::api::GetData(device).dispatch.CreateBufferView(device, pCreateInfo, pAllocator, pView);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyBufferView(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyBufferView(device, bufferView, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage) {
+    return vulkan::api::GetData(device).dispatch.CreateImage(device, pCreateInfo, pAllocator, pImage);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyImage(device, image, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageSubresourceLayout(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout) {
+    vulkan::api::GetData(device).dispatch.GetImageSubresourceLayout(device, image, pSubresource, pLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateImageView(VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView) {
+    return vulkan::api::GetData(device).dispatch.CreateImageView(device, pCreateInfo, pAllocator, pView);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyImageView(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyImageView(device, imageView, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule) {
+    return vulkan::api::GetData(device).dispatch.CreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyShaderModule(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyShaderModule(device, shaderModule, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreatePipelineCache(VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache) {
+    return vulkan::api::GetData(device).dispatch.CreatePipelineCache(device, pCreateInfo, pAllocator, pPipelineCache);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyPipelineCache(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyPipelineCache(device, pipelineCache, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPipelineCacheData(VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData) {
+    return vulkan::api::GetData(device).dispatch.GetPipelineCacheData(device, pipelineCache, pDataSize, pData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkMergePipelineCaches(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches) {
+    return vulkan::api::GetData(device).dispatch.MergePipelineCaches(device, dstCache, srcCacheCount, pSrcCaches);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) {
+    return vulkan::api::GetData(device).dispatch.CreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) {
+    return vulkan::api::GetData(device).dispatch.CreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyPipeline(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyPipeline(device, pipeline, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreatePipelineLayout(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout) {
+    return vulkan::api::GetData(device).dispatch.CreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyPipelineLayout(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyPipelineLayout(device, pipelineLayout, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateSampler(VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler) {
+    return vulkan::api::GetData(device).dispatch.CreateSampler(device, pCreateInfo, pAllocator, pSampler);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroySampler(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroySampler(device, sampler, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateDescriptorSetLayout(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout) {
+    return vulkan::api::GetData(device).dispatch.CreateDescriptorSetLayout(device, pCreateInfo, pAllocator, pSetLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyDescriptorSetLayout(device, descriptorSetLayout, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateDescriptorPool(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool) {
+    return vulkan::api::GetData(device).dispatch.CreateDescriptorPool(device, pCreateInfo, pAllocator, pDescriptorPool);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyDescriptorPool(device, descriptorPool, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags) {
+    return vulkan::api::GetData(device).dispatch.ResetDescriptorPool(device, descriptorPool, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets) {
+    return vulkan::api::GetData(device).dispatch.AllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets) {
+    return vulkan::api::GetData(device).dispatch.FreeDescriptorSets(device, descriptorPool, descriptorSetCount, pDescriptorSets);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkUpdateDescriptorSets(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies) {
+    vulkan::api::GetData(device).dispatch.UpdateDescriptorSets(device, descriptorWriteCount, pDescriptorWrites, descriptorCopyCount, pDescriptorCopies);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer) {
+    return vulkan::api::GetData(device).dispatch.CreateFramebuffer(device, pCreateInfo, pAllocator, pFramebuffer);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyFramebuffer(device, framebuffer, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+    return vulkan::api::GetData(device).dispatch.CreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyRenderPass(device, renderPass, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity) {
+    vulkan::api::GetData(device).dispatch.GetRenderAreaGranularity(device, renderPass, pGranularity);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
+    return vulkan::api::GetData(device).dispatch.CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroyCommandPool(device, commandPool, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags) {
+    return vulkan::api::GetData(device).dispatch.ResetCommandPool(device, commandPool, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkAllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers) {
+    return vulkan::api::GetData(device).dispatch.AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) {
+    vulkan::api::GetData(device).dispatch.FreeCommandBuffers(device, commandPool, commandBufferCount, pCommandBuffers);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo) {
+    return vulkan::api::GetData(commandBuffer).dispatch.BeginCommandBuffer(commandBuffer, pBeginInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEndCommandBuffer(VkCommandBuffer commandBuffer) {
+    return vulkan::api::GetData(commandBuffer).dispatch.EndCommandBuffer(commandBuffer);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkResetCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) {
+    return vulkan::api::GetData(commandBuffer).dispatch.ResetCommandBuffer(commandBuffer, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindPipeline(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBindPipeline(commandBuffer, pipelineBindPoint, pipeline);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetViewport(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetViewport(commandBuffer, firstViewport, viewportCount, pViewports);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetScissor(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetScissor(commandBuffer, firstScissor, scissorCount, pScissors);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetLineWidth(commandBuffer, lineWidth);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthBias(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetDepthBias(commandBuffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetBlendConstants(VkCommandBuffer commandBuffer, const float blendConstants[4]) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetBlendConstants(commandBuffer, blendConstants);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDepthBounds(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetDepthBounds(commandBuffer, minDepthBounds, maxDepthBounds);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetStencilCompareMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetStencilCompareMask(commandBuffer, faceMask, compareMask);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetStencilWriteMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetStencilWriteMask(commandBuffer, faceMask, writeMask);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetStencilReference(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetStencilReference(commandBuffer, faceMask, reference);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBindDescriptorSets(commandBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBindIndexBuffer(commandBuffer, buffer, offset, indexType);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBindVertexBuffers(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDraw(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdDrawIndirect(commandBuffer, buffer, offset, drawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdDrawIndexedIndirect(commandBuffer, buffer, offset, drawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdDispatch(commandBuffer, x, y, z);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdDispatchIndirect(commandBuffer, buffer, offset);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBlitImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdCopyBufferToImage(commandBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdFillBuffer(commandBuffer, dstBuffer, dstOffset, size, data);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdClearColorImage(commandBuffer, image, imageLayout, pColor, rangeCount, pRanges);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdClearDepthStencilImage(commandBuffer, image, imageLayout, pDepthStencil, rangeCount, pRanges);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdClearAttachments(commandBuffer, attachmentCount, pAttachments, rectCount, pRects);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdResolveImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdSetEvent(commandBuffer, event, stageMask);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdResetEvent(commandBuffer, event, stageMask);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdWaitEvents(commandBuffer, eventCount, pEvents, srcStageMask, dstStageMask, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBeginQuery(commandBuffer, queryPool, query, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdEndQuery(commandBuffer, queryPool, query);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdResetQueryPool(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdResetQueryPool(commandBuffer, queryPool, firstQuery, queryCount);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, query);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, stride, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdPushConstants(commandBuffer, layout, stageFlags, offset, size, pValues);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdNextSubpass(commandBuffer, contents);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndRenderPass(VkCommandBuffer commandBuffer) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdEndRenderPass(commandBuffer);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) {
+    vulkan::api::GetData(commandBuffer).dispatch.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(instance).dispatch.DestroySurfaceKHR(instance, surface, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) {
+    return vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) {
+    return vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) {
+    return vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) {
+    return vulkan::api::GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
+    return vulkan::api::GetData(device).dispatch.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::GetData(device).dispatch.DestroySwapchainKHR(device, swapchain, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
+    return vulkan::api::GetData(device).dispatch.GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {
+    return vulkan::api::GetData(device).dispatch.AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) {
+    return vulkan::api::GetData(queue).dispatch.QueuePresentKHR(queue, pPresentInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
+    return vulkan::api::GetData(instance).dispatch.CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+}
+
+// clang-format on
diff --git a/vulkan/libvulkan/dispatch_gen.h b/vulkan/libvulkan/api_gen.h
similarity index 82%
rename from vulkan/libvulkan/dispatch_gen.h
rename to vulkan/libvulkan/api_gen.h
index cef4ccf..779b654 100644
--- a/vulkan/libvulkan/dispatch_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 The Android Open Source Project
+ * Copyright 2016 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.
@@ -16,16 +16,21 @@
 
 // WARNING: This file is generated. See ../README.md for instructions.
 
-#define VK_USE_PLATFORM_ANDROID_KHR
-#include <vulkan/vk_android_native_buffer.h>
+#ifndef LIBVULKAN_API_GEN_H
+#define LIBVULKAN_API_GEN_H
+
+#include <bitset>
 #include <vulkan/vulkan.h>
+#include "driver_gen.h"
 
 namespace vulkan {
+namespace api {
 
 struct InstanceDispatchTable {
     // clang-format off
     PFN_vkDestroyInstance DestroyInstance;
     PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
+    PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
     PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties;
     PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties;
     PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties;
@@ -33,6 +38,7 @@
     PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
     PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
     PFN_vkCreateDevice CreateDevice;
+    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
     PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
@@ -41,14 +47,12 @@
     PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR;
     PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR;
     PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR;
-    PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
-    PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
-    PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
     // clang-format on
 };
 
 struct DeviceDispatchTable {
     // clang-format off
+    PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
     PFN_vkDestroyDevice DestroyDevice;
     PFN_vkGetDeviceQueue GetDeviceQueue;
     PFN_vkQueueSubmit QueueSubmit;
@@ -177,29 +181,16 @@
     // clang-format on
 };
 
-struct DriverDispatchTable {
-    // clang-format off
-    PFN_vkDestroyInstance DestroyInstance;
-    PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
-    PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties;
-    PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties;
-    PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties;
-    PFN_vkGetPhysicalDeviceFeatures GetPhysicalDeviceFeatures;
-    PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
-    PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
-    PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
-    PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
-    PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
-    PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
-    PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
-    PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
-    PFN_vkCreateImage CreateImage;
-    PFN_vkDestroyImage DestroyImage;
-    PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
-    PFN_vkAcquireImageANDROID AcquireImageANDROID;
-    PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
-    // clang-format on
-};
+bool InitDispatchTable(
+    VkInstance instance,
+    PFN_vkGetInstanceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
 
+}  // namespace api
 }  // namespace vulkan
+
+#endif  // LIBVULKAN_API_GEN_H
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
new file mode 100644
index 0000000..3968371
--- /dev/null
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -0,0 +1,1108 @@
+{{define "Copyright"}}
+/*
+•* Copyright 2016 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.
+•*/
+¶{{end}}
+
+{{Include "../api/templates/vulkan_common.tmpl"}}
+{{Global "clang-format" (Strings "clang-format" "-style=file")}}
+{{Macro "DefineGlobals" $}}
+{{$ | Macro "api_gen.h"   | Format (Global "clang-format") | Write "api_gen.h"  }}
+{{$ | Macro "api_gen.cpp" | Format (Global "clang-format") | Write "api_gen.cpp"}}
+{{$ | Macro "driver_gen.h" | Format (Global "clang-format") | Write "driver_gen.h"}}
+{{$ | Macro "driver_gen.cpp" | Format (Global "clang-format") | Write "driver_gen.cpp"}}
+
+{{/*
+-------------------------------------------------------------------------------
+  api_gen.h
+-------------------------------------------------------------------------------
+*/}}
+{{define "api_gen.h"}}
+{{Macro "Copyright"}}

+// WARNING: This file is generated. See ../README.md for instructions.

+#ifndef LIBVULKAN_API_GEN_H
+#define LIBVULKAN_API_GEN_H

+#include <bitset>
+#include <vulkan/vulkan.h>
+#include "driver_gen.h"

+namespace vulkan {«
+namespace api {«

+struct InstanceDispatchTable {
+  // clang-format off
+  {{range $f := AllCommands $}}
+    {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}}
+      {{Macro "C++.DeclareTableEntry" $f}};
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+struct DeviceDispatchTable {
+  // clang-format off
+  {{range $f := AllCommands $}}
+    {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}}
+      {{Macro "C++.DeclareTableEntry" $f}};
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+bool InitDispatchTable(
+    VkInstance instance,
+    PFN_vkGetInstanceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions);
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions);

+»} // namespace api
+»} // namespace vulkan

+#endif // LIBVULKAN_API_GEN_H
+¶{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  api_gen.cpp
+-------------------------------------------------------------------------------
+*/}}
+{{define "api_gen.cpp"}}
+{{Macro "Copyright"}}

+// WARNING: This file is generated. See ../README.md for instructions.

+#include <string.h>
+#include <algorithm>
+#include <log/log.h>

+#include "api.h"

+namespace vulkan {«
+namespace api {«

+{{Macro "C++.DefineInitProcMacro" "dispatch"}}

+{{Macro "api.C++.DefineInitProcExtMacro"}}

+namespace {«

+// clang-format off

+{{range $f := AllCommands $}}
+  {{Macro "api.C++.DefineExtensionStub" $f}}
+{{end}}
+// clang-format on

+»} // anonymous

+bool InitDispatchTable(
+    VkInstance instance,
+    PFN_vkGetInstanceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
+    auto& data = GetData(instance);
+    bool success = true;
+    ¶
+    // clang-format off
+    {{range $f := AllCommands $}}
+      {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}}
+        {{Macro "C++.InitProc" $f}}
+      {{end}}
+    {{end}}
+    // clang-format on
+    ¶
+    return success;
+}

+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
+    auto& data = GetData(dev);
+    bool success = true;
+    ¶
+    // clang-format off
+    {{range $f := AllCommands $}}
+      {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}}
+        {{Macro "C++.InitProc" $f}}
+      {{end}}
+    {{end}}
+    // clang-format on
+    ¶
+    return success;
+}

+»} // namespace api
+»} // namespace vulkan

+// clang-format off

+{{range $f := AllCommands $}}
+  {{if (Macro "IsFunctionExported" $f)}}
+    __attribute__((visibility("default")))
+    VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) {
+      {{     if eq $f.Name "vkGetInstanceProcAddr"}}
+        {{Macro "api.C++.InterceptInstanceProcAddr" $}}
+      {{else if eq $f.Name "vkGetDeviceProcAddr"}}
+        {{Macro "api.C++.InterceptDeviceProcAddr" $}}
+      {{end}}
+
+      {{Macro "api.C++.Dispatch" $f}}
+    }
+    ¶
+  {{end}}
+{{end}}

+// clang-format on
+¶{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  driver_gen.h
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver_gen.h"}}
+{{Macro "Copyright"}}

+// WARNING: This file is generated. See ../README.md for instructions.

+#ifndef LIBVULKAN_DRIVER_GEN_H
+#define LIBVULKAN_DRIVER_GEN_H

+#include <bitset>
+#include <vulkan/vulkan.h>
+#include <vulkan/vk_android_native_buffer.h>

+namespace vulkan {«
+namespace driver {«

+{{Macro "driver.C++.DefineProcHookType"}}

+struct InstanceDriverTable {
+  // clang-format off
+  {{range $f := AllCommands $}}
+    {{if (Macro "driver.IsInstanceDriverTableEntry" $f)}}
+      {{Macro "C++.DeclareTableEntry" $f}};
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+struct DeviceDriverTable {
+  // clang-format off
+  {{range $f := AllCommands $}}
+    {{if (Macro "driver.IsDeviceDriverTableEntry" $f)}}
+      {{Macro "C++.DeclareTableEntry" $f}};
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+const ProcHook* GetProcHook(const char* name);
+ProcHook::Extension GetProcHookExtension(const char* name);

+bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions);
+bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions);

+»} // namespace driver
+»} // namespace vulkan

+#endif // LIBVULKAN_DRIVER_TABLE_H
+¶{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  driver_gen.cpp
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver_gen.cpp"}}
+{{Macro "Copyright"}}

+// WARNING: This file is generated. See ../README.md for instructions.

+#include <string.h>
+#include <algorithm>
+#include <log/log.h>

+#include "driver.h"

+namespace vulkan {«
+namespace driver {«

+namespace {«

+// clang-format off

+{{range $f := AllCommands $}}
+  {{Macro "driver.C++.DefineProcHookStub" $f}}
+{{end}}
+// clang-format on

+const ProcHook g_proc_hooks[] = {
+  // clang-format off
+  {{range $f := SortBy (AllCommands $) "FunctionName"}}
+    {{if (Macro "driver.IsIntercepted" $f)}}
+      {{     if (Macro "IsGloballyDispatched" $f)}}
+        {{Macro "driver.C++.DefineGlobalProcHook" $f}}
+      {{else if (Macro "IsInstanceDispatched" $f)}}
+        {{Macro "driver.C++.DefineInstanceProcHook" $f}}
+      {{else if (Macro "IsDeviceDispatched" $f)}}
+        {{Macro "driver.C++.DefineDeviceProcHook" $f}}
+      {{end}}
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+»} // anonymous

+const ProcHook* GetProcHook(const char* name) {
+    const auto& begin = g_proc_hooks;
+    const auto& end = g_proc_hooks +
+      sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
+    const auto hook = std::lower_bound(begin, end, name,
+        [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
+    return (hook <  end && strcmp(hook->name, name) == 0) ? hook : nullptr;
+}

+ProcHook::Extension GetProcHookExtension(const char* name) {
+  {{$exts := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
+  // clang-format off
+  {{range $e := $exts}}
+    if (strcmp(name, "{{$e}}") == 0) return ProcHook::{{TrimPrefix "VK_" $e}};
+  {{end}}
+  // clang-format on
+  return ProcHook::EXTENSION_UNKNOWN;
+}

+{{Macro "C++.DefineInitProcMacro" "driver"}}

+{{Macro "driver.C++.DefineInitProcExtMacro"}}

+bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
+{
+    auto& data = GetData(instance);
+    bool success = true;
+    ¶
+    // clang-format off
+    {{range $f := AllCommands $}}
+      {{if (Macro "driver.IsInstanceDriverTableEntry" $f)}}
+        {{Macro "C++.InitProc" $f}}
+      {{end}}
+    {{end}}
+    // clang-format on
+    ¶
+    return success;
+}

+bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
+{
+    auto& data = GetData(dev);
+    bool success = true;
+    ¶
+    // clang-format off
+    {{range $f := AllCommands $}}
+      {{if (Macro "driver.IsDeviceDriverTableEntry" $f)}}
+        {{Macro "C++.InitProc" $f}}
+      {{end}}
+    {{end}}
+    // clang-format on
+    ¶
+    return success;
+}

+»} // namespace driver
+»} // namespace vulkan

+// clang-format on
+¶{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits a declaration of a dispatch/driver table entry.
+------------------------------------------------------------------------------
+*/}}
+{{define "C++.DeclareTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{Macro "FunctionPtrName" $}} {{Macro "BaseName" $}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits INIT_PROC macro.
+-------------------------------------------------------------------------------
+*/}}
+{{define "C++.DefineInitProcMacro"}}
+  #define UNLIKELY(expr) __builtin_expect((expr), 0)
+  ¶
+  #define INIT_PROC(obj, proc) do {                             \
+      data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>(       \
+              get_proc(obj, "vk" # proc));                      \
+      if (UNLIKELY(!data.{{$}}.proc)) {                         \
+          ALOGE("missing " # obj " proc: vk" # proc);           \
+          success = false;                                      \
+      }                                                         \
+  } while(0)
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits code to invoke INIT_PROC or INIT_PROC_EXT.
+-------------------------------------------------------------------------------
+*/}}
+{{define "C++.InitProc"}}
+  {{AssertType $ "Function"}}
+
+  {{$ext := GetAnnotation $ "extension"}}
+  {{if $ext}}
+    INIT_PROC_EXT({{Macro "BaseName" $ext}}, §
+  {{else}}
+    INIT_PROC(§
+  {{end}}
+
+  {{if (Macro "IsInstanceDispatched" $)}}
+    instance, §
+  {{else}}
+    dev, §
+  {{end}}
+
+  {{Macro "BaseName" $}});
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is exported and instance-dispatched.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.IsInstanceDispatchTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is exported and device-dispatched.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.IsDeviceDispatchTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "IsFunctionExported" $) (Macro "IsDeviceDispatched" $)}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is intercepted by vulkan::api.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.IsIntercepted"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "IsFunctionSupported" $)}}
+    {{/* Global functions cannot be dispatched at all */}}
+    {{     if (Macro "IsGloballyDispatched" $)}}true
+
+    {{/* VkPhysicalDevice functions that manage device layers */}}
+    {{else if eq $.Name "vkCreateDevice"}}true
+    {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
+    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
+
+    {{/* Destroy functions of dispatchable objects */}}
+    {{else if eq $.Name "vkDestroyInstance"}}true
+    {{else if eq $.Name "vkDestroyDevice"}}true
+
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits INIT_PROC_EXT macro for vulkan::api.
+-------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.DefineInitProcExtMacro"}}
+  // Exported extension functions may be invoked even when their extensions
+  // are disabled.  Dispatch to stubs when that happens.
+  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+      if (extensions[driver::ProcHook::ext])                    \
+        INIT_PROC(obj, proc);                                   \
+      else                                                      \
+        data.dispatch.proc = disabled ## proc;                  \
+  } while(0)
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits a stub for an exported extension function.
+-------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.DefineExtensionStub"}}
+  {{AssertType $ "Function"}}
+
+  {{$ext := GetAnnotation $ "extension"}}
+  {{if and $ext (Macro "IsFunctionExported" $)}}
+    {{$ext_name := index $ext.Arguments 0}}
+
+    {{$base := (Macro "BaseName" $)}}
+    {{$unnamed_params := (ForEach $.CallParameters "ParameterType" | JoinWith ", ")}}
+
+    VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$unnamed_params}}) {
+      ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
+      {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
+    }
+    ¶
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits code for vkGetInstanceProcAddr for function interception.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.InterceptInstanceProcAddr"}}
+  {{AssertType $ "API"}}
+
+  // global functions
+  if (!instance) {
+    {{range $f := AllCommands $}}
+      {{if (Macro "IsGloballyDispatched" $f)}}
+        if (strcmp(pName, "{{$f.Name}}") == 0) return §
+          reinterpret_cast<PFN_vkVoidFunction>(§
+            vulkan::api::{{Macro "BaseName" $f}});
+      {{end}}
+    {{end}}
+    ¶
+    ALOGE("vkGetInstanceProcAddr called with %s without instance",  pName);
+    return nullptr;
+  }
+  ¶
+  static const struct Hook {
+    const char* name;
+    PFN_vkVoidFunction proc;
+  } hooks[] = {
+    {{range $f := SortBy (AllCommands $) "FunctionName"}}
+      {{if (Macro "IsFunctionExported" $f)}}
+        {{/* hide global functions */}}
+        {{if (Macro "IsGloballyDispatched" $f)}}
+          { "{{$f.Name}}", nullptr },
+
+        {{/* redirect intercepted functions */}}
+        {{else if (Macro "api.IsIntercepted" $f)}}
+          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
+            vulkan::api::{{Macro "BaseName" $f}}) },
+
+        {{/* redirect vkGetInstanceProcAddr to itself */}}
+        {{else if eq $f.Name "vkGetInstanceProcAddr"}}
+          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}}) },
+
+        {{/* redirect device functions to themselves as a workaround for
+             layers that do not intercept in their vkGetInstanceProcAddr */}}
+        {{else if (Macro "IsDeviceDispatched" $f)}}
+          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}}) },
+
+        {{end}}
+      {{end}}
+    {{end}}
+  };
+  // clang-format on
+  constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
+  auto hook = std::lower_bound(
+    hooks, hooks + count, pName,
+    [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
+  if (hook <  hooks + count && strcmp(hook->name, pName) == 0) {
+    if (!hook->proc)
+      ALOGE("vkGetInstanceProcAddr called with %s with instance",  pName);
+    return hook->proc;
+  }
+  // clang-format off
+  ¶
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits code for vkGetDeviceProcAddr for function interception.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.InterceptDeviceProcAddr"}}
+  {{AssertType $ "API"}}
+
+  if (device == VK_NULL_HANDLE) {
+    ALOGE("vkGetDeviceProcAddr called with invalid device");
+    return nullptr;
+  }
+  ¶
+  static const char* const known_non_device_names[] = {
+    {{range $f := SortBy (AllCommands $) "FunctionName"}}
+      {{if (Macro "IsFunctionSupported" $f)}}
+        {{if not (Macro "IsDeviceDispatched" $f)}}
+          "{{$f.Name}}",
+        {{end}}
+      {{end}}
+    {{end}}
+  };
+  // clang-format on
+  constexpr size_t count = sizeof(known_non_device_names) /
+    sizeof(known_non_device_names[0]);
+  if (!pName ||
+      std::binary_search(
+        known_non_device_names, known_non_device_names + count, pName,
+        [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
+    ALOGE("vkGetDeviceProcAddr called with %s", (pName) ? pName : "(null)");
+    return nullptr;
+  }
+  // clang-format off
+  ¶
+  {{range $f := AllCommands $}}
+    {{if (Macro "IsDeviceDispatched" $f)}}
+      {{     if (Macro "api.IsIntercepted" $f)}}
+        if (strcmp(pName, "{{$f.Name}}") == 0) return §
+          reinterpret_cast<PFN_vkVoidFunction>(§
+            vulkan::api::{{Macro "BaseName" $f}});
+      {{else if eq $f.Name "vkGetDeviceProcAddr"}}
+        if (strcmp(pName, "{{$f.Name}}") == 0) return §
+          reinterpret_cast<PFN_vkVoidFunction>(§
+            {{$f.Name}});
+      {{end}}
+    {{end}}
+  {{end}}
+  ¶
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits code to dispatch a function.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.Dispatch"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "api.IsIntercepted" $)}}// call into api.cpp{{end}}
+  {{if not (IsVoid $.Return.Type)}}return §{{end}}
+
+  {{if (Macro "api.IsIntercepted" $)}}
+    vulkan::api::§
+  {{else}}
+    {{$p0 := index $.CallParameters 0}}
+    vulkan::api::GetData({{$p0.Name}}).dispatch.§
+  {{end}}
+
+  {{Macro "BaseName" $}}({{Macro "Arguments" $}});
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits a list of extensions intercepted by vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.InterceptedExtensions"}}
+VK_ANDROID_native_buffer
+VK_EXT_debug_report
+VK_KHR_android_surface
+VK_KHR_surface
+VK_KHR_swapchain
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if an extension is intercepted by vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.IsExtensionIntercepted"}}
+  {{$ext_name := index $.Arguments 0}}
+  {{$filters := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
+
+  {{range $f := $filters}}
+    {{if eq $ext_name $f}}true{{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is intercepted by vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.IsIntercepted"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "IsFunctionSupported" $)}}
+    {{/* Create functions of dispatchable objects */}}
+    {{     if eq $.Name "vkCreateInstance"}}true
+    {{else if eq $.Name "vkCreateDevice"}}true
+    {{else if eq $.Name "vkEnumeratePhysicalDevices"}}true
+    {{else if eq $.Name "vkGetDeviceQueue"}}true
+    {{else if eq $.Name "vkAllocateCommandBuffers"}}true
+
+    {{/* Destroy functions of dispatchable objects */}}
+    {{else if eq $.Name "vkDestroyInstance"}}true
+    {{else if eq $.Name "vkDestroyDevice"}}true
+
+    {{/* Enumeration of extensions */}}
+    {{else if eq $.Name "vkEnumerateInstanceExtensionProperties"}}true
+    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
+
+    {{else if eq $.Name "vkGetInstanceProcAddr"}}true
+    {{else if eq $.Name "vkGetDeviceProcAddr"}}true
+
+    {{end}}
+
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      {{Macro "driver.IsExtensionIntercepted" $ext}}
+    {{end}}
+
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function needs a ProcHook stub.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.NeedProcHookStub"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "driver.IsIntercepted" $) (Macro "IsDeviceDispatched" $)}}
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      {{if not (Macro "IsExtensionInternal" $ext)}}true{{end}}
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits definition of struct ProcHook.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.C++.DefineProcHookType"}}
+  struct ProcHook {
+      enum Type {
+        GLOBAL,
+        INSTANCE,
+        DEVICE,
+      };
+
+      enum Extension {
+        {{$exts := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
+        {{range $e := $exts}}
+          {{TrimPrefix "VK_" $e}},
+        {{end}}
+        ¶
+        EXTENSION_CORE, // valid bit
+        EXTENSION_COUNT,
+        EXTENSION_UNKNOWN,
+      };
+      ¶
+      const char* name;
+      Type type;
+      Extension extension;
+      ¶
+      PFN_vkVoidFunction proc;
+      PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
+  };
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits INIT_PROC_EXT macro for vulkan::driver.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.C++.DefineInitProcExtMacro"}}
+  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+      if (extensions[ProcHook::ext])                            \
+        INIT_PROC(obj, proc);                                   \
+  } while(0)
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits a stub for ProcHook::checked_proc.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.C++.DefineProcHookStub"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "driver.NeedProcHookStub" $)}}
+    {{$ext := GetAnnotation $ "extension"}}
+    {{$ext_name := index $ext.Arguments 0}}
+
+    {{$base := (Macro "BaseName" $)}}
+    {{$unnamed_params := (ForEach $.CallParameters "ParameterType" | JoinWith ", ")}}
+
+    VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
+      {{$p0 := index $.CallParameters 0}}
+      {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}}
+
+      if (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) {
+        {{if not (IsVoid $.Return.Type)}}return §{{end}}
+        {{$base}}({{Macro "Arguments" $}});
+      } else {
+        ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
+        {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
+      }
+    }
+    ¶
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits definition of a global ProcHook.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.C++.DefineGlobalProcHook"}}
+  {{AssertType $ "Function"}}
+
+  {{$base := (Macro "BaseName" $)}}
+
+  {{$ext := GetAnnotation $ "extension"}}
+  {{if $ext}}
+    {{Error "invalid global extension"}}
+  {{end}}
+
+  {
+    "{{$.Name}}",
+    ProcHook::GLOBAL,
+    ProcHook::EXTENSION_CORE,
+    reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
+    nullptr,
+  },
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits definition of an instance ProcHook.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.C++.DefineInstanceProcHook"}}
+  {{AssertType $ "Function"}}
+
+  {{$base := (Macro "BaseName" $)}}
+
+  {
+    "{{$.Name}}",
+    ProcHook::INSTANCE,
+
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      ProcHook::{{Macro "BaseName" $ext}},
+
+      {{if (Macro "IsExtensionInternal" $ext)}}
+        nullptr,
+        nullptr,
+      {{else}}
+        reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
+        nullptr,
+      {{end}}
+    {{else}}
+      ProcHook::EXTENSION_CORE,
+      reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
+      nullptr,
+    {{end}}
+  },
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits definition of a device ProcHook.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.C++.DefineDeviceProcHook"}}
+  {{AssertType $ "Function"}}
+
+  {{$base := (Macro "BaseName" $)}}
+
+  {
+    "{{$.Name}}",
+    ProcHook::DEVICE,
+
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      ProcHook::{{Macro "BaseName" $ext}},
+
+      {{if (Macro "IsExtensionInternal" $ext)}}
+        nullptr,
+        nullptr,
+      {{else}}
+        reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
+        reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}),
+      {{end}}
+    {{else}}
+      ProcHook::EXTENSION_CORE,
+      reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
+      nullptr,
+    {{end}}
+  },
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits true if a function is needed by vulkan::driver.
+-------------------------------------------------------------------------------
+*/}}
+{{define "driver.IsDriverTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "IsFunctionSupported" $)}}
+    {{/* Create functions of dispatchable objects */}}
+    {{     if eq $.Name "vkCreateDevice"}}true
+    {{else if eq $.Name "vkGetDeviceQueue"}}true
+    {{else if eq $.Name "vkAllocateCommandBuffers"}}true
+
+    {{/* Destroy functions of dispatchable objects */}}
+    {{else if eq $.Name "vkDestroyInstance"}}true
+    {{else if eq $.Name "vkDestroyDevice"}}true
+
+    {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
+
+    {{/* Enumeration of extensions */}}
+    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
+
+    {{/* We cache physical devices in loader.cpp */}}
+    {{else if eq $.Name "vkEnumeratePhysicalDevices"}}true
+
+    {{else if eq $.Name "vkGetInstanceProcAddr"}}true
+    {{else if eq $.Name "vkGetDeviceProcAddr"}}true
+
+    {{/* VK_KHR_swapchain->VK_ANDROID_native_buffer translation */}}
+    {{else if eq $.Name "vkCreateImage"}}true
+    {{else if eq $.Name "vkDestroyImage"}}true
+
+    {{end}}
+
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      {{$ext_name := index $ext.Arguments 0}}
+      {{     if eq $ext_name "VK_ANDROID_native_buffer"}}true
+      {{else if eq $ext_name "VK_EXT_debug_report"}}true
+      {{end}}
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if an instance-dispatched function is needed by vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.IsInstanceDriverTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "driver.IsDriverTableEntry" $) (Macro "IsInstanceDispatched" $)}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a device-dispatched function is needed by vulkan::driver.
+------------------------------------------------------------------------------
+*/}}
+{{define "driver.IsDeviceDriverTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "driver.IsDriverTableEntry" $) (Macro "IsDeviceDispatched" $)}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits a function/extension name without the "vk"/"VK_" prefix.
+-------------------------------------------------------------------------------
+*/}}
+{{define "BaseName"}}
+  {{     if IsFunction $}}{{TrimPrefix "vk" $.Name}}
+  {{else if eq $.Name "extension"}}{{TrimPrefix "VK_" (index $.Arguments 0)}}
+  {{else}}{{Error "invalid use of BaseName"}}
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits a comma-separated list of C parameter names for the given command.
+-------------------------------------------------------------------------------
+*/}}
+{{define "Arguments"}}
+  {{AssertType $ "Function"}}
+
+  {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+*/}}
+{{define "IsGloballyDispatched"}}
+  {{AssertType $ "Function"}}
+  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Global")}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" for supported functions that undergo table dispatch. Only global
+  functions and functions handled in the loader top without calling into
+  lower layers are not dispatched.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsInstanceDispatched"}}
+  {{AssertType $ "Function"}}
+  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" for supported functions that can have device-specific dispatch.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsDeviceDispatched"}}
+  {{AssertType $ "Function"}}
+  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Device")}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" if a function is core or from a supportable extension.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsFunctionSupported"}}
+  {{AssertType $ "Function"}}
+  {{if not (GetAnnotation $ "pfn")}}
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if not $ext}}true
+    {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Decides whether a function should be exported from the Android Vulkan
+  library. Functions in the core API and in loader extensions are exported.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsFunctionExported"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "IsFunctionSupported" $)}}
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      {{Macro "IsExtensionExported" $ext}}
+    {{else}}
+      true
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" if an extension is unsupportable on Android.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsExtensionBlacklisted"}}
+  {{$ext := index $.Arguments 0}}
+  {{     if eq $ext "VK_KHR_display"}}true
+  {{else if eq $ext "VK_KHR_display_swapchain"}}true
+  {{else if eq $ext "VK_KHR_xlib_surface"}}true
+  {{else if eq $ext "VK_KHR_xcb_surface"}}true
+  {{else if eq $ext "VK_KHR_wayland_surface"}}true
+  {{else if eq $ext "VK_KHR_mir_surface"}}true
+  {{else if eq $ext "VK_KHR_win32_surface"}}true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Reports whether an extension is implemented entirely by the loader,
+  so drivers should not enumerate it.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsExtensionExported"}}
+  {{$ext := index $.Arguments 0}}
+  {{     if eq $ext "VK_KHR_surface"}}true
+  {{else if eq $ext "VK_KHR_swapchain"}}true
+  {{else if eq $ext "VK_KHR_android_surface"}}true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Reports whether an extension is internal to the loader and drivers,
+  so the loader should not enumerate it.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsExtensionInternal"}}
+  {{$ext := index $.Arguments 0}}
+  {{     if eq $ext "VK_ANDROID_native_buffer"}}true
+  {{end}}
+{{end}}
diff --git a/vulkan/libvulkan/debug_report.cpp b/vulkan/libvulkan/debug_report.cpp
index 41b6040..c4a1174 100644
--- a/vulkan/libvulkan/debug_report.cpp
+++ b/vulkan/libvulkan/debug_report.cpp
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-#include "loader.h"
+#include "driver.h"
 
 namespace vulkan {
+namespace driver {
 
 VkResult DebugReportCallbackList::CreateCallback(
     VkInstance instance,
@@ -25,24 +26,22 @@
     VkDebugReportCallbackEXT* callback) {
     VkDebugReportCallbackEXT driver_callback = VK_NULL_HANDLE;
 
-    if (GetDriverDispatch(instance).CreateDebugReportCallbackEXT) {
-        VkResult result =
-            GetDriverDispatch(instance).CreateDebugReportCallbackEXT(
-                GetDriverInstance(instance), create_info, allocator,
-                &driver_callback);
+    if (GetData(instance).driver.CreateDebugReportCallbackEXT) {
+        VkResult result = GetData(instance).driver.CreateDebugReportCallbackEXT(
+            instance, create_info, allocator, &driver_callback);
         if (result != VK_SUCCESS)
             return result;
     }
 
     const VkAllocationCallbacks* alloc =
-        allocator ? allocator : GetAllocator(instance);
+        allocator ? allocator : &GetData(instance).allocator;
     void* mem =
         alloc->pfnAllocation(alloc->pUserData, sizeof(Node), alignof(Node),
                              VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
     if (!mem) {
-        if (GetDriverDispatch(instance).DestroyDebugReportCallbackEXT) {
-            GetDriverDispatch(instance).DestroyDebugReportCallbackEXT(
-                GetDriverInstance(instance), driver_callback, allocator);
+        if (GetData(instance).driver.DestroyDebugReportCallbackEXT) {
+            GetData(instance).driver.DestroyDebugReportCallbackEXT(
+                instance, driver_callback, allocator);
         }
         return VK_ERROR_OUT_OF_HOST_MEMORY;
     }
@@ -68,13 +67,13 @@
     prev->next = node->next;
     lock.unlock();
 
-    if (GetDriverDispatch(instance).DestroyDebugReportCallbackEXT) {
-        GetDriverDispatch(instance).DestroyDebugReportCallbackEXT(
-            GetDriverInstance(instance), node->driver_callback, allocator);
+    if (GetData(instance).driver.DestroyDebugReportCallbackEXT) {
+        GetData(instance).driver.DestroyDebugReportCallbackEXT(
+            instance, node->driver_callback, allocator);
     }
 
     const VkAllocationCallbacks* alloc =
-        allocator ? allocator : GetAllocator(instance);
+        allocator ? allocator : &GetData(instance).allocator;
     alloc->pfnFree(alloc->pUserData, node);
 }
 
@@ -95,40 +94,40 @@
     }
 }
 
-VkResult CreateDebugReportCallbackEXT_Bottom(
+VkResult CreateDebugReportCallbackEXT(
     VkInstance instance,
     const VkDebugReportCallbackCreateInfoEXT* create_info,
     const VkAllocationCallbacks* allocator,
     VkDebugReportCallbackEXT* callback) {
-    return GetDebugReportCallbacks(instance).CreateCallback(
+    return GetData(instance).debug_report_callbacks.CreateCallback(
         instance, create_info, allocator, callback);
 }
 
-void DestroyDebugReportCallbackEXT_Bottom(
-    VkInstance instance,
-    VkDebugReportCallbackEXT callback,
-    const VkAllocationCallbacks* allocator) {
+void DestroyDebugReportCallbackEXT(VkInstance instance,
+                                   VkDebugReportCallbackEXT callback,
+                                   const VkAllocationCallbacks* allocator) {
     if (callback)
-        GetDebugReportCallbacks(instance).DestroyCallback(instance, callback,
-                                                          allocator);
+        GetData(instance).debug_report_callbacks.DestroyCallback(
+            instance, callback, allocator);
 }
 
-void DebugReportMessageEXT_Bottom(VkInstance instance,
-                                  VkDebugReportFlagsEXT flags,
-                                  VkDebugReportObjectTypeEXT object_type,
-                                  uint64_t object,
-                                  size_t location,
-                                  int32_t message_code,
-                                  const char* layer_prefix,
-                                  const char* message) {
-    if (GetDriverDispatch(instance).DebugReportMessageEXT) {
-        GetDriverDispatch(instance).DebugReportMessageEXT(
-            GetDriverInstance(instance), flags, object_type, object, location,
-            message_code, layer_prefix, message);
+void DebugReportMessageEXT(VkInstance instance,
+                           VkDebugReportFlagsEXT flags,
+                           VkDebugReportObjectTypeEXT object_type,
+                           uint64_t object,
+                           size_t location,
+                           int32_t message_code,
+                           const char* layer_prefix,
+                           const char* message) {
+    if (GetData(instance).driver.DebugReportMessageEXT) {
+        GetData(instance).driver.DebugReportMessageEXT(
+            instance, flags, object_type, object, location, message_code,
+            layer_prefix, message);
     }
-    GetDebugReportCallbacks(instance).Message(flags, object_type, object,
-                                              location, message_code,
-                                              layer_prefix, message);
+    GetData(instance).debug_report_callbacks.Message(flags, object_type, object,
+                                                     location, message_code,
+                                                     layer_prefix, message);
 }
 
+}  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index c6f7570..72b1887 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -17,14 +17,16 @@
 #ifndef LIBVULKAN_DEBUG_REPORT_H
 #define LIBVULKAN_DEBUG_REPORT_H 1
 
+#include <vulkan/vulkan.h>
 #include <shared_mutex>
 
 namespace vulkan {
+namespace driver {
 
 // clang-format off
-VKAPI_ATTR VkResult CreateDebugReportCallbackEXT_Bottom(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
-VKAPI_ATTR void DestroyDebugReportCallbackEXT_Bottom(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR void DebugReportMessageEXT_Bottom(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
+VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
+VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
 // clang-format on
 
 class DebugReportCallbackList {
@@ -65,6 +67,7 @@
     Node head_;
 };
 
+}  // namespace driver
 }  // namespace vulkan
 
 #endif  // LIBVULKAN_DEBUG_REPORT_H
diff --git a/vulkan/libvulkan/dispatch.tmpl b/vulkan/libvulkan/dispatch.tmpl
deleted file mode 100644
index 0a0338e..0000000
--- a/vulkan/libvulkan/dispatch.tmpl
+++ /dev/null
@@ -1,624 +0,0 @@
-{{/*
- * Copyright 2015 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 "../api/templates/vulkan_common.tmpl"}}
-{{Global "clang-format" (Strings "clang-format" "-style=file")}}
-{{Macro "DefineGlobals" $}}
-{{$ | Macro "dispatch_gen.h"   | Format (Global "clang-format") | Write "dispatch_gen.h"  }}
-{{$ | Macro "dispatch_gen.cpp" | Format (Global "clang-format") | Write "dispatch_gen.cpp"}}
-
-{{/*
--------------------------------------------------------------------------------
-  dispatch_gen.h
--------------------------------------------------------------------------------
-*/}}
-{{define "dispatch_gen.h"}}
-/*
-•* Copyright 2015 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.
-•*/

-// WARNING: This file is generated. See ../README.md for instructions.

-#define VK_USE_PLATFORM_ANDROID_KHR
-#include <vulkan/vk_android_native_buffer.h>
-#include <vulkan/vulkan.h>

-namespace vulkan {

-struct InstanceDispatchTable {«
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsInstanceDispatched" $f)}}
-      {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}};
-    {{end}}
-  {{end}}
-  // clang-format on
-»};

-struct DeviceDispatchTable {«
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsDeviceDispatched" $f)}}
-      {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}};
-    {{end}}
-  {{end}}
-  // clang-format on
-»};

-struct DriverDispatchTable {«
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsInstanceDispatched" $f)}}
-      {{if not (Macro "IsLoaderFunction" $f)}}
-        {{Macro "FunctionPtrName" $f}} {{Macro "BaseName" $f}};
-      {{end}}
-    {{end}}
-  {{end}}
-
-    PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
-
-    {{/* TODO(jessehall): Needed by swapchain code. Figure out a better way of
-         handling this that avoids the special case. Probably should rework
-         things so the driver dispatch table has all driver functions. Probably
-         need separate instance- and device-level copies, fill in all device-
-         dispatched functions in the device-level copies only, and change
-         GetDeviceProcAddr_Bottom to look in the already-loaded driver
-         dispatch table rather than forwarding to the driver's
-         vkGetDeviceProcAddr. */}}
-    PFN_vkCreateImage CreateImage;
-    PFN_vkDestroyImage DestroyImage;
-
-    PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
-    PFN_vkAcquireImageANDROID AcquireImageANDROID;
-    PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
-  // clang-format on
-»};

-} // namespace vulkan
-¶{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  dispatch_gen.cpp
--------------------------------------------------------------------------------
-*/}}
-{{define "dispatch_gen.cpp"}}
-/*
-•* Copyright 2015 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.
-•*/

-// WARNING: This file is generated. See ../README.md for instructions.

-#include <log/log.h>
-#include <algorithm>
-#include "loader.h"

-#define UNLIKELY(expr) __builtin_expect((expr), 0)

-using namespace vulkan;

-namespace {

-struct NameProc {
-    const char* name;
-    PFN_vkVoidFunction proc;
-};

-PFN_vkVoidFunction Lookup(const char* name, const NameProc* begin, const NameProc* end) {
-    const auto& entry = std::lower_bound(
-        begin, end, name,
-        [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; });
-    if (entry == end || strcmp(entry->name, name) != 0)
-        return nullptr;
-    return entry->proc;
-}

-template <size_t N>
-PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) {
-    return Lookup(name, procs, procs + N);
-}

-const NameProc kLoaderExportProcs[] = {«
-    // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "IsExported" $f)}}
-      {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}})},
-    {{end}}
-  {{end}}
-    // clang-format on
-»};

-const NameProc kLoaderGlobalProcs[] = {«
-    // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if and (Macro "HasLoaderTopImpl" $f) (eq (Macro "Vtbl" $f) "Global")}}
-      {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
-        static_cast<{{Macro "FunctionPtrName" $f}}>(§
-          {{Macro "BaseName" $f}}_Top))},
-    {{end}}
-  {{end}}
-    // clang-format on
-»};

-const NameProc kLoaderTopProcs[] = {«
-    // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "HasLoaderTopImpl" $f)}}
-      {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
-        static_cast<{{Macro "FunctionPtrName" $f}}>(§
-          {{Macro "BaseName" $f}}_Top))},
-    {{end}}
-  {{end}}
-    // clang-format on
-»};

-const NameProc kLoaderBottomProcs[] = {«
-    // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "HasLoaderBottomImpl" $f)}}
-    {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
-        static_cast<{{Macro "FunctionPtrName" $f}}>(§
-            {{Macro "BaseName" $f}}_Bottom))},
-    {{end}}
-  {{end}}
-    // clang-format on
-»};

-struct NameOffset {
-    const char* name;
-    size_t offset;
-};

-ssize_t Lookup(const char* name,
-               const NameOffset* begin,
-               const NameOffset* end) {
-    const auto& entry = std::lower_bound(
-        begin, end, name, [](const NameOffset& e, const char* n) {
-            return strcmp(e.name, n) < 0;
-        });
-    if (entry == end || strcmp(entry->name, name) != 0)
-        return -1;
-    return static_cast<ssize_t>(entry->offset);
-}

-template <size_t N, class Table>
-PFN_vkVoidFunction Lookup(const char* name,
-                          const NameOffset (&offsets)[N],
-                          const Table& table) {
-    ssize_t offset = Lookup(name, offsets, offsets + N);
-    if (offset < 0)
-        return nullptr;
-    uintptr_t base = reinterpret_cast<uintptr_t>(&table);
-    return *reinterpret_cast<PFN_vkVoidFunction*>(base +
-                                                  static_cast<size_t>(offset));
-}

-const NameOffset kInstanceDispatchOffsets[] = {«
-  // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "IsInstanceDispatched" $f)}}
-      {"{{$f.Name}}", offsetof(InstanceDispatchTable, {{Macro "BaseName" $f}})},
-    {{end}}
-  {{end}}
-  // clang-format on
-»};

-const NameOffset kDeviceDispatchOffsets[] = {«
-  // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "IsDeviceDispatched" $f)}}
-      {"{{$f.Name}}", offsetof(DeviceDispatchTable, {{Macro "BaseName" $f}})},
-    {{end}}
-  {{end}}
-  // clang-format on
-»};

-} // anonymous namespace

-namespace vulkan {

-PFN_vkVoidFunction GetLoaderExportProcAddr(const char* name) {
-    return Lookup(name, kLoaderExportProcs);
-}

-PFN_vkVoidFunction GetLoaderGlobalProcAddr(const char* name) {
-    return Lookup(name, kLoaderGlobalProcs);
-}

-PFN_vkVoidFunction GetLoaderTopProcAddr(const char* name) {
-    return Lookup(name, kLoaderTopProcs);
-}

-PFN_vkVoidFunction GetLoaderBottomProcAddr(const char* name) {
-    return Lookup(name, kLoaderBottomProcs);
-}

-PFN_vkVoidFunction GetDispatchProcAddr(const InstanceDispatchTable& dispatch,
-                                       const char* name) {
-    return Lookup(name, kInstanceDispatchOffsets, dispatch);
-}

-PFN_vkVoidFunction GetDispatchProcAddr(const DeviceDispatchTable& dispatch,
-                                       const char* name) {
-    return Lookup(name, kDeviceDispatchOffsets, dispatch);
-}

-bool LoadInstanceDispatchTable(VkInstance instance,
-                               PFN_vkGetInstanceProcAddr get_proc_addr,
-                               InstanceDispatchTable& dispatch) {«
-    bool success = true;
-    // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsInstanceDispatched" $f)}}
-    dispatch.{{Macro "BaseName" $f}} = §
-        reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
-            get_proc_addr(instance, "{{$f.Name}}"));
-    if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
-        ALOGE("missing instance proc: %s", "{{$f.Name}}");
-        success = false;
-    }
-    {{end}}
-  {{end}}
-    // clang-format on
-    return success;
-»}

-bool LoadDeviceDispatchTable(VkDevice device,
-                             PFN_vkGetDeviceProcAddr get_proc_addr,
-                             DeviceDispatchTable& dispatch) {«
-    bool success = true;
-    // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsDeviceDispatched" $f)}}
-    dispatch.{{Macro "BaseName" $f}} = §
-        reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
-            get_proc_addr(device, "{{$f.Name}}"));
-    if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
-        ALOGE("missing device proc: %s", "{{$f.Name}}");
-        success = false;
-    }
-    {{end}}
-  {{end}}
-    // clang-format on
-    return success;
-»}

-bool LoadDriverDispatchTable(VkInstance instance,
-                             PFN_vkGetInstanceProcAddr get_proc_addr,
-                             const InstanceExtensionSet& extensions,
-                             DriverDispatchTable& dispatch) {«
-    bool success = true;
-    // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsInstanceDispatched" $f)}}
-      {{if not (Macro "IsLoaderFunction" $f)}}
-        {{$ext := GetAnnotation $f "extension"}}
-          {{if $ext}}
-    if (extensions[{{Macro "ExtensionConstant" $ext}}]) {
-          {{end}}
-        dispatch.{{Macro "BaseName" $f}} = §
-            reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
-                get_proc_addr(instance, "{{$f.Name}}"));
-        if (UNLIKELY(!dispatch.{{Macro "BaseName" $f}})) {
-            ALOGE("missing driver proc: %s", "{{$f.Name}}");
-            success = false;
-        }
-        {{if $ext}}
-    }
-        {{end}}
-      {{end}}
-    {{end}}
-  {{end}}
-    dispatch.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(get_proc_addr(instance, "vkGetDeviceProcAddr"));
-    if (UNLIKELY(!dispatch.GetDeviceProcAddr)) {
-        ALOGE("missing driver proc: %s", "vkGetDeviceProcAddr");
-        success = false;
-    }
-    dispatch.CreateImage = reinterpret_cast<PFN_vkCreateImage>(get_proc_addr(instance, "vkCreateImage"));
-    if (UNLIKELY(!dispatch.CreateImage)) {
-        ALOGE("missing driver proc: %s", "vkCreateImage");
-        success = false;
-    }
-    dispatch.DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(get_proc_addr(instance, "vkDestroyImage"));
-    if (UNLIKELY(!dispatch.DestroyImage)) {
-        ALOGE("missing driver proc: %s", "vkDestroyImage");
-        success = false;
-    }
-    dispatch.GetSwapchainGrallocUsageANDROID = reinterpret_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(get_proc_addr(instance, "vkGetSwapchainGrallocUsageANDROID"));
-    if (UNLIKELY(!dispatch.GetSwapchainGrallocUsageANDROID)) {
-        ALOGE("missing driver proc: %s", "vkGetSwapchainGrallocUsageANDROID");
-        success = false;
-    }
-    dispatch.AcquireImageANDROID = reinterpret_cast<PFN_vkAcquireImageANDROID>(get_proc_addr(instance, "vkAcquireImageANDROID"));
-    if (UNLIKELY(!dispatch.AcquireImageANDROID)) {
-        ALOGE("missing driver proc: %s", "vkAcquireImageANDROID");
-        success = false;
-    }
-    dispatch.QueueSignalReleaseImageANDROID = reinterpret_cast<PFN_vkQueueSignalReleaseImageANDROID>(get_proc_addr(instance, "vkQueueSignalReleaseImageANDROID"));
-    if (UNLIKELY(!dispatch.QueueSignalReleaseImageANDROID)) {
-        ALOGE("missing driver proc: %s", "vkQueueSignalReleaseImageANDROID");
-        success = false;
-    }
-    // clang-format on
-    return success;
-»}

-} // namespace vulkan

-// clang-format off

-{{range $f := AllCommands $}}
-  {{if and (not (GetAnnotation $f "pfn")) (Macro "IsExported" $f)}}
-    __attribute__((visibility("default")))
-    VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) {
-      {{if not (IsVoid $f.Return.Type)}}return §{{end}}
-      {{Macro "Dispatch" $f}}({{Macro "Arguments" $f}});
-    }
-    ¶
-  {{end}}
-{{end}}

-// clang-format on
-¶{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emit the dispatch lookup for a function based on its first parameter.
--------------------------------------------------------------------------------
-*/}}
-{{define "Dispatch"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "HasLoaderTopImpl" $)}}
-    {{Macro "BaseName" $}}_Top§
-  {{else}}
-    {{$p0 := index $.CallParameters 0}}
-    GetDispatchTable({{$p0.Name}}).{{Macro "BaseName" $}}§
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Map an extension name to InstanceExtension or DeviceExtension enum value
--------------------------------------------------------------------------------
-*/}}
-{{define "ExtensionConstant"}}
-  {{$name := index $.Arguments 0}}
-  {{     if (eq $name "VK_KHR_surface")}}kKHR_surface
-  {{else if (eq $name "VK_KHR_android_surface")}}kKHR_android_surface
-  {{else if (eq $name "VK_EXT_debug_report")}}kEXT_debug_report
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a function name without the "vk" prefix.
--------------------------------------------------------------------------------
-*/}}
-{{define "BaseName"}}
-  {{AssertType $ "Function"}}
-  {{TrimPrefix "vk" $.Name}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a comma-separated list of C parameter names for the given command.
--------------------------------------------------------------------------------
-*/}}
-{{define "Arguments"}}
-  {{AssertType $ "Function"}}
-
-  {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" for supported functions that undergo table dispatch. Only global
-  functions and functions handled in the loader top without calling into
-  lower layers are not dispatched.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsInstanceDispatched"}}
-  {{AssertType $ "Function"}}
-  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}}
-    {{if and (ne $.Name "vkEnumerateDeviceLayerProperties") (ne $.Name "vkGetInstanceProcAddr")}}true{{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" for supported functions that can have device-specific dispatch.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsDeviceDispatched"}}
-  {{AssertType $ "Function"}}
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{if eq (Macro "Vtbl" $) "Device"}}
-      {{if ne $.Name "vkGetDeviceProcAddr"}}
-        true
-      {{end}}
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" if a function is core or from a supportable extension.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsFunctionSupported"}}
-  {{AssertType $ "Function"}}
-  {{if not (GetAnnotation $ "pfn")}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if not $ext}}true
-    {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Decides whether a function should be exported from the Android Vulkan
-  library. Functions in the core API and in loader extensions are exported.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsExported"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{Macro "IsLoaderExtension" $ext}}
-    {{else}}
-      true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Reports whether an extension function is implemented entirely by the loader,
-  and not implemented by drivers.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsLoaderFunction"}}
-  {{AssertType $ "Function"}}
-
-  {{$ext := GetAnnotation $ "extension"}}
-  {{if $ext}}
-    {{Macro "IsLoaderExtension" $ext}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emit "true" if the loader has a top-level implementation for the function
-  that should be called directly rather than dispatching to the first layer.
--------------------------------------------------------------------------------
-*/}}
-{{define "HasLoaderTopImpl"}}
-  {{AssertType $ "Function"}}
-
-  {{/* Global functions can't be dispatched */}}
-  {{     if and (not (GetAnnotation $ "pfn")) (eq (Macro "Vtbl" $) "Global")}}true
-
-  {{/* G*PA are implemented by reading the dispatch table, not by dispatching
-       through it. */}}
-  {{else if eq $.Name "vkGetInstanceProcAddr"}}true
-  {{else if eq $.Name "vkGetDeviceProcAddr"}}true
-
-  {{/* Loader top needs to initialize dispatch for device-level dispatchable
-       objects */}}
-  {{else if eq $.Name "vkGetDeviceQueue"}}true
-  {{else if eq $.Name "vkAllocateCommandBuffers"}}true
-  {{else if eq $.Name "vkCreateDevice"}}true
-  {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
-  {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
-
-  {{/* vkDestroy for dispatchable objects needs to handle VK_NULL_HANDLE;
-       trying to dispatch through that would crash. */}}
-  {{else if eq $.Name "vkDestroyInstance"}}true
-  {{else if eq $.Name "vkDestroyDevice"}}true
-
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emit "true" if the loader has a bottom-level implementation for the function
-  which terminates the dispatch chain.
--------------------------------------------------------------------------------
-*/}}
-{{define "HasLoaderBottomImpl"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{     if (eq (Macro "Vtbl" $) "Instance")}}true
-    {{else if (Macro "IsLoaderFunction" $)}}true
-    {{else if (eq $.Name "vkCreateInstance")}}true
-    {{else if (eq $.Name "vkGetDeviceProcAddr")}}true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" if an extension is unsupportable on Android.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsExtensionBlacklisted"}}
-  {{$ext := index $.Arguments 0}}
-  {{     if eq $ext "VK_KHR_display"}}true
-  {{else if eq $ext "VK_KHR_display_swapchain"}}true
-  {{else if eq $ext "VK_KHR_xlib_surface"}}true
-  {{else if eq $ext "VK_KHR_xcb_surface"}}true
-  {{else if eq $ext "VK_KHR_wayland_surface"}}true
-  {{else if eq $ext "VK_KHR_mir_surface"}}true
-  {{else if eq $ext "VK_KHR_win32_surface"}}true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Reports whether an extension is implemented entirely by the loader,
-  so drivers should not enumerate it.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsLoaderExtension"}}
-  {{$ext := index $.Arguments 0}}
-  {{     if eq $ext "VK_KHR_surface"}}true
-  {{else if eq $ext "VK_KHR_swapchain"}}true
-  {{else if eq $ext "VK_KHR_android_surface"}}true
-  {{end}}
-{{end}}
diff --git a/vulkan/libvulkan/dispatch_gen.cpp b/vulkan/libvulkan/dispatch_gen.cpp
deleted file mode 100644
index b41efb8..0000000
--- a/vulkan/libvulkan/dispatch_gen.cpp
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-
-// WARNING: This file is generated. See ../README.md for instructions.
-
-#include <log/log.h>
-#include <algorithm>
-#include "loader.h"
-
-#define UNLIKELY(expr) __builtin_expect((expr), 0)
-
-using namespace vulkan;
-
-namespace {
-
-struct NameProc {
-    const char* name;
-    PFN_vkVoidFunction proc;
-};
-
-PFN_vkVoidFunction Lookup(const char* name,
-                          const NameProc* begin,
-                          const NameProc* end) {
-    const auto& entry = std::lower_bound(
-        begin, end, name,
-        [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; });
-    if (entry == end || strcmp(entry->name, name) != 0)
-        return nullptr;
-    return entry->proc;
-}
-
-template <size_t N>
-PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) {
-    return Lookup(name, procs, procs + N);
-}
-
-const NameProc kLoaderExportProcs[] = {
-    // clang-format off
-    {"vkAcquireNextImageKHR", reinterpret_cast<PFN_vkVoidFunction>(vkAcquireNextImageKHR)},
-    {"vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(vkAllocateCommandBuffers)},
-    {"vkAllocateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkAllocateDescriptorSets)},
-    {"vkAllocateMemory", reinterpret_cast<PFN_vkVoidFunction>(vkAllocateMemory)},
-    {"vkBeginCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkBeginCommandBuffer)},
-    {"vkBindBufferMemory", reinterpret_cast<PFN_vkVoidFunction>(vkBindBufferMemory)},
-    {"vkBindImageMemory", reinterpret_cast<PFN_vkVoidFunction>(vkBindImageMemory)},
-    {"vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBeginQuery)},
-    {"vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBeginRenderPass)},
-    {"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindDescriptorSets)},
-    {"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindIndexBuffer)},
-    {"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindPipeline)},
-    {"vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBindVertexBuffers)},
-    {"vkCmdBlitImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdBlitImage)},
-    {"vkCmdClearAttachments", reinterpret_cast<PFN_vkVoidFunction>(vkCmdClearAttachments)},
-    {"vkCmdClearColorImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdClearColorImage)},
-    {"vkCmdClearDepthStencilImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdClearDepthStencilImage)},
-    {"vkCmdCopyBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyBuffer)},
-    {"vkCmdCopyBufferToImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyBufferToImage)},
-    {"vkCmdCopyImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyImage)},
-    {"vkCmdCopyImageToBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyImageToBuffer)},
-    {"vkCmdCopyQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(vkCmdCopyQueryPoolResults)},
-    {"vkCmdDispatch", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDispatch)},
-    {"vkCmdDispatchIndirect", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDispatchIndirect)},
-    {"vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDraw)},
-    {"vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDrawIndexed)},
-    {"vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDrawIndexedIndirect)},
-    {"vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(vkCmdDrawIndirect)},
-    {"vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(vkCmdEndQuery)},
-    {"vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkCmdEndRenderPass)},
-    {"vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(vkCmdExecuteCommands)},
-    {"vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdFillBuffer)},
-    {"vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(vkCmdNextSubpass)},
-    {"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(vkCmdPipelineBarrier)},
-    {"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(vkCmdPushConstants)},
-    {"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkCmdResetEvent)},
-    {"vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(vkCmdResetQueryPool)},
-    {"vkCmdResolveImage", reinterpret_cast<PFN_vkVoidFunction>(vkCmdResolveImage)},
-    {"vkCmdSetBlendConstants", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetBlendConstants)},
-    {"vkCmdSetDepthBias", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetDepthBias)},
-    {"vkCmdSetDepthBounds", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetDepthBounds)},
-    {"vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetEvent)},
-    {"vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetLineWidth)},
-    {"vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetScissor)},
-    {"vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetStencilCompareMask)},
-    {"vkCmdSetStencilReference", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetStencilReference)},
-    {"vkCmdSetStencilWriteMask", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetStencilWriteMask)},
-    {"vkCmdSetViewport", reinterpret_cast<PFN_vkVoidFunction>(vkCmdSetViewport)},
-    {"vkCmdUpdateBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCmdUpdateBuffer)},
-    {"vkCmdWaitEvents", reinterpret_cast<PFN_vkVoidFunction>(vkCmdWaitEvents)},
-    {"vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(vkCmdWriteTimestamp)},
-    {"vkCreateAndroidSurfaceKHR", reinterpret_cast<PFN_vkVoidFunction>(vkCreateAndroidSurfaceKHR)},
-    {"vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCreateBuffer)},
-    {"vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(vkCreateBufferView)},
-    {"vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(vkCreateCommandPool)},
-    {"vkCreateComputePipelines", reinterpret_cast<PFN_vkVoidFunction>(vkCreateComputePipelines)},
-    {"vkCreateDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(vkCreateDescriptorPool)},
-    {"vkCreateDescriptorSetLayout", reinterpret_cast<PFN_vkVoidFunction>(vkCreateDescriptorSetLayout)},
-    {"vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(vkCreateDevice)},
-    {"vkCreateEvent", reinterpret_cast<PFN_vkVoidFunction>(vkCreateEvent)},
-    {"vkCreateFence", reinterpret_cast<PFN_vkVoidFunction>(vkCreateFence)},
-    {"vkCreateFramebuffer", reinterpret_cast<PFN_vkVoidFunction>(vkCreateFramebuffer)},
-    {"vkCreateGraphicsPipelines", reinterpret_cast<PFN_vkVoidFunction>(vkCreateGraphicsPipelines)},
-    {"vkCreateImage", reinterpret_cast<PFN_vkVoidFunction>(vkCreateImage)},
-    {"vkCreateImageView", reinterpret_cast<PFN_vkVoidFunction>(vkCreateImageView)},
-    {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(vkCreateInstance)},
-    {"vkCreatePipelineCache", reinterpret_cast<PFN_vkVoidFunction>(vkCreatePipelineCache)},
-    {"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(vkCreatePipelineLayout)},
-    {"vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(vkCreateQueryPool)},
-    {"vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkCreateRenderPass)},
-    {"vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(vkCreateSampler)},
-    {"vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(vkCreateSemaphore)},
-    {"vkCreateShaderModule", reinterpret_cast<PFN_vkVoidFunction>(vkCreateShaderModule)},
-    {"vkCreateSwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(vkCreateSwapchainKHR)},
-    {"vkDestroyBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyBuffer)},
-    {"vkDestroyBufferView", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyBufferView)},
-    {"vkDestroyCommandPool", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyCommandPool)},
-    {"vkDestroyDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyDescriptorPool)},
-    {"vkDestroyDescriptorSetLayout", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyDescriptorSetLayout)},
-    {"vkDestroyDevice", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyDevice)},
-    {"vkDestroyEvent", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyEvent)},
-    {"vkDestroyFence", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyFence)},
-    {"vkDestroyFramebuffer", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyFramebuffer)},
-    {"vkDestroyImage", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyImage)},
-    {"vkDestroyImageView", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyImageView)},
-    {"vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyInstance)},
-    {"vkDestroyPipeline", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyPipeline)},
-    {"vkDestroyPipelineCache", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyPipelineCache)},
-    {"vkDestroyPipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyPipelineLayout)},
-    {"vkDestroyQueryPool", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyQueryPool)},
-    {"vkDestroyRenderPass", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyRenderPass)},
-    {"vkDestroySampler", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySampler)},
-    {"vkDestroySemaphore", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySemaphore)},
-    {"vkDestroyShaderModule", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyShaderModule)},
-    {"vkDestroySurfaceKHR", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySurfaceKHR)},
-    {"vkDestroySwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySwapchainKHR)},
-    {"vkDeviceWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(vkDeviceWaitIdle)},
-    {"vkEndCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkEndCommandBuffer)},
-    {"vkEnumerateDeviceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateDeviceExtensionProperties)},
-    {"vkEnumerateDeviceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateDeviceLayerProperties)},
-    {"vkEnumerateInstanceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateInstanceExtensionProperties)},
-    {"vkEnumerateInstanceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateInstanceLayerProperties)},
-    {"vkEnumeratePhysicalDevices", reinterpret_cast<PFN_vkVoidFunction>(vkEnumeratePhysicalDevices)},
-    {"vkFlushMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(vkFlushMappedMemoryRanges)},
-    {"vkFreeCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(vkFreeCommandBuffers)},
-    {"vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkFreeDescriptorSets)},
-    {"vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(vkFreeMemory)},
-    {"vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(vkGetBufferMemoryRequirements)},
-    {"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceMemoryCommitment)},
-    {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceProcAddr)},
-    {"vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceQueue)},
-    {"vkGetEventStatus", reinterpret_cast<PFN_vkVoidFunction>(vkGetEventStatus)},
-    {"vkGetFenceStatus", reinterpret_cast<PFN_vkVoidFunction>(vkGetFenceStatus)},
-    {"vkGetImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(vkGetImageMemoryRequirements)},
-    {"vkGetImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(vkGetImageSparseMemoryRequirements)},
-    {"vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(vkGetImageSubresourceLayout)},
-    {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(vkGetInstanceProcAddr)},
-    {"vkGetPhysicalDeviceFeatures", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceFeatures)},
-    {"vkGetPhysicalDeviceFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceFormatProperties)},
-    {"vkGetPhysicalDeviceImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceImageFormatProperties)},
-    {"vkGetPhysicalDeviceMemoryProperties", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceMemoryProperties)},
-    {"vkGetPhysicalDeviceProperties", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceProperties)},
-    {"vkGetPhysicalDeviceQueueFamilyProperties", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceQueueFamilyProperties)},
-    {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSparseImageFormatProperties)},
-    {"vkGetPhysicalDeviceSurfaceCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSurfaceCapabilitiesKHR)},
-    {"vkGetPhysicalDeviceSurfaceFormatsKHR", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSurfaceFormatsKHR)},
-    {"vkGetPhysicalDeviceSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSurfacePresentModesKHR)},
-    {"vkGetPhysicalDeviceSurfaceSupportKHR", reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSurfaceSupportKHR)},
-    {"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(vkGetPipelineCacheData)},
-    {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(vkGetQueryPoolResults)},
-    {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(vkGetRenderAreaGranularity)},
-    {"vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(vkGetSwapchainImagesKHR)},
-    {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(vkInvalidateMappedMemoryRanges)},
-    {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(vkMapMemory)},
-    {"vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(vkMergePipelineCaches)},
-    {"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(vkQueueBindSparse)},
-    {"vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(vkQueuePresentKHR)},
-    {"vkQueueSubmit", reinterpret_cast<PFN_vkVoidFunction>(vkQueueSubmit)},
-    {"vkQueueWaitIdle", reinterpret_cast<PFN_vkVoidFunction>(vkQueueWaitIdle)},
-    {"vkResetCommandBuffer", reinterpret_cast<PFN_vkVoidFunction>(vkResetCommandBuffer)},
-    {"vkResetCommandPool", reinterpret_cast<PFN_vkVoidFunction>(vkResetCommandPool)},
-    {"vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(vkResetDescriptorPool)},
-    {"vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkResetEvent)},
-    {"vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(vkResetFences)},
-    {"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(vkSetEvent)},
-    {"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(vkUnmapMemory)},
-    {"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(vkUpdateDescriptorSets)},
-    {"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(vkWaitForFences)},
-    // clang-format on
-};
-
-const NameProc kLoaderGlobalProcs[] = {
-    // clang-format off
-    {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateInstance>(CreateInstance_Top))},
-    {"vkEnumerateInstanceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateInstanceExtensionProperties>(EnumerateInstanceExtensionProperties_Top))},
-    {"vkEnumerateInstanceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateInstanceLayerProperties>(EnumerateInstanceLayerProperties_Top))},
-    // clang-format on
-};
-
-const NameProc kLoaderTopProcs[] = {
-    // clang-format off
-    {"vkAllocateCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAllocateCommandBuffers>(AllocateCommandBuffers_Top))},
-    {"vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateDevice>(CreateDevice_Top))},
-    {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateInstance>(CreateInstance_Top))},
-    {"vkDestroyDevice", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyDevice>(DestroyDevice_Top))},
-    {"vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyInstance>(DestroyInstance_Top))},
-    {"vkEnumerateDeviceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateDeviceExtensionProperties>(EnumerateDeviceExtensionProperties_Top))},
-    {"vkEnumerateDeviceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateDeviceLayerProperties>(EnumerateDeviceLayerProperties_Top))},
-    {"vkEnumerateInstanceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateInstanceExtensionProperties>(EnumerateInstanceExtensionProperties_Top))},
-    {"vkEnumerateInstanceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateInstanceLayerProperties>(EnumerateInstanceLayerProperties_Top))},
-    {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr_Top))},
-    {"vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue>(GetDeviceQueue_Top))},
-    {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr_Top))},
-    // clang-format on
-};
-
-const NameProc kLoaderBottomProcs[] = {
-    // clang-format off
-    {"vkAcquireNextImageKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkAcquireNextImageKHR>(AcquireNextImageKHR_Bottom))},
-    {"vkCreateAndroidSurfaceKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateAndroidSurfaceKHR>(CreateAndroidSurfaceKHR_Bottom))},
-    {"vkCreateDebugReportCallbackEXT", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateDebugReportCallbackEXT>(CreateDebugReportCallbackEXT_Bottom))},
-    {"vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateDevice>(CreateDevice_Bottom))},
-    {"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateInstance>(CreateInstance_Bottom))},
-    {"vkCreateSwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSwapchainKHR>(CreateSwapchainKHR_Bottom))},
-    {"vkDebugReportMessageEXT", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDebugReportMessageEXT>(DebugReportMessageEXT_Bottom))},
-    {"vkDestroyDebugReportCallbackEXT", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyDebugReportCallbackEXT>(DestroyDebugReportCallbackEXT_Bottom))},
-    {"vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroyInstance>(DestroyInstance_Bottom))},
-    {"vkDestroySurfaceKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroySurfaceKHR>(DestroySurfaceKHR_Bottom))},
-    {"vkDestroySwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkDestroySwapchainKHR>(DestroySwapchainKHR_Bottom))},
-    {"vkEnumerateDeviceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateDeviceExtensionProperties>(EnumerateDeviceExtensionProperties_Bottom))},
-    {"vkEnumerateDeviceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumerateDeviceLayerProperties>(EnumerateDeviceLayerProperties_Bottom))},
-    {"vkEnumeratePhysicalDevices", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkEnumeratePhysicalDevices>(EnumeratePhysicalDevices_Bottom))},
-    {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr_Bottom))},
-    {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr_Bottom))},
-    {"vkGetPhysicalDeviceFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFeatures>(GetPhysicalDeviceFeatures_Bottom))},
-    {"vkGetPhysicalDeviceFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceFormatProperties>(GetPhysicalDeviceFormatProperties_Bottom))},
-    {"vkGetPhysicalDeviceImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(GetPhysicalDeviceImageFormatProperties_Bottom))},
-    {"vkGetPhysicalDeviceMemoryProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(GetPhysicalDeviceMemoryProperties_Bottom))},
-    {"vkGetPhysicalDeviceProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceProperties>(GetPhysicalDeviceProperties_Bottom))},
-    {"vkGetPhysicalDeviceQueueFamilyProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(GetPhysicalDeviceQueueFamilyProperties_Bottom))},
-    {"vkGetPhysicalDeviceSparseImageFormatProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(GetPhysicalDeviceSparseImageFormatProperties_Bottom))},
-    {"vkGetPhysicalDeviceSurfaceCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(GetPhysicalDeviceSurfaceCapabilitiesKHR_Bottom))},
-    {"vkGetPhysicalDeviceSurfaceFormatsKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(GetPhysicalDeviceSurfaceFormatsKHR_Bottom))},
-    {"vkGetPhysicalDeviceSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(GetPhysicalDeviceSurfacePresentModesKHR_Bottom))},
-    {"vkGetPhysicalDeviceSurfaceSupportKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(GetPhysicalDeviceSurfaceSupportKHR_Bottom))},
-    {"vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainImagesKHR>(GetSwapchainImagesKHR_Bottom))},
-    {"vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueuePresentKHR>(QueuePresentKHR_Bottom))},
-    // clang-format on
-};
-
-struct NameOffset {
-    const char* name;
-    size_t offset;
-};
-
-ssize_t Lookup(const char* name,
-               const NameOffset* begin,
-               const NameOffset* end) {
-    const auto& entry = std::lower_bound(
-        begin, end, name, [](const NameOffset& e, const char* n) {
-            return strcmp(e.name, n) < 0;
-        });
-    if (entry == end || strcmp(entry->name, name) != 0)
-        return -1;
-    return static_cast<ssize_t>(entry->offset);
-}
-
-template <size_t N, class Table>
-PFN_vkVoidFunction Lookup(const char* name,
-                          const NameOffset (&offsets)[N],
-                          const Table& table) {
-    ssize_t offset = Lookup(name, offsets, offsets + N);
-    if (offset < 0)
-        return nullptr;
-    uintptr_t base = reinterpret_cast<uintptr_t>(&table);
-    return *reinterpret_cast<PFN_vkVoidFunction*>(base +
-                                                  static_cast<size_t>(offset));
-}
-
-const NameOffset kInstanceDispatchOffsets[] = {
-    // clang-format off
-    {"vkCreateAndroidSurfaceKHR", offsetof(InstanceDispatchTable, CreateAndroidSurfaceKHR)},
-    {"vkCreateDebugReportCallbackEXT", offsetof(InstanceDispatchTable, CreateDebugReportCallbackEXT)},
-    {"vkCreateDevice", offsetof(InstanceDispatchTable, CreateDevice)},
-    {"vkDebugReportMessageEXT", offsetof(InstanceDispatchTable, DebugReportMessageEXT)},
-    {"vkDestroyDebugReportCallbackEXT", offsetof(InstanceDispatchTable, DestroyDebugReportCallbackEXT)},
-    {"vkDestroyInstance", offsetof(InstanceDispatchTable, DestroyInstance)},
-    {"vkDestroySurfaceKHR", offsetof(InstanceDispatchTable, DestroySurfaceKHR)},
-    {"vkEnumerateDeviceExtensionProperties", offsetof(InstanceDispatchTable, EnumerateDeviceExtensionProperties)},
-    {"vkEnumeratePhysicalDevices", offsetof(InstanceDispatchTable, EnumeratePhysicalDevices)},
-    {"vkGetPhysicalDeviceFeatures", offsetof(InstanceDispatchTable, GetPhysicalDeviceFeatures)},
-    {"vkGetPhysicalDeviceFormatProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceFormatProperties)},
-    {"vkGetPhysicalDeviceImageFormatProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceImageFormatProperties)},
-    {"vkGetPhysicalDeviceMemoryProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceMemoryProperties)},
-    {"vkGetPhysicalDeviceProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceProperties)},
-    {"vkGetPhysicalDeviceQueueFamilyProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceQueueFamilyProperties)},
-    {"vkGetPhysicalDeviceSparseImageFormatProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceSparseImageFormatProperties)},
-    {"vkGetPhysicalDeviceSurfaceCapabilitiesKHR", offsetof(InstanceDispatchTable, GetPhysicalDeviceSurfaceCapabilitiesKHR)},
-    {"vkGetPhysicalDeviceSurfaceFormatsKHR", offsetof(InstanceDispatchTable, GetPhysicalDeviceSurfaceFormatsKHR)},
-    {"vkGetPhysicalDeviceSurfacePresentModesKHR", offsetof(InstanceDispatchTable, GetPhysicalDeviceSurfacePresentModesKHR)},
-    {"vkGetPhysicalDeviceSurfaceSupportKHR", offsetof(InstanceDispatchTable, GetPhysicalDeviceSurfaceSupportKHR)},
-    // clang-format on
-};
-
-const NameOffset kDeviceDispatchOffsets[] = {
-    // clang-format off
-    {"vkAcquireNextImageKHR", offsetof(DeviceDispatchTable, AcquireNextImageKHR)},
-    {"vkAllocateCommandBuffers", offsetof(DeviceDispatchTable, AllocateCommandBuffers)},
-    {"vkAllocateDescriptorSets", offsetof(DeviceDispatchTable, AllocateDescriptorSets)},
-    {"vkAllocateMemory", offsetof(DeviceDispatchTable, AllocateMemory)},
-    {"vkBeginCommandBuffer", offsetof(DeviceDispatchTable, BeginCommandBuffer)},
-    {"vkBindBufferMemory", offsetof(DeviceDispatchTable, BindBufferMemory)},
-    {"vkBindImageMemory", offsetof(DeviceDispatchTable, BindImageMemory)},
-    {"vkCmdBeginQuery", offsetof(DeviceDispatchTable, CmdBeginQuery)},
-    {"vkCmdBeginRenderPass", offsetof(DeviceDispatchTable, CmdBeginRenderPass)},
-    {"vkCmdBindDescriptorSets", offsetof(DeviceDispatchTable, CmdBindDescriptorSets)},
-    {"vkCmdBindIndexBuffer", offsetof(DeviceDispatchTable, CmdBindIndexBuffer)},
-    {"vkCmdBindPipeline", offsetof(DeviceDispatchTable, CmdBindPipeline)},
-    {"vkCmdBindVertexBuffers", offsetof(DeviceDispatchTable, CmdBindVertexBuffers)},
-    {"vkCmdBlitImage", offsetof(DeviceDispatchTable, CmdBlitImage)},
-    {"vkCmdClearAttachments", offsetof(DeviceDispatchTable, CmdClearAttachments)},
-    {"vkCmdClearColorImage", offsetof(DeviceDispatchTable, CmdClearColorImage)},
-    {"vkCmdClearDepthStencilImage", offsetof(DeviceDispatchTable, CmdClearDepthStencilImage)},
-    {"vkCmdCopyBuffer", offsetof(DeviceDispatchTable, CmdCopyBuffer)},
-    {"vkCmdCopyBufferToImage", offsetof(DeviceDispatchTable, CmdCopyBufferToImage)},
-    {"vkCmdCopyImage", offsetof(DeviceDispatchTable, CmdCopyImage)},
-    {"vkCmdCopyImageToBuffer", offsetof(DeviceDispatchTable, CmdCopyImageToBuffer)},
-    {"vkCmdCopyQueryPoolResults", offsetof(DeviceDispatchTable, CmdCopyQueryPoolResults)},
-    {"vkCmdDispatch", offsetof(DeviceDispatchTable, CmdDispatch)},
-    {"vkCmdDispatchIndirect", offsetof(DeviceDispatchTable, CmdDispatchIndirect)},
-    {"vkCmdDraw", offsetof(DeviceDispatchTable, CmdDraw)},
-    {"vkCmdDrawIndexed", offsetof(DeviceDispatchTable, CmdDrawIndexed)},
-    {"vkCmdDrawIndexedIndirect", offsetof(DeviceDispatchTable, CmdDrawIndexedIndirect)},
-    {"vkCmdDrawIndirect", offsetof(DeviceDispatchTable, CmdDrawIndirect)},
-    {"vkCmdEndQuery", offsetof(DeviceDispatchTable, CmdEndQuery)},
-    {"vkCmdEndRenderPass", offsetof(DeviceDispatchTable, CmdEndRenderPass)},
-    {"vkCmdExecuteCommands", offsetof(DeviceDispatchTable, CmdExecuteCommands)},
-    {"vkCmdFillBuffer", offsetof(DeviceDispatchTable, CmdFillBuffer)},
-    {"vkCmdNextSubpass", offsetof(DeviceDispatchTable, CmdNextSubpass)},
-    {"vkCmdPipelineBarrier", offsetof(DeviceDispatchTable, CmdPipelineBarrier)},
-    {"vkCmdPushConstants", offsetof(DeviceDispatchTable, CmdPushConstants)},
-    {"vkCmdResetEvent", offsetof(DeviceDispatchTable, CmdResetEvent)},
-    {"vkCmdResetQueryPool", offsetof(DeviceDispatchTable, CmdResetQueryPool)},
-    {"vkCmdResolveImage", offsetof(DeviceDispatchTable, CmdResolveImage)},
-    {"vkCmdSetBlendConstants", offsetof(DeviceDispatchTable, CmdSetBlendConstants)},
-    {"vkCmdSetDepthBias", offsetof(DeviceDispatchTable, CmdSetDepthBias)},
-    {"vkCmdSetDepthBounds", offsetof(DeviceDispatchTable, CmdSetDepthBounds)},
-    {"vkCmdSetEvent", offsetof(DeviceDispatchTable, CmdSetEvent)},
-    {"vkCmdSetLineWidth", offsetof(DeviceDispatchTable, CmdSetLineWidth)},
-    {"vkCmdSetScissor", offsetof(DeviceDispatchTable, CmdSetScissor)},
-    {"vkCmdSetStencilCompareMask", offsetof(DeviceDispatchTable, CmdSetStencilCompareMask)},
-    {"vkCmdSetStencilReference", offsetof(DeviceDispatchTable, CmdSetStencilReference)},
-    {"vkCmdSetStencilWriteMask", offsetof(DeviceDispatchTable, CmdSetStencilWriteMask)},
-    {"vkCmdSetViewport", offsetof(DeviceDispatchTable, CmdSetViewport)},
-    {"vkCmdUpdateBuffer", offsetof(DeviceDispatchTable, CmdUpdateBuffer)},
-    {"vkCmdWaitEvents", offsetof(DeviceDispatchTable, CmdWaitEvents)},
-    {"vkCmdWriteTimestamp", offsetof(DeviceDispatchTable, CmdWriteTimestamp)},
-    {"vkCreateBuffer", offsetof(DeviceDispatchTable, CreateBuffer)},
-    {"vkCreateBufferView", offsetof(DeviceDispatchTable, CreateBufferView)},
-    {"vkCreateCommandPool", offsetof(DeviceDispatchTable, CreateCommandPool)},
-    {"vkCreateComputePipelines", offsetof(DeviceDispatchTable, CreateComputePipelines)},
-    {"vkCreateDescriptorPool", offsetof(DeviceDispatchTable, CreateDescriptorPool)},
-    {"vkCreateDescriptorSetLayout", offsetof(DeviceDispatchTable, CreateDescriptorSetLayout)},
-    {"vkCreateEvent", offsetof(DeviceDispatchTable, CreateEvent)},
-    {"vkCreateFence", offsetof(DeviceDispatchTable, CreateFence)},
-    {"vkCreateFramebuffer", offsetof(DeviceDispatchTable, CreateFramebuffer)},
-    {"vkCreateGraphicsPipelines", offsetof(DeviceDispatchTable, CreateGraphicsPipelines)},
-    {"vkCreateImage", offsetof(DeviceDispatchTable, CreateImage)},
-    {"vkCreateImageView", offsetof(DeviceDispatchTable, CreateImageView)},
-    {"vkCreatePipelineCache", offsetof(DeviceDispatchTable, CreatePipelineCache)},
-    {"vkCreatePipelineLayout", offsetof(DeviceDispatchTable, CreatePipelineLayout)},
-    {"vkCreateQueryPool", offsetof(DeviceDispatchTable, CreateQueryPool)},
-    {"vkCreateRenderPass", offsetof(DeviceDispatchTable, CreateRenderPass)},
-    {"vkCreateSampler", offsetof(DeviceDispatchTable, CreateSampler)},
-    {"vkCreateSemaphore", offsetof(DeviceDispatchTable, CreateSemaphore)},
-    {"vkCreateShaderModule", offsetof(DeviceDispatchTable, CreateShaderModule)},
-    {"vkCreateSwapchainKHR", offsetof(DeviceDispatchTable, CreateSwapchainKHR)},
-    {"vkDestroyBuffer", offsetof(DeviceDispatchTable, DestroyBuffer)},
-    {"vkDestroyBufferView", offsetof(DeviceDispatchTable, DestroyBufferView)},
-    {"vkDestroyCommandPool", offsetof(DeviceDispatchTable, DestroyCommandPool)},
-    {"vkDestroyDescriptorPool", offsetof(DeviceDispatchTable, DestroyDescriptorPool)},
-    {"vkDestroyDescriptorSetLayout", offsetof(DeviceDispatchTable, DestroyDescriptorSetLayout)},
-    {"vkDestroyDevice", offsetof(DeviceDispatchTable, DestroyDevice)},
-    {"vkDestroyEvent", offsetof(DeviceDispatchTable, DestroyEvent)},
-    {"vkDestroyFence", offsetof(DeviceDispatchTable, DestroyFence)},
-    {"vkDestroyFramebuffer", offsetof(DeviceDispatchTable, DestroyFramebuffer)},
-    {"vkDestroyImage", offsetof(DeviceDispatchTable, DestroyImage)},
-    {"vkDestroyImageView", offsetof(DeviceDispatchTable, DestroyImageView)},
-    {"vkDestroyPipeline", offsetof(DeviceDispatchTable, DestroyPipeline)},
-    {"vkDestroyPipelineCache", offsetof(DeviceDispatchTable, DestroyPipelineCache)},
-    {"vkDestroyPipelineLayout", offsetof(DeviceDispatchTable, DestroyPipelineLayout)},
-    {"vkDestroyQueryPool", offsetof(DeviceDispatchTable, DestroyQueryPool)},
-    {"vkDestroyRenderPass", offsetof(DeviceDispatchTable, DestroyRenderPass)},
-    {"vkDestroySampler", offsetof(DeviceDispatchTable, DestroySampler)},
-    {"vkDestroySemaphore", offsetof(DeviceDispatchTable, DestroySemaphore)},
-    {"vkDestroyShaderModule", offsetof(DeviceDispatchTable, DestroyShaderModule)},
-    {"vkDestroySwapchainKHR", offsetof(DeviceDispatchTable, DestroySwapchainKHR)},
-    {"vkDeviceWaitIdle", offsetof(DeviceDispatchTable, DeviceWaitIdle)},
-    {"vkEndCommandBuffer", offsetof(DeviceDispatchTable, EndCommandBuffer)},
-    {"vkFlushMappedMemoryRanges", offsetof(DeviceDispatchTable, FlushMappedMemoryRanges)},
-    {"vkFreeCommandBuffers", offsetof(DeviceDispatchTable, FreeCommandBuffers)},
-    {"vkFreeDescriptorSets", offsetof(DeviceDispatchTable, FreeDescriptorSets)},
-    {"vkFreeMemory", offsetof(DeviceDispatchTable, FreeMemory)},
-    {"vkGetBufferMemoryRequirements", offsetof(DeviceDispatchTable, GetBufferMemoryRequirements)},
-    {"vkGetDeviceMemoryCommitment", offsetof(DeviceDispatchTable, GetDeviceMemoryCommitment)},
-    {"vkGetDeviceQueue", offsetof(DeviceDispatchTable, GetDeviceQueue)},
-    {"vkGetEventStatus", offsetof(DeviceDispatchTable, GetEventStatus)},
-    {"vkGetFenceStatus", offsetof(DeviceDispatchTable, GetFenceStatus)},
-    {"vkGetImageMemoryRequirements", offsetof(DeviceDispatchTable, GetImageMemoryRequirements)},
-    {"vkGetImageSparseMemoryRequirements", offsetof(DeviceDispatchTable, GetImageSparseMemoryRequirements)},
-    {"vkGetImageSubresourceLayout", offsetof(DeviceDispatchTable, GetImageSubresourceLayout)},
-    {"vkGetPipelineCacheData", offsetof(DeviceDispatchTable, GetPipelineCacheData)},
-    {"vkGetQueryPoolResults", offsetof(DeviceDispatchTable, GetQueryPoolResults)},
-    {"vkGetRenderAreaGranularity", offsetof(DeviceDispatchTable, GetRenderAreaGranularity)},
-    {"vkGetSwapchainImagesKHR", offsetof(DeviceDispatchTable, GetSwapchainImagesKHR)},
-    {"vkInvalidateMappedMemoryRanges", offsetof(DeviceDispatchTable, InvalidateMappedMemoryRanges)},
-    {"vkMapMemory", offsetof(DeviceDispatchTable, MapMemory)},
-    {"vkMergePipelineCaches", offsetof(DeviceDispatchTable, MergePipelineCaches)},
-    {"vkQueueBindSparse", offsetof(DeviceDispatchTable, QueueBindSparse)},
-    {"vkQueuePresentKHR", offsetof(DeviceDispatchTable, QueuePresentKHR)},
-    {"vkQueueSubmit", offsetof(DeviceDispatchTable, QueueSubmit)},
-    {"vkQueueWaitIdle", offsetof(DeviceDispatchTable, QueueWaitIdle)},
-    {"vkResetCommandBuffer", offsetof(DeviceDispatchTable, ResetCommandBuffer)},
-    {"vkResetCommandPool", offsetof(DeviceDispatchTable, ResetCommandPool)},
-    {"vkResetDescriptorPool", offsetof(DeviceDispatchTable, ResetDescriptorPool)},
-    {"vkResetEvent", offsetof(DeviceDispatchTable, ResetEvent)},
-    {"vkResetFences", offsetof(DeviceDispatchTable, ResetFences)},
-    {"vkSetEvent", offsetof(DeviceDispatchTable, SetEvent)},
-    {"vkUnmapMemory", offsetof(DeviceDispatchTable, UnmapMemory)},
-    {"vkUpdateDescriptorSets", offsetof(DeviceDispatchTable, UpdateDescriptorSets)},
-    {"vkWaitForFences", offsetof(DeviceDispatchTable, WaitForFences)},
-    // clang-format on
-};
-
-}  // anonymous namespace
-
-namespace vulkan {
-
-PFN_vkVoidFunction GetLoaderExportProcAddr(const char* name) {
-    return Lookup(name, kLoaderExportProcs);
-}
-
-PFN_vkVoidFunction GetLoaderGlobalProcAddr(const char* name) {
-    return Lookup(name, kLoaderGlobalProcs);
-}
-
-PFN_vkVoidFunction GetLoaderTopProcAddr(const char* name) {
-    return Lookup(name, kLoaderTopProcs);
-}
-
-PFN_vkVoidFunction GetLoaderBottomProcAddr(const char* name) {
-    return Lookup(name, kLoaderBottomProcs);
-}
-
-PFN_vkVoidFunction GetDispatchProcAddr(const InstanceDispatchTable& dispatch,
-                                       const char* name) {
-    return Lookup(name, kInstanceDispatchOffsets, dispatch);
-}
-
-PFN_vkVoidFunction GetDispatchProcAddr(const DeviceDispatchTable& dispatch,
-                                       const char* name) {
-    return Lookup(name, kDeviceDispatchOffsets, dispatch);
-}
-
-bool LoadInstanceDispatchTable(VkInstance instance,
-                               PFN_vkGetInstanceProcAddr get_proc_addr,
-                               InstanceDispatchTable& dispatch) {
-    bool success = true;
-    // clang-format off
-    dispatch.DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(get_proc_addr(instance, "vkDestroyInstance"));
-    if (UNLIKELY(!dispatch.DestroyInstance)) {
-        ALOGE("missing instance proc: %s", "vkDestroyInstance");
-        success = false;
-    }
-    dispatch.EnumeratePhysicalDevices = reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(get_proc_addr(instance, "vkEnumeratePhysicalDevices"));
-    if (UNLIKELY(!dispatch.EnumeratePhysicalDevices)) {
-        ALOGE("missing instance proc: %s", "vkEnumeratePhysicalDevices");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceProperties)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceQueueFamilyProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceQueueFamilyProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceQueueFamilyProperties)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceQueueFamilyProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceMemoryProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceMemoryProperties)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceMemoryProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceFeatures = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures>(get_proc_addr(instance, "vkGetPhysicalDeviceFeatures"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceFeatures)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceFeatures");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceFormatProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceFormatProperties)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceFormatProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceImageFormatProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceImageFormatProperties)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceImageFormatProperties");
-        success = false;
-    }
-    dispatch.CreateDevice = reinterpret_cast<PFN_vkCreateDevice>(get_proc_addr(instance, "vkCreateDevice"));
-    if (UNLIKELY(!dispatch.CreateDevice)) {
-        ALOGE("missing instance proc: %s", "vkCreateDevice");
-        success = false;
-    }
-    dispatch.EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(get_proc_addr(instance, "vkEnumerateDeviceExtensionProperties"));
-    if (UNLIKELY(!dispatch.EnumerateDeviceExtensionProperties)) {
-        ALOGE("missing instance proc: %s", "vkEnumerateDeviceExtensionProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceSparseImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceSparseImageFormatProperties)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceSparseImageFormatProperties");
-        success = false;
-    }
-    dispatch.DestroySurfaceKHR = reinterpret_cast<PFN_vkDestroySurfaceKHR>(get_proc_addr(instance, "vkDestroySurfaceKHR"));
-    if (UNLIKELY(!dispatch.DestroySurfaceKHR)) {
-        ALOGE("missing instance proc: %s", "vkDestroySurfaceKHR");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(get_proc_addr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceSurfaceSupportKHR)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceSurfaceSupportKHR");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(get_proc_addr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(get_proc_addr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceSurfaceFormatsKHR)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceSurfaceFormatsKHR");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(get_proc_addr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceSurfacePresentModesKHR)) {
-        ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceSurfacePresentModesKHR");
-        success = false;
-    }
-    dispatch.CreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(get_proc_addr(instance, "vkCreateAndroidSurfaceKHR"));
-    if (UNLIKELY(!dispatch.CreateAndroidSurfaceKHR)) {
-        ALOGE("missing instance proc: %s", "vkCreateAndroidSurfaceKHR");
-        success = false;
-    }
-    dispatch.CreateDebugReportCallbackEXT = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(get_proc_addr(instance, "vkCreateDebugReportCallbackEXT"));
-    if (UNLIKELY(!dispatch.CreateDebugReportCallbackEXT)) {
-        ALOGE("missing instance proc: %s", "vkCreateDebugReportCallbackEXT");
-        success = false;
-    }
-    dispatch.DestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(get_proc_addr(instance, "vkDestroyDebugReportCallbackEXT"));
-    if (UNLIKELY(!dispatch.DestroyDebugReportCallbackEXT)) {
-        ALOGE("missing instance proc: %s", "vkDestroyDebugReportCallbackEXT");
-        success = false;
-    }
-    dispatch.DebugReportMessageEXT = reinterpret_cast<PFN_vkDebugReportMessageEXT>(get_proc_addr(instance, "vkDebugReportMessageEXT"));
-    if (UNLIKELY(!dispatch.DebugReportMessageEXT)) {
-        ALOGE("missing instance proc: %s", "vkDebugReportMessageEXT");
-        success = false;
-    }
-    // clang-format on
-    return success;
-}
-
-bool LoadDeviceDispatchTable(VkDevice device,
-                             PFN_vkGetDeviceProcAddr get_proc_addr,
-                             DeviceDispatchTable& dispatch) {
-    bool success = true;
-    // clang-format off
-    dispatch.DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(get_proc_addr(device, "vkDestroyDevice"));
-    if (UNLIKELY(!dispatch.DestroyDevice)) {
-        ALOGE("missing device proc: %s", "vkDestroyDevice");
-        success = false;
-    }
-    dispatch.GetDeviceQueue = reinterpret_cast<PFN_vkGetDeviceQueue>(get_proc_addr(device, "vkGetDeviceQueue"));
-    if (UNLIKELY(!dispatch.GetDeviceQueue)) {
-        ALOGE("missing device proc: %s", "vkGetDeviceQueue");
-        success = false;
-    }
-    dispatch.QueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(get_proc_addr(device, "vkQueueSubmit"));
-    if (UNLIKELY(!dispatch.QueueSubmit)) {
-        ALOGE("missing device proc: %s", "vkQueueSubmit");
-        success = false;
-    }
-    dispatch.QueueWaitIdle = reinterpret_cast<PFN_vkQueueWaitIdle>(get_proc_addr(device, "vkQueueWaitIdle"));
-    if (UNLIKELY(!dispatch.QueueWaitIdle)) {
-        ALOGE("missing device proc: %s", "vkQueueWaitIdle");
-        success = false;
-    }
-    dispatch.DeviceWaitIdle = reinterpret_cast<PFN_vkDeviceWaitIdle>(get_proc_addr(device, "vkDeviceWaitIdle"));
-    if (UNLIKELY(!dispatch.DeviceWaitIdle)) {
-        ALOGE("missing device proc: %s", "vkDeviceWaitIdle");
-        success = false;
-    }
-    dispatch.AllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(get_proc_addr(device, "vkAllocateMemory"));
-    if (UNLIKELY(!dispatch.AllocateMemory)) {
-        ALOGE("missing device proc: %s", "vkAllocateMemory");
-        success = false;
-    }
-    dispatch.FreeMemory = reinterpret_cast<PFN_vkFreeMemory>(get_proc_addr(device, "vkFreeMemory"));
-    if (UNLIKELY(!dispatch.FreeMemory)) {
-        ALOGE("missing device proc: %s", "vkFreeMemory");
-        success = false;
-    }
-    dispatch.MapMemory = reinterpret_cast<PFN_vkMapMemory>(get_proc_addr(device, "vkMapMemory"));
-    if (UNLIKELY(!dispatch.MapMemory)) {
-        ALOGE("missing device proc: %s", "vkMapMemory");
-        success = false;
-    }
-    dispatch.UnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(get_proc_addr(device, "vkUnmapMemory"));
-    if (UNLIKELY(!dispatch.UnmapMemory)) {
-        ALOGE("missing device proc: %s", "vkUnmapMemory");
-        success = false;
-    }
-    dispatch.FlushMappedMemoryRanges = reinterpret_cast<PFN_vkFlushMappedMemoryRanges>(get_proc_addr(device, "vkFlushMappedMemoryRanges"));
-    if (UNLIKELY(!dispatch.FlushMappedMemoryRanges)) {
-        ALOGE("missing device proc: %s", "vkFlushMappedMemoryRanges");
-        success = false;
-    }
-    dispatch.InvalidateMappedMemoryRanges = reinterpret_cast<PFN_vkInvalidateMappedMemoryRanges>(get_proc_addr(device, "vkInvalidateMappedMemoryRanges"));
-    if (UNLIKELY(!dispatch.InvalidateMappedMemoryRanges)) {
-        ALOGE("missing device proc: %s", "vkInvalidateMappedMemoryRanges");
-        success = false;
-    }
-    dispatch.GetDeviceMemoryCommitment = reinterpret_cast<PFN_vkGetDeviceMemoryCommitment>(get_proc_addr(device, "vkGetDeviceMemoryCommitment"));
-    if (UNLIKELY(!dispatch.GetDeviceMemoryCommitment)) {
-        ALOGE("missing device proc: %s", "vkGetDeviceMemoryCommitment");
-        success = false;
-    }
-    dispatch.GetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(get_proc_addr(device, "vkGetBufferMemoryRequirements"));
-    if (UNLIKELY(!dispatch.GetBufferMemoryRequirements)) {
-        ALOGE("missing device proc: %s", "vkGetBufferMemoryRequirements");
-        success = false;
-    }
-    dispatch.BindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(get_proc_addr(device, "vkBindBufferMemory"));
-    if (UNLIKELY(!dispatch.BindBufferMemory)) {
-        ALOGE("missing device proc: %s", "vkBindBufferMemory");
-        success = false;
-    }
-    dispatch.GetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(get_proc_addr(device, "vkGetImageMemoryRequirements"));
-    if (UNLIKELY(!dispatch.GetImageMemoryRequirements)) {
-        ALOGE("missing device proc: %s", "vkGetImageMemoryRequirements");
-        success = false;
-    }
-    dispatch.BindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(get_proc_addr(device, "vkBindImageMemory"));
-    if (UNLIKELY(!dispatch.BindImageMemory)) {
-        ALOGE("missing device proc: %s", "vkBindImageMemory");
-        success = false;
-    }
-    dispatch.GetImageSparseMemoryRequirements = reinterpret_cast<PFN_vkGetImageSparseMemoryRequirements>(get_proc_addr(device, "vkGetImageSparseMemoryRequirements"));
-    if (UNLIKELY(!dispatch.GetImageSparseMemoryRequirements)) {
-        ALOGE("missing device proc: %s", "vkGetImageSparseMemoryRequirements");
-        success = false;
-    }
-    dispatch.QueueBindSparse = reinterpret_cast<PFN_vkQueueBindSparse>(get_proc_addr(device, "vkQueueBindSparse"));
-    if (UNLIKELY(!dispatch.QueueBindSparse)) {
-        ALOGE("missing device proc: %s", "vkQueueBindSparse");
-        success = false;
-    }
-    dispatch.CreateFence = reinterpret_cast<PFN_vkCreateFence>(get_proc_addr(device, "vkCreateFence"));
-    if (UNLIKELY(!dispatch.CreateFence)) {
-        ALOGE("missing device proc: %s", "vkCreateFence");
-        success = false;
-    }
-    dispatch.DestroyFence = reinterpret_cast<PFN_vkDestroyFence>(get_proc_addr(device, "vkDestroyFence"));
-    if (UNLIKELY(!dispatch.DestroyFence)) {
-        ALOGE("missing device proc: %s", "vkDestroyFence");
-        success = false;
-    }
-    dispatch.ResetFences = reinterpret_cast<PFN_vkResetFences>(get_proc_addr(device, "vkResetFences"));
-    if (UNLIKELY(!dispatch.ResetFences)) {
-        ALOGE("missing device proc: %s", "vkResetFences");
-        success = false;
-    }
-    dispatch.GetFenceStatus = reinterpret_cast<PFN_vkGetFenceStatus>(get_proc_addr(device, "vkGetFenceStatus"));
-    if (UNLIKELY(!dispatch.GetFenceStatus)) {
-        ALOGE("missing device proc: %s", "vkGetFenceStatus");
-        success = false;
-    }
-    dispatch.WaitForFences = reinterpret_cast<PFN_vkWaitForFences>(get_proc_addr(device, "vkWaitForFences"));
-    if (UNLIKELY(!dispatch.WaitForFences)) {
-        ALOGE("missing device proc: %s", "vkWaitForFences");
-        success = false;
-    }
-    dispatch.CreateSemaphore = reinterpret_cast<PFN_vkCreateSemaphore>(get_proc_addr(device, "vkCreateSemaphore"));
-    if (UNLIKELY(!dispatch.CreateSemaphore)) {
-        ALOGE("missing device proc: %s", "vkCreateSemaphore");
-        success = false;
-    }
-    dispatch.DestroySemaphore = reinterpret_cast<PFN_vkDestroySemaphore>(get_proc_addr(device, "vkDestroySemaphore"));
-    if (UNLIKELY(!dispatch.DestroySemaphore)) {
-        ALOGE("missing device proc: %s", "vkDestroySemaphore");
-        success = false;
-    }
-    dispatch.CreateEvent = reinterpret_cast<PFN_vkCreateEvent>(get_proc_addr(device, "vkCreateEvent"));
-    if (UNLIKELY(!dispatch.CreateEvent)) {
-        ALOGE("missing device proc: %s", "vkCreateEvent");
-        success = false;
-    }
-    dispatch.DestroyEvent = reinterpret_cast<PFN_vkDestroyEvent>(get_proc_addr(device, "vkDestroyEvent"));
-    if (UNLIKELY(!dispatch.DestroyEvent)) {
-        ALOGE("missing device proc: %s", "vkDestroyEvent");
-        success = false;
-    }
-    dispatch.GetEventStatus = reinterpret_cast<PFN_vkGetEventStatus>(get_proc_addr(device, "vkGetEventStatus"));
-    if (UNLIKELY(!dispatch.GetEventStatus)) {
-        ALOGE("missing device proc: %s", "vkGetEventStatus");
-        success = false;
-    }
-    dispatch.SetEvent = reinterpret_cast<PFN_vkSetEvent>(get_proc_addr(device, "vkSetEvent"));
-    if (UNLIKELY(!dispatch.SetEvent)) {
-        ALOGE("missing device proc: %s", "vkSetEvent");
-        success = false;
-    }
-    dispatch.ResetEvent = reinterpret_cast<PFN_vkResetEvent>(get_proc_addr(device, "vkResetEvent"));
-    if (UNLIKELY(!dispatch.ResetEvent)) {
-        ALOGE("missing device proc: %s", "vkResetEvent");
-        success = false;
-    }
-    dispatch.CreateQueryPool = reinterpret_cast<PFN_vkCreateQueryPool>(get_proc_addr(device, "vkCreateQueryPool"));
-    if (UNLIKELY(!dispatch.CreateQueryPool)) {
-        ALOGE("missing device proc: %s", "vkCreateQueryPool");
-        success = false;
-    }
-    dispatch.DestroyQueryPool = reinterpret_cast<PFN_vkDestroyQueryPool>(get_proc_addr(device, "vkDestroyQueryPool"));
-    if (UNLIKELY(!dispatch.DestroyQueryPool)) {
-        ALOGE("missing device proc: %s", "vkDestroyQueryPool");
-        success = false;
-    }
-    dispatch.GetQueryPoolResults = reinterpret_cast<PFN_vkGetQueryPoolResults>(get_proc_addr(device, "vkGetQueryPoolResults"));
-    if (UNLIKELY(!dispatch.GetQueryPoolResults)) {
-        ALOGE("missing device proc: %s", "vkGetQueryPoolResults");
-        success = false;
-    }
-    dispatch.CreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(get_proc_addr(device, "vkCreateBuffer"));
-    if (UNLIKELY(!dispatch.CreateBuffer)) {
-        ALOGE("missing device proc: %s", "vkCreateBuffer");
-        success = false;
-    }
-    dispatch.DestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(get_proc_addr(device, "vkDestroyBuffer"));
-    if (UNLIKELY(!dispatch.DestroyBuffer)) {
-        ALOGE("missing device proc: %s", "vkDestroyBuffer");
-        success = false;
-    }
-    dispatch.CreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(get_proc_addr(device, "vkCreateBufferView"));
-    if (UNLIKELY(!dispatch.CreateBufferView)) {
-        ALOGE("missing device proc: %s", "vkCreateBufferView");
-        success = false;
-    }
-    dispatch.DestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(get_proc_addr(device, "vkDestroyBufferView"));
-    if (UNLIKELY(!dispatch.DestroyBufferView)) {
-        ALOGE("missing device proc: %s", "vkDestroyBufferView");
-        success = false;
-    }
-    dispatch.CreateImage = reinterpret_cast<PFN_vkCreateImage>(get_proc_addr(device, "vkCreateImage"));
-    if (UNLIKELY(!dispatch.CreateImage)) {
-        ALOGE("missing device proc: %s", "vkCreateImage");
-        success = false;
-    }
-    dispatch.DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(get_proc_addr(device, "vkDestroyImage"));
-    if (UNLIKELY(!dispatch.DestroyImage)) {
-        ALOGE("missing device proc: %s", "vkDestroyImage");
-        success = false;
-    }
-    dispatch.GetImageSubresourceLayout = reinterpret_cast<PFN_vkGetImageSubresourceLayout>(get_proc_addr(device, "vkGetImageSubresourceLayout"));
-    if (UNLIKELY(!dispatch.GetImageSubresourceLayout)) {
-        ALOGE("missing device proc: %s", "vkGetImageSubresourceLayout");
-        success = false;
-    }
-    dispatch.CreateImageView = reinterpret_cast<PFN_vkCreateImageView>(get_proc_addr(device, "vkCreateImageView"));
-    if (UNLIKELY(!dispatch.CreateImageView)) {
-        ALOGE("missing device proc: %s", "vkCreateImageView");
-        success = false;
-    }
-    dispatch.DestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(get_proc_addr(device, "vkDestroyImageView"));
-    if (UNLIKELY(!dispatch.DestroyImageView)) {
-        ALOGE("missing device proc: %s", "vkDestroyImageView");
-        success = false;
-    }
-    dispatch.CreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(get_proc_addr(device, "vkCreateShaderModule"));
-    if (UNLIKELY(!dispatch.CreateShaderModule)) {
-        ALOGE("missing device proc: %s", "vkCreateShaderModule");
-        success = false;
-    }
-    dispatch.DestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(get_proc_addr(device, "vkDestroyShaderModule"));
-    if (UNLIKELY(!dispatch.DestroyShaderModule)) {
-        ALOGE("missing device proc: %s", "vkDestroyShaderModule");
-        success = false;
-    }
-    dispatch.CreatePipelineCache = reinterpret_cast<PFN_vkCreatePipelineCache>(get_proc_addr(device, "vkCreatePipelineCache"));
-    if (UNLIKELY(!dispatch.CreatePipelineCache)) {
-        ALOGE("missing device proc: %s", "vkCreatePipelineCache");
-        success = false;
-    }
-    dispatch.DestroyPipelineCache = reinterpret_cast<PFN_vkDestroyPipelineCache>(get_proc_addr(device, "vkDestroyPipelineCache"));
-    if (UNLIKELY(!dispatch.DestroyPipelineCache)) {
-        ALOGE("missing device proc: %s", "vkDestroyPipelineCache");
-        success = false;
-    }
-    dispatch.GetPipelineCacheData = reinterpret_cast<PFN_vkGetPipelineCacheData>(get_proc_addr(device, "vkGetPipelineCacheData"));
-    if (UNLIKELY(!dispatch.GetPipelineCacheData)) {
-        ALOGE("missing device proc: %s", "vkGetPipelineCacheData");
-        success = false;
-    }
-    dispatch.MergePipelineCaches = reinterpret_cast<PFN_vkMergePipelineCaches>(get_proc_addr(device, "vkMergePipelineCaches"));
-    if (UNLIKELY(!dispatch.MergePipelineCaches)) {
-        ALOGE("missing device proc: %s", "vkMergePipelineCaches");
-        success = false;
-    }
-    dispatch.CreateGraphicsPipelines = reinterpret_cast<PFN_vkCreateGraphicsPipelines>(get_proc_addr(device, "vkCreateGraphicsPipelines"));
-    if (UNLIKELY(!dispatch.CreateGraphicsPipelines)) {
-        ALOGE("missing device proc: %s", "vkCreateGraphicsPipelines");
-        success = false;
-    }
-    dispatch.CreateComputePipelines = reinterpret_cast<PFN_vkCreateComputePipelines>(get_proc_addr(device, "vkCreateComputePipelines"));
-    if (UNLIKELY(!dispatch.CreateComputePipelines)) {
-        ALOGE("missing device proc: %s", "vkCreateComputePipelines");
-        success = false;
-    }
-    dispatch.DestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(get_proc_addr(device, "vkDestroyPipeline"));
-    if (UNLIKELY(!dispatch.DestroyPipeline)) {
-        ALOGE("missing device proc: %s", "vkDestroyPipeline");
-        success = false;
-    }
-    dispatch.CreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(get_proc_addr(device, "vkCreatePipelineLayout"));
-    if (UNLIKELY(!dispatch.CreatePipelineLayout)) {
-        ALOGE("missing device proc: %s", "vkCreatePipelineLayout");
-        success = false;
-    }
-    dispatch.DestroyPipelineLayout = reinterpret_cast<PFN_vkDestroyPipelineLayout>(get_proc_addr(device, "vkDestroyPipelineLayout"));
-    if (UNLIKELY(!dispatch.DestroyPipelineLayout)) {
-        ALOGE("missing device proc: %s", "vkDestroyPipelineLayout");
-        success = false;
-    }
-    dispatch.CreateSampler = reinterpret_cast<PFN_vkCreateSampler>(get_proc_addr(device, "vkCreateSampler"));
-    if (UNLIKELY(!dispatch.CreateSampler)) {
-        ALOGE("missing device proc: %s", "vkCreateSampler");
-        success = false;
-    }
-    dispatch.DestroySampler = reinterpret_cast<PFN_vkDestroySampler>(get_proc_addr(device, "vkDestroySampler"));
-    if (UNLIKELY(!dispatch.DestroySampler)) {
-        ALOGE("missing device proc: %s", "vkDestroySampler");
-        success = false;
-    }
-    dispatch.CreateDescriptorSetLayout = reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(get_proc_addr(device, "vkCreateDescriptorSetLayout"));
-    if (UNLIKELY(!dispatch.CreateDescriptorSetLayout)) {
-        ALOGE("missing device proc: %s", "vkCreateDescriptorSetLayout");
-        success = false;
-    }
-    dispatch.DestroyDescriptorSetLayout = reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(get_proc_addr(device, "vkDestroyDescriptorSetLayout"));
-    if (UNLIKELY(!dispatch.DestroyDescriptorSetLayout)) {
-        ALOGE("missing device proc: %s", "vkDestroyDescriptorSetLayout");
-        success = false;
-    }
-    dispatch.CreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(get_proc_addr(device, "vkCreateDescriptorPool"));
-    if (UNLIKELY(!dispatch.CreateDescriptorPool)) {
-        ALOGE("missing device proc: %s", "vkCreateDescriptorPool");
-        success = false;
-    }
-    dispatch.DestroyDescriptorPool = reinterpret_cast<PFN_vkDestroyDescriptorPool>(get_proc_addr(device, "vkDestroyDescriptorPool"));
-    if (UNLIKELY(!dispatch.DestroyDescriptorPool)) {
-        ALOGE("missing device proc: %s", "vkDestroyDescriptorPool");
-        success = false;
-    }
-    dispatch.ResetDescriptorPool = reinterpret_cast<PFN_vkResetDescriptorPool>(get_proc_addr(device, "vkResetDescriptorPool"));
-    if (UNLIKELY(!dispatch.ResetDescriptorPool)) {
-        ALOGE("missing device proc: %s", "vkResetDescriptorPool");
-        success = false;
-    }
-    dispatch.AllocateDescriptorSets = reinterpret_cast<PFN_vkAllocateDescriptorSets>(get_proc_addr(device, "vkAllocateDescriptorSets"));
-    if (UNLIKELY(!dispatch.AllocateDescriptorSets)) {
-        ALOGE("missing device proc: %s", "vkAllocateDescriptorSets");
-        success = false;
-    }
-    dispatch.FreeDescriptorSets = reinterpret_cast<PFN_vkFreeDescriptorSets>(get_proc_addr(device, "vkFreeDescriptorSets"));
-    if (UNLIKELY(!dispatch.FreeDescriptorSets)) {
-        ALOGE("missing device proc: %s", "vkFreeDescriptorSets");
-        success = false;
-    }
-    dispatch.UpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(get_proc_addr(device, "vkUpdateDescriptorSets"));
-    if (UNLIKELY(!dispatch.UpdateDescriptorSets)) {
-        ALOGE("missing device proc: %s", "vkUpdateDescriptorSets");
-        success = false;
-    }
-    dispatch.CreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(get_proc_addr(device, "vkCreateFramebuffer"));
-    if (UNLIKELY(!dispatch.CreateFramebuffer)) {
-        ALOGE("missing device proc: %s", "vkCreateFramebuffer");
-        success = false;
-    }
-    dispatch.DestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(get_proc_addr(device, "vkDestroyFramebuffer"));
-    if (UNLIKELY(!dispatch.DestroyFramebuffer)) {
-        ALOGE("missing device proc: %s", "vkDestroyFramebuffer");
-        success = false;
-    }
-    dispatch.CreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(get_proc_addr(device, "vkCreateRenderPass"));
-    if (UNLIKELY(!dispatch.CreateRenderPass)) {
-        ALOGE("missing device proc: %s", "vkCreateRenderPass");
-        success = false;
-    }
-    dispatch.DestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(get_proc_addr(device, "vkDestroyRenderPass"));
-    if (UNLIKELY(!dispatch.DestroyRenderPass)) {
-        ALOGE("missing device proc: %s", "vkDestroyRenderPass");
-        success = false;
-    }
-    dispatch.GetRenderAreaGranularity = reinterpret_cast<PFN_vkGetRenderAreaGranularity>(get_proc_addr(device, "vkGetRenderAreaGranularity"));
-    if (UNLIKELY(!dispatch.GetRenderAreaGranularity)) {
-        ALOGE("missing device proc: %s", "vkGetRenderAreaGranularity");
-        success = false;
-    }
-    dispatch.CreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(get_proc_addr(device, "vkCreateCommandPool"));
-    if (UNLIKELY(!dispatch.CreateCommandPool)) {
-        ALOGE("missing device proc: %s", "vkCreateCommandPool");
-        success = false;
-    }
-    dispatch.DestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(get_proc_addr(device, "vkDestroyCommandPool"));
-    if (UNLIKELY(!dispatch.DestroyCommandPool)) {
-        ALOGE("missing device proc: %s", "vkDestroyCommandPool");
-        success = false;
-    }
-    dispatch.ResetCommandPool = reinterpret_cast<PFN_vkResetCommandPool>(get_proc_addr(device, "vkResetCommandPool"));
-    if (UNLIKELY(!dispatch.ResetCommandPool)) {
-        ALOGE("missing device proc: %s", "vkResetCommandPool");
-        success = false;
-    }
-    dispatch.AllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(get_proc_addr(device, "vkAllocateCommandBuffers"));
-    if (UNLIKELY(!dispatch.AllocateCommandBuffers)) {
-        ALOGE("missing device proc: %s", "vkAllocateCommandBuffers");
-        success = false;
-    }
-    dispatch.FreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(get_proc_addr(device, "vkFreeCommandBuffers"));
-    if (UNLIKELY(!dispatch.FreeCommandBuffers)) {
-        ALOGE("missing device proc: %s", "vkFreeCommandBuffers");
-        success = false;
-    }
-    dispatch.BeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(get_proc_addr(device, "vkBeginCommandBuffer"));
-    if (UNLIKELY(!dispatch.BeginCommandBuffer)) {
-        ALOGE("missing device proc: %s", "vkBeginCommandBuffer");
-        success = false;
-    }
-    dispatch.EndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(get_proc_addr(device, "vkEndCommandBuffer"));
-    if (UNLIKELY(!dispatch.EndCommandBuffer)) {
-        ALOGE("missing device proc: %s", "vkEndCommandBuffer");
-        success = false;
-    }
-    dispatch.ResetCommandBuffer = reinterpret_cast<PFN_vkResetCommandBuffer>(get_proc_addr(device, "vkResetCommandBuffer"));
-    if (UNLIKELY(!dispatch.ResetCommandBuffer)) {
-        ALOGE("missing device proc: %s", "vkResetCommandBuffer");
-        success = false;
-    }
-    dispatch.CmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(get_proc_addr(device, "vkCmdBindPipeline"));
-    if (UNLIKELY(!dispatch.CmdBindPipeline)) {
-        ALOGE("missing device proc: %s", "vkCmdBindPipeline");
-        success = false;
-    }
-    dispatch.CmdSetViewport = reinterpret_cast<PFN_vkCmdSetViewport>(get_proc_addr(device, "vkCmdSetViewport"));
-    if (UNLIKELY(!dispatch.CmdSetViewport)) {
-        ALOGE("missing device proc: %s", "vkCmdSetViewport");
-        success = false;
-    }
-    dispatch.CmdSetScissor = reinterpret_cast<PFN_vkCmdSetScissor>(get_proc_addr(device, "vkCmdSetScissor"));
-    if (UNLIKELY(!dispatch.CmdSetScissor)) {
-        ALOGE("missing device proc: %s", "vkCmdSetScissor");
-        success = false;
-    }
-    dispatch.CmdSetLineWidth = reinterpret_cast<PFN_vkCmdSetLineWidth>(get_proc_addr(device, "vkCmdSetLineWidth"));
-    if (UNLIKELY(!dispatch.CmdSetLineWidth)) {
-        ALOGE("missing device proc: %s", "vkCmdSetLineWidth");
-        success = false;
-    }
-    dispatch.CmdSetDepthBias = reinterpret_cast<PFN_vkCmdSetDepthBias>(get_proc_addr(device, "vkCmdSetDepthBias"));
-    if (UNLIKELY(!dispatch.CmdSetDepthBias)) {
-        ALOGE("missing device proc: %s", "vkCmdSetDepthBias");
-        success = false;
-    }
-    dispatch.CmdSetBlendConstants = reinterpret_cast<PFN_vkCmdSetBlendConstants>(get_proc_addr(device, "vkCmdSetBlendConstants"));
-    if (UNLIKELY(!dispatch.CmdSetBlendConstants)) {
-        ALOGE("missing device proc: %s", "vkCmdSetBlendConstants");
-        success = false;
-    }
-    dispatch.CmdSetDepthBounds = reinterpret_cast<PFN_vkCmdSetDepthBounds>(get_proc_addr(device, "vkCmdSetDepthBounds"));
-    if (UNLIKELY(!dispatch.CmdSetDepthBounds)) {
-        ALOGE("missing device proc: %s", "vkCmdSetDepthBounds");
-        success = false;
-    }
-    dispatch.CmdSetStencilCompareMask = reinterpret_cast<PFN_vkCmdSetStencilCompareMask>(get_proc_addr(device, "vkCmdSetStencilCompareMask"));
-    if (UNLIKELY(!dispatch.CmdSetStencilCompareMask)) {
-        ALOGE("missing device proc: %s", "vkCmdSetStencilCompareMask");
-        success = false;
-    }
-    dispatch.CmdSetStencilWriteMask = reinterpret_cast<PFN_vkCmdSetStencilWriteMask>(get_proc_addr(device, "vkCmdSetStencilWriteMask"));
-    if (UNLIKELY(!dispatch.CmdSetStencilWriteMask)) {
-        ALOGE("missing device proc: %s", "vkCmdSetStencilWriteMask");
-        success = false;
-    }
-    dispatch.CmdSetStencilReference = reinterpret_cast<PFN_vkCmdSetStencilReference>(get_proc_addr(device, "vkCmdSetStencilReference"));
-    if (UNLIKELY(!dispatch.CmdSetStencilReference)) {
-        ALOGE("missing device proc: %s", "vkCmdSetStencilReference");
-        success = false;
-    }
-    dispatch.CmdBindDescriptorSets = reinterpret_cast<PFN_vkCmdBindDescriptorSets>(get_proc_addr(device, "vkCmdBindDescriptorSets"));
-    if (UNLIKELY(!dispatch.CmdBindDescriptorSets)) {
-        ALOGE("missing device proc: %s", "vkCmdBindDescriptorSets");
-        success = false;
-    }
-    dispatch.CmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(get_proc_addr(device, "vkCmdBindIndexBuffer"));
-    if (UNLIKELY(!dispatch.CmdBindIndexBuffer)) {
-        ALOGE("missing device proc: %s", "vkCmdBindIndexBuffer");
-        success = false;
-    }
-    dispatch.CmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(get_proc_addr(device, "vkCmdBindVertexBuffers"));
-    if (UNLIKELY(!dispatch.CmdBindVertexBuffers)) {
-        ALOGE("missing device proc: %s", "vkCmdBindVertexBuffers");
-        success = false;
-    }
-    dispatch.CmdDraw = reinterpret_cast<PFN_vkCmdDraw>(get_proc_addr(device, "vkCmdDraw"));
-    if (UNLIKELY(!dispatch.CmdDraw)) {
-        ALOGE("missing device proc: %s", "vkCmdDraw");
-        success = false;
-    }
-    dispatch.CmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(get_proc_addr(device, "vkCmdDrawIndexed"));
-    if (UNLIKELY(!dispatch.CmdDrawIndexed)) {
-        ALOGE("missing device proc: %s", "vkCmdDrawIndexed");
-        success = false;
-    }
-    dispatch.CmdDrawIndirect = reinterpret_cast<PFN_vkCmdDrawIndirect>(get_proc_addr(device, "vkCmdDrawIndirect"));
-    if (UNLIKELY(!dispatch.CmdDrawIndirect)) {
-        ALOGE("missing device proc: %s", "vkCmdDrawIndirect");
-        success = false;
-    }
-    dispatch.CmdDrawIndexedIndirect = reinterpret_cast<PFN_vkCmdDrawIndexedIndirect>(get_proc_addr(device, "vkCmdDrawIndexedIndirect"));
-    if (UNLIKELY(!dispatch.CmdDrawIndexedIndirect)) {
-        ALOGE("missing device proc: %s", "vkCmdDrawIndexedIndirect");
-        success = false;
-    }
-    dispatch.CmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(get_proc_addr(device, "vkCmdDispatch"));
-    if (UNLIKELY(!dispatch.CmdDispatch)) {
-        ALOGE("missing device proc: %s", "vkCmdDispatch");
-        success = false;
-    }
-    dispatch.CmdDispatchIndirect = reinterpret_cast<PFN_vkCmdDispatchIndirect>(get_proc_addr(device, "vkCmdDispatchIndirect"));
-    if (UNLIKELY(!dispatch.CmdDispatchIndirect)) {
-        ALOGE("missing device proc: %s", "vkCmdDispatchIndirect");
-        success = false;
-    }
-    dispatch.CmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(get_proc_addr(device, "vkCmdCopyBuffer"));
-    if (UNLIKELY(!dispatch.CmdCopyBuffer)) {
-        ALOGE("missing device proc: %s", "vkCmdCopyBuffer");
-        success = false;
-    }
-    dispatch.CmdCopyImage = reinterpret_cast<PFN_vkCmdCopyImage>(get_proc_addr(device, "vkCmdCopyImage"));
-    if (UNLIKELY(!dispatch.CmdCopyImage)) {
-        ALOGE("missing device proc: %s", "vkCmdCopyImage");
-        success = false;
-    }
-    dispatch.CmdBlitImage = reinterpret_cast<PFN_vkCmdBlitImage>(get_proc_addr(device, "vkCmdBlitImage"));
-    if (UNLIKELY(!dispatch.CmdBlitImage)) {
-        ALOGE("missing device proc: %s", "vkCmdBlitImage");
-        success = false;
-    }
-    dispatch.CmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(get_proc_addr(device, "vkCmdCopyBufferToImage"));
-    if (UNLIKELY(!dispatch.CmdCopyBufferToImage)) {
-        ALOGE("missing device proc: %s", "vkCmdCopyBufferToImage");
-        success = false;
-    }
-    dispatch.CmdCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(get_proc_addr(device, "vkCmdCopyImageToBuffer"));
-    if (UNLIKELY(!dispatch.CmdCopyImageToBuffer)) {
-        ALOGE("missing device proc: %s", "vkCmdCopyImageToBuffer");
-        success = false;
-    }
-    dispatch.CmdUpdateBuffer = reinterpret_cast<PFN_vkCmdUpdateBuffer>(get_proc_addr(device, "vkCmdUpdateBuffer"));
-    if (UNLIKELY(!dispatch.CmdUpdateBuffer)) {
-        ALOGE("missing device proc: %s", "vkCmdUpdateBuffer");
-        success = false;
-    }
-    dispatch.CmdFillBuffer = reinterpret_cast<PFN_vkCmdFillBuffer>(get_proc_addr(device, "vkCmdFillBuffer"));
-    if (UNLIKELY(!dispatch.CmdFillBuffer)) {
-        ALOGE("missing device proc: %s", "vkCmdFillBuffer");
-        success = false;
-    }
-    dispatch.CmdClearColorImage = reinterpret_cast<PFN_vkCmdClearColorImage>(get_proc_addr(device, "vkCmdClearColorImage"));
-    if (UNLIKELY(!dispatch.CmdClearColorImage)) {
-        ALOGE("missing device proc: %s", "vkCmdClearColorImage");
-        success = false;
-    }
-    dispatch.CmdClearDepthStencilImage = reinterpret_cast<PFN_vkCmdClearDepthStencilImage>(get_proc_addr(device, "vkCmdClearDepthStencilImage"));
-    if (UNLIKELY(!dispatch.CmdClearDepthStencilImage)) {
-        ALOGE("missing device proc: %s", "vkCmdClearDepthStencilImage");
-        success = false;
-    }
-    dispatch.CmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(get_proc_addr(device, "vkCmdClearAttachments"));
-    if (UNLIKELY(!dispatch.CmdClearAttachments)) {
-        ALOGE("missing device proc: %s", "vkCmdClearAttachments");
-        success = false;
-    }
-    dispatch.CmdResolveImage = reinterpret_cast<PFN_vkCmdResolveImage>(get_proc_addr(device, "vkCmdResolveImage"));
-    if (UNLIKELY(!dispatch.CmdResolveImage)) {
-        ALOGE("missing device proc: %s", "vkCmdResolveImage");
-        success = false;
-    }
-    dispatch.CmdSetEvent = reinterpret_cast<PFN_vkCmdSetEvent>(get_proc_addr(device, "vkCmdSetEvent"));
-    if (UNLIKELY(!dispatch.CmdSetEvent)) {
-        ALOGE("missing device proc: %s", "vkCmdSetEvent");
-        success = false;
-    }
-    dispatch.CmdResetEvent = reinterpret_cast<PFN_vkCmdResetEvent>(get_proc_addr(device, "vkCmdResetEvent"));
-    if (UNLIKELY(!dispatch.CmdResetEvent)) {
-        ALOGE("missing device proc: %s", "vkCmdResetEvent");
-        success = false;
-    }
-    dispatch.CmdWaitEvents = reinterpret_cast<PFN_vkCmdWaitEvents>(get_proc_addr(device, "vkCmdWaitEvents"));
-    if (UNLIKELY(!dispatch.CmdWaitEvents)) {
-        ALOGE("missing device proc: %s", "vkCmdWaitEvents");
-        success = false;
-    }
-    dispatch.CmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(get_proc_addr(device, "vkCmdPipelineBarrier"));
-    if (UNLIKELY(!dispatch.CmdPipelineBarrier)) {
-        ALOGE("missing device proc: %s", "vkCmdPipelineBarrier");
-        success = false;
-    }
-    dispatch.CmdBeginQuery = reinterpret_cast<PFN_vkCmdBeginQuery>(get_proc_addr(device, "vkCmdBeginQuery"));
-    if (UNLIKELY(!dispatch.CmdBeginQuery)) {
-        ALOGE("missing device proc: %s", "vkCmdBeginQuery");
-        success = false;
-    }
-    dispatch.CmdEndQuery = reinterpret_cast<PFN_vkCmdEndQuery>(get_proc_addr(device, "vkCmdEndQuery"));
-    if (UNLIKELY(!dispatch.CmdEndQuery)) {
-        ALOGE("missing device proc: %s", "vkCmdEndQuery");
-        success = false;
-    }
-    dispatch.CmdResetQueryPool = reinterpret_cast<PFN_vkCmdResetQueryPool>(get_proc_addr(device, "vkCmdResetQueryPool"));
-    if (UNLIKELY(!dispatch.CmdResetQueryPool)) {
-        ALOGE("missing device proc: %s", "vkCmdResetQueryPool");
-        success = false;
-    }
-    dispatch.CmdWriteTimestamp = reinterpret_cast<PFN_vkCmdWriteTimestamp>(get_proc_addr(device, "vkCmdWriteTimestamp"));
-    if (UNLIKELY(!dispatch.CmdWriteTimestamp)) {
-        ALOGE("missing device proc: %s", "vkCmdWriteTimestamp");
-        success = false;
-    }
-    dispatch.CmdCopyQueryPoolResults = reinterpret_cast<PFN_vkCmdCopyQueryPoolResults>(get_proc_addr(device, "vkCmdCopyQueryPoolResults"));
-    if (UNLIKELY(!dispatch.CmdCopyQueryPoolResults)) {
-        ALOGE("missing device proc: %s", "vkCmdCopyQueryPoolResults");
-        success = false;
-    }
-    dispatch.CmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(get_proc_addr(device, "vkCmdPushConstants"));
-    if (UNLIKELY(!dispatch.CmdPushConstants)) {
-        ALOGE("missing device proc: %s", "vkCmdPushConstants");
-        success = false;
-    }
-    dispatch.CmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(get_proc_addr(device, "vkCmdBeginRenderPass"));
-    if (UNLIKELY(!dispatch.CmdBeginRenderPass)) {
-        ALOGE("missing device proc: %s", "vkCmdBeginRenderPass");
-        success = false;
-    }
-    dispatch.CmdNextSubpass = reinterpret_cast<PFN_vkCmdNextSubpass>(get_proc_addr(device, "vkCmdNextSubpass"));
-    if (UNLIKELY(!dispatch.CmdNextSubpass)) {
-        ALOGE("missing device proc: %s", "vkCmdNextSubpass");
-        success = false;
-    }
-    dispatch.CmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(get_proc_addr(device, "vkCmdEndRenderPass"));
-    if (UNLIKELY(!dispatch.CmdEndRenderPass)) {
-        ALOGE("missing device proc: %s", "vkCmdEndRenderPass");
-        success = false;
-    }
-    dispatch.CmdExecuteCommands = reinterpret_cast<PFN_vkCmdExecuteCommands>(get_proc_addr(device, "vkCmdExecuteCommands"));
-    if (UNLIKELY(!dispatch.CmdExecuteCommands)) {
-        ALOGE("missing device proc: %s", "vkCmdExecuteCommands");
-        success = false;
-    }
-    dispatch.CreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(get_proc_addr(device, "vkCreateSwapchainKHR"));
-    if (UNLIKELY(!dispatch.CreateSwapchainKHR)) {
-        ALOGE("missing device proc: %s", "vkCreateSwapchainKHR");
-        success = false;
-    }
-    dispatch.DestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(get_proc_addr(device, "vkDestroySwapchainKHR"));
-    if (UNLIKELY(!dispatch.DestroySwapchainKHR)) {
-        ALOGE("missing device proc: %s", "vkDestroySwapchainKHR");
-        success = false;
-    }
-    dispatch.GetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(get_proc_addr(device, "vkGetSwapchainImagesKHR"));
-    if (UNLIKELY(!dispatch.GetSwapchainImagesKHR)) {
-        ALOGE("missing device proc: %s", "vkGetSwapchainImagesKHR");
-        success = false;
-    }
-    dispatch.AcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(get_proc_addr(device, "vkAcquireNextImageKHR"));
-    if (UNLIKELY(!dispatch.AcquireNextImageKHR)) {
-        ALOGE("missing device proc: %s", "vkAcquireNextImageKHR");
-        success = false;
-    }
-    dispatch.QueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(get_proc_addr(device, "vkQueuePresentKHR"));
-    if (UNLIKELY(!dispatch.QueuePresentKHR)) {
-        ALOGE("missing device proc: %s", "vkQueuePresentKHR");
-        success = false;
-    }
-    // clang-format on
-    return success;
-}
-
-bool LoadDriverDispatchTable(VkInstance instance,
-                             PFN_vkGetInstanceProcAddr get_proc_addr,
-                             const InstanceExtensionSet& extensions,
-                             DriverDispatchTable& dispatch) {
-    bool success = true;
-    // clang-format off
-    dispatch.DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(get_proc_addr(instance, "vkDestroyInstance"));
-    if (UNLIKELY(!dispatch.DestroyInstance)) {
-        ALOGE("missing driver proc: %s", "vkDestroyInstance");
-        success = false;
-    }
-    dispatch.EnumeratePhysicalDevices = reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(get_proc_addr(instance, "vkEnumeratePhysicalDevices"));
-    if (UNLIKELY(!dispatch.EnumeratePhysicalDevices)) {
-        ALOGE("missing driver proc: %s", "vkEnumeratePhysicalDevices");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceProperties)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceQueueFamilyProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceQueueFamilyProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceQueueFamilyProperties)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceQueueFamilyProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceMemoryProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceMemoryProperties)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceMemoryProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceFeatures = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures>(get_proc_addr(instance, "vkGetPhysicalDeviceFeatures"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceFeatures)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceFeatures");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceFormatProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceFormatProperties)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceFormatProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceImageFormatProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceImageFormatProperties)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceImageFormatProperties");
-        success = false;
-    }
-    dispatch.CreateDevice = reinterpret_cast<PFN_vkCreateDevice>(get_proc_addr(instance, "vkCreateDevice"));
-    if (UNLIKELY(!dispatch.CreateDevice)) {
-        ALOGE("missing driver proc: %s", "vkCreateDevice");
-        success = false;
-    }
-    dispatch.EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(get_proc_addr(instance, "vkEnumerateDeviceExtensionProperties"));
-    if (UNLIKELY(!dispatch.EnumerateDeviceExtensionProperties)) {
-        ALOGE("missing driver proc: %s", "vkEnumerateDeviceExtensionProperties");
-        success = false;
-    }
-    dispatch.GetPhysicalDeviceSparseImageFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties>(get_proc_addr(instance, "vkGetPhysicalDeviceSparseImageFormatProperties"));
-    if (UNLIKELY(!dispatch.GetPhysicalDeviceSparseImageFormatProperties)) {
-        ALOGE("missing driver proc: %s", "vkGetPhysicalDeviceSparseImageFormatProperties");
-        success = false;
-    }
-    if (extensions[kEXT_debug_report]) {
-        dispatch.CreateDebugReportCallbackEXT = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(get_proc_addr(instance, "vkCreateDebugReportCallbackEXT"));
-        if (UNLIKELY(!dispatch.CreateDebugReportCallbackEXT)) {
-            ALOGE("missing driver proc: %s", "vkCreateDebugReportCallbackEXT");
-            success = false;
-        }
-    }
-    if (extensions[kEXT_debug_report]) {
-        dispatch.DestroyDebugReportCallbackEXT = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(get_proc_addr(instance, "vkDestroyDebugReportCallbackEXT"));
-        if (UNLIKELY(!dispatch.DestroyDebugReportCallbackEXT)) {
-            ALOGE("missing driver proc: %s", "vkDestroyDebugReportCallbackEXT");
-            success = false;
-        }
-    }
-    if (extensions[kEXT_debug_report]) {
-        dispatch.DebugReportMessageEXT = reinterpret_cast<PFN_vkDebugReportMessageEXT>(get_proc_addr(instance, "vkDebugReportMessageEXT"));
-        if (UNLIKELY(!dispatch.DebugReportMessageEXT)) {
-            ALOGE("missing driver proc: %s", "vkDebugReportMessageEXT");
-            success = false;
-        }
-    }
-    dispatch.GetDeviceProcAddr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(get_proc_addr(instance, "vkGetDeviceProcAddr"));
-    if (UNLIKELY(!dispatch.GetDeviceProcAddr)) {
-        ALOGE("missing driver proc: %s", "vkGetDeviceProcAddr");
-        success = false;
-    }
-    dispatch.CreateImage = reinterpret_cast<PFN_vkCreateImage>(get_proc_addr(instance, "vkCreateImage"));
-    if (UNLIKELY(!dispatch.CreateImage)) {
-        ALOGE("missing driver proc: %s", "vkCreateImage");
-        success = false;
-    }
-    dispatch.DestroyImage = reinterpret_cast<PFN_vkDestroyImage>(get_proc_addr(instance, "vkDestroyImage"));
-    if (UNLIKELY(!dispatch.DestroyImage)) {
-        ALOGE("missing driver proc: %s", "vkDestroyImage");
-        success = false;
-    }
-    dispatch.GetSwapchainGrallocUsageANDROID = reinterpret_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(get_proc_addr(instance, "vkGetSwapchainGrallocUsageANDROID"));
-    if (UNLIKELY(!dispatch.GetSwapchainGrallocUsageANDROID)) {
-        ALOGE("missing driver proc: %s", "vkGetSwapchainGrallocUsageANDROID");
-        success = false;
-    }
-    dispatch.AcquireImageANDROID = reinterpret_cast<PFN_vkAcquireImageANDROID>(get_proc_addr(instance, "vkAcquireImageANDROID"));
-    if (UNLIKELY(!dispatch.AcquireImageANDROID)) {
-        ALOGE("missing driver proc: %s", "vkAcquireImageANDROID");
-        success = false;
-    }
-    dispatch.QueueSignalReleaseImageANDROID = reinterpret_cast<PFN_vkQueueSignalReleaseImageANDROID>(get_proc_addr(instance, "vkQueueSignalReleaseImageANDROID"));
-    if (UNLIKELY(!dispatch.QueueSignalReleaseImageANDROID)) {
-        ALOGE("missing driver proc: %s", "vkQueueSignalReleaseImageANDROID");
-        success = false;
-    }
-    // clang-format on
-    return success;
-}
-
-}  // namespace vulkan
-
-// clang-format off
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) {
-    return CreateInstance_Top(pCreateInfo, pAllocator, pInstance);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) {
-    DestroyInstance_Top(instance, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
-    return GetDispatchTable(instance).EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR PFN_vkVoidFunction vkGetDeviceProcAddr(VkDevice device, const char* pName) {
-    return GetDeviceProcAddr_Top(device, pName);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance instance, const char* pName) {
-    return GetInstanceProcAddr_Top(instance, pName);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties) {
-    GetDispatchTable(physicalDevice).GetPhysicalDeviceProperties(physicalDevice, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties) {
-    GetDispatchTable(physicalDevice).GetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
-    GetDispatchTable(physicalDevice).GetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures) {
-    GetDispatchTable(physicalDevice).GetPhysicalDeviceFeatures(physicalDevice, pFeatures);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties) {
-    GetDispatchTable(physicalDevice).GetPhysicalDeviceFormatProperties(physicalDevice, format, pFormatProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties) {
-    return GetDispatchTable(physicalDevice).GetPhysicalDeviceImageFormatProperties(physicalDevice, format, type, tiling, usage, flags, pImageFormatProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) {
-    return CreateDevice_Top(physicalDevice, pCreateInfo, pAllocator, pDevice);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
-    DestroyDevice_Top(device, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
-    return EnumerateInstanceLayerProperties_Top(pPropertyCount, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
-    return EnumerateInstanceExtensionProperties_Top(pLayerName, pPropertyCount, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
-    return EnumerateDeviceLayerProperties_Top(physicalDevice, pPropertyCount, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) {
-    return EnumerateDeviceExtensionProperties_Top(physicalDevice, pLayerName, pPropertyCount, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue) {
-    GetDeviceQueue_Top(device, queueFamilyIndex, queueIndex, pQueue);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {
-    return GetDispatchTable(queue).QueueSubmit(queue, submitCount, pSubmits, fence);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkQueueWaitIdle(VkQueue queue) {
-    return GetDispatchTable(queue).QueueWaitIdle(queue);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkDeviceWaitIdle(VkDevice device) {
-    return GetDispatchTable(device).DeviceWaitIdle(device);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) {
-    return GetDispatchTable(device).AllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).FreeMemory(device, memory, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) {
-    return GetDispatchTable(device).MapMemory(device, memory, offset, size, flags, ppData);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkUnmapMemory(VkDevice device, VkDeviceMemory memory) {
-    GetDispatchTable(device).UnmapMemory(device, memory);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) {
-    return GetDispatchTable(device).FlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges) {
-    return GetDispatchTable(device).InvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetDeviceMemoryCommitment(VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes) {
-    GetDispatchTable(device).GetDeviceMemoryCommitment(device, memory, pCommittedMemoryInBytes);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements) {
-    GetDispatchTable(device).GetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
-    return GetDispatchTable(device).BindBufferMemory(device, buffer, memory, memoryOffset);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements) {
-    GetDispatchTable(device).GetImageMemoryRequirements(device, image, pMemoryRequirements);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) {
-    return GetDispatchTable(device).BindImageMemory(device, image, memory, memoryOffset);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetImageSparseMemoryRequirements(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements) {
-    GetDispatchTable(device).GetImageSparseMemoryRequirements(device, image, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceSparseImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties) {
-    GetDispatchTable(physicalDevice).GetPhysicalDeviceSparseImageFormatProperties(physicalDevice, format, type, samples, usage, tiling, pPropertyCount, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkQueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence) {
-    return GetDispatchTable(queue).QueueBindSparse(queue, bindInfoCount, pBindInfo, fence);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateFence(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence) {
-    return GetDispatchTable(device).CreateFence(device, pCreateInfo, pAllocator, pFence);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyFence(device, fence, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkResetFences(VkDevice device, uint32_t fenceCount, const VkFence* pFences) {
-    return GetDispatchTable(device).ResetFences(device, fenceCount, pFences);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetFenceStatus(VkDevice device, VkFence fence) {
-    return GetDispatchTable(device).GetFenceStatus(device, fence);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkWaitForFences(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout) {
-    return GetDispatchTable(device).WaitForFences(device, fenceCount, pFences, waitAll, timeout);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateSemaphore(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore) {
-    return GetDispatchTable(device).CreateSemaphore(device, pCreateInfo, pAllocator, pSemaphore);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroySemaphore(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroySemaphore(device, semaphore, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateEvent(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent) {
-    return GetDispatchTable(device).CreateEvent(device, pCreateInfo, pAllocator, pEvent);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyEvent(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyEvent(device, event, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetEventStatus(VkDevice device, VkEvent event) {
-    return GetDispatchTable(device).GetEventStatus(device, event);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkSetEvent(VkDevice device, VkEvent event) {
-    return GetDispatchTable(device).SetEvent(device, event);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkResetEvent(VkDevice device, VkEvent event) {
-    return GetDispatchTable(device).ResetEvent(device, event);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool) {
-    return GetDispatchTable(device).CreateQueryPool(device, pCreateInfo, pAllocator, pQueryPool);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyQueryPool(device, queryPool, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags) {
-    return GetDispatchTable(device).GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
-    return GetDispatchTable(device).CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyBuffer(device, buffer, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView) {
-    return GetDispatchTable(device).CreateBufferView(device, pCreateInfo, pAllocator, pView);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyBufferView(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyBufferView(device, bufferView, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage) {
-    return GetDispatchTable(device).CreateImage(device, pCreateInfo, pAllocator, pImage);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyImage(device, image, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetImageSubresourceLayout(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout) {
-    GetDispatchTable(device).GetImageSubresourceLayout(device, image, pSubresource, pLayout);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateImageView(VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView) {
-    return GetDispatchTable(device).CreateImageView(device, pCreateInfo, pAllocator, pView);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyImageView(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyImageView(device, imageView, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule) {
-    return GetDispatchTable(device).CreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyShaderModule(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyShaderModule(device, shaderModule, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreatePipelineCache(VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache) {
-    return GetDispatchTable(device).CreatePipelineCache(device, pCreateInfo, pAllocator, pPipelineCache);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyPipelineCache(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyPipelineCache(device, pipelineCache, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPipelineCacheData(VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData) {
-    return GetDispatchTable(device).GetPipelineCacheData(device, pipelineCache, pDataSize, pData);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkMergePipelineCaches(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches) {
-    return GetDispatchTable(device).MergePipelineCaches(device, dstCache, srcCacheCount, pSrcCaches);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) {
-    return GetDispatchTable(device).CreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) {
-    return GetDispatchTable(device).CreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyPipeline(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyPipeline(device, pipeline, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreatePipelineLayout(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout) {
-    return GetDispatchTable(device).CreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyPipelineLayout(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyPipelineLayout(device, pipelineLayout, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateSampler(VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler) {
-    return GetDispatchTable(device).CreateSampler(device, pCreateInfo, pAllocator, pSampler);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroySampler(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroySampler(device, sampler, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateDescriptorSetLayout(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout) {
-    return GetDispatchTable(device).CreateDescriptorSetLayout(device, pCreateInfo, pAllocator, pSetLayout);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyDescriptorSetLayout(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyDescriptorSetLayout(device, descriptorSetLayout, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateDescriptorPool(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool) {
-    return GetDispatchTable(device).CreateDescriptorPool(device, pCreateInfo, pAllocator, pDescriptorPool);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyDescriptorPool(device, descriptorPool, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags) {
-    return GetDispatchTable(device).ResetDescriptorPool(device, descriptorPool, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets) {
-    return GetDispatchTable(device).AllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets) {
-    return GetDispatchTable(device).FreeDescriptorSets(device, descriptorPool, descriptorSetCount, pDescriptorSets);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkUpdateDescriptorSets(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies) {
-    GetDispatchTable(device).UpdateDescriptorSets(device, descriptorWriteCount, pDescriptorWrites, descriptorCopyCount, pDescriptorCopies);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer) {
-    return GetDispatchTable(device).CreateFramebuffer(device, pCreateInfo, pAllocator, pFramebuffer);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyFramebuffer(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyFramebuffer(device, framebuffer, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
-    return GetDispatchTable(device).CreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyRenderPass(device, renderPass, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity) {
-    GetDispatchTable(device).GetRenderAreaGranularity(device, renderPass, pGranularity);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
-    return GetDispatchTable(device).CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroyCommandPool(device, commandPool, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags) {
-    return GetDispatchTable(device).ResetCommandPool(device, commandPool, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkAllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers) {
-    return AllocateCommandBuffers_Top(device, pAllocateInfo, pCommandBuffers);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) {
-    GetDispatchTable(device).FreeCommandBuffers(device, commandPool, commandBufferCount, pCommandBuffers);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo) {
-    return GetDispatchTable(commandBuffer).BeginCommandBuffer(commandBuffer, pBeginInfo);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEndCommandBuffer(VkCommandBuffer commandBuffer) {
-    return GetDispatchTable(commandBuffer).EndCommandBuffer(commandBuffer);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkResetCommandBuffer(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) {
-    return GetDispatchTable(commandBuffer).ResetCommandBuffer(commandBuffer, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBindPipeline(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) {
-    GetDispatchTable(commandBuffer).CmdBindPipeline(commandBuffer, pipelineBindPoint, pipeline);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetViewport(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports) {
-    GetDispatchTable(commandBuffer).CmdSetViewport(commandBuffer, firstViewport, viewportCount, pViewports);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetScissor(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors) {
-    GetDispatchTable(commandBuffer).CmdSetScissor(commandBuffer, firstScissor, scissorCount, pScissors);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetLineWidth(VkCommandBuffer commandBuffer, float lineWidth) {
-    GetDispatchTable(commandBuffer).CmdSetLineWidth(commandBuffer, lineWidth);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetDepthBias(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor) {
-    GetDispatchTable(commandBuffer).CmdSetDepthBias(commandBuffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetBlendConstants(VkCommandBuffer commandBuffer, const float blendConstants[4]) {
-    GetDispatchTable(commandBuffer).CmdSetBlendConstants(commandBuffer, blendConstants);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetDepthBounds(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds) {
-    GetDispatchTable(commandBuffer).CmdSetDepthBounds(commandBuffer, minDepthBounds, maxDepthBounds);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetStencilCompareMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask) {
-    GetDispatchTable(commandBuffer).CmdSetStencilCompareMask(commandBuffer, faceMask, compareMask);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetStencilWriteMask(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask) {
-    GetDispatchTable(commandBuffer).CmdSetStencilWriteMask(commandBuffer, faceMask, writeMask);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetStencilReference(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference) {
-    GetDispatchTable(commandBuffer).CmdSetStencilReference(commandBuffer, faceMask, reference);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets) {
-    GetDispatchTable(commandBuffer).CmdBindDescriptorSets(commandBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) {
-    GetDispatchTable(commandBuffer).CmdBindIndexBuffer(commandBuffer, buffer, offset, indexType);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) {
-    GetDispatchTable(commandBuffer).CmdBindVertexBuffers(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDraw(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) {
-    GetDispatchTable(commandBuffer).CmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) {
-    GetDispatchTable(commandBuffer).CmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) {
-    GetDispatchTable(commandBuffer).CmdDrawIndirect(commandBuffer, buffer, offset, drawCount, stride);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride) {
-    GetDispatchTable(commandBuffer).CmdDrawIndexedIndirect(commandBuffer, buffer, offset, drawCount, stride);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDispatch(VkCommandBuffer commandBuffer, uint32_t x, uint32_t y, uint32_t z) {
-    GetDispatchTable(commandBuffer).CmdDispatch(commandBuffer, x, y, z);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset) {
-    GetDispatchTable(commandBuffer).CmdDispatchIndirect(commandBuffer, buffer, offset);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions) {
-    GetDispatchTable(commandBuffer).CmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdCopyImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions) {
-    GetDispatchTable(commandBuffer).CmdCopyImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter) {
-    GetDispatchTable(commandBuffer).CmdBlitImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions, filter);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
-    GetDispatchTable(commandBuffer).CmdCopyBufferToImage(commandBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions) {
-    GetDispatchTable(commandBuffer).CmdCopyImageToBuffer(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const uint32_t* pData) {
-    GetDispatchTable(commandBuffer).CmdUpdateBuffer(commandBuffer, dstBuffer, dstOffset, dataSize, pData);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data) {
-    GetDispatchTable(commandBuffer).CmdFillBuffer(commandBuffer, dstBuffer, dstOffset, size, data);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges) {
-    GetDispatchTable(commandBuffer).CmdClearColorImage(commandBuffer, image, imageLayout, pColor, rangeCount, pRanges);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges) {
-    GetDispatchTable(commandBuffer).CmdClearDepthStencilImage(commandBuffer, image, imageLayout, pDepthStencil, rangeCount, pRanges);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects) {
-    GetDispatchTable(commandBuffer).CmdClearAttachments(commandBuffer, attachmentCount, pAttachments, rectCount, pRects);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions) {
-    GetDispatchTable(commandBuffer).CmdResolveImage(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) {
-    GetDispatchTable(commandBuffer).CmdSetEvent(commandBuffer, event, stageMask);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) {
-    GetDispatchTable(commandBuffer).CmdResetEvent(commandBuffer, event, stageMask);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) {
-    GetDispatchTable(commandBuffer).CmdWaitEvents(commandBuffer, eventCount, pEvents, srcStageMask, dstStageMask, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) {
-    GetDispatchTable(commandBuffer).CmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags) {
-    GetDispatchTable(commandBuffer).CmdBeginQuery(commandBuffer, queryPool, query, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query) {
-    GetDispatchTable(commandBuffer).CmdEndQuery(commandBuffer, queryPool, query);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdResetQueryPool(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
-    GetDispatchTable(commandBuffer).CmdResetQueryPool(commandBuffer, queryPool, firstQuery, queryCount);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query) {
-    GetDispatchTable(commandBuffer).CmdWriteTimestamp(commandBuffer, pipelineStage, queryPool, query);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags) {
-    GetDispatchTable(commandBuffer).CmdCopyQueryPoolResults(commandBuffer, queryPool, firstQuery, queryCount, dstBuffer, dstOffset, stride, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues) {
-    GetDispatchTable(commandBuffer).CmdPushConstants(commandBuffer, layout, stageFlags, offset, size, pValues);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents) {
-    GetDispatchTable(commandBuffer).CmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents) {
-    GetDispatchTable(commandBuffer).CmdNextSubpass(commandBuffer, contents);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdEndRenderPass(VkCommandBuffer commandBuffer) {
-    GetDispatchTable(commandBuffer).CmdEndRenderPass(commandBuffer);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) {
-    GetDispatchTable(commandBuffer).CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(instance).DestroySurfaceKHR(instance, surface, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) {
-    return GetDispatchTable(physicalDevice).GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) {
-    return GetDispatchTable(physicalDevice).GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) {
-    return GetDispatchTable(physicalDevice).GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) {
-    return GetDispatchTable(physicalDevice).GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
-    return GetDispatchTable(device).CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) {
-    GetDispatchTable(device).DestroySwapchainKHR(device, swapchain, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
-    return GetDispatchTable(device).GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {
-    return GetDispatchTable(device).AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) {
-    return GetDispatchTable(queue).QueuePresentKHR(queue, pPresentInfo);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
-    return GetDispatchTable(instance).CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
-}
-
-// clang-format on
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
new file mode 100644
index 0000000..b02f5b4
--- /dev/null
+++ b/vulkan/libvulkan/driver.cpp
@@ -0,0 +1,777 @@
+/*
+ * Copyright 2016 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 <stdlib.h>
+#include <string.h>
+#include <algorithm>
+#include <array>
+#include <new>
+#include <malloc.h>
+#include <sys/prctl.h>
+
+#include "driver.h"
+#include "stubhal.h"
+
+// #define ENABLE_ALLOC_CALLSTACKS 1
+#if ENABLE_ALLOC_CALLSTACKS
+#include <utils/CallStack.h>
+#define ALOGD_CALLSTACK(...)                             \
+    do {                                                 \
+        ALOGD(__VA_ARGS__);                              \
+        android::CallStack callstack;                    \
+        callstack.update();                              \
+        callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, "  "); \
+    } while (false)
+#else
+#define ALOGD_CALLSTACK(...) \
+    do {                     \
+    } while (false)
+#endif
+
+namespace vulkan {
+namespace driver {
+
+namespace {
+
+class CreateInfoWrapper {
+   public:
+    CreateInfoWrapper(const hwvulkan_device_t* hw_dev,
+                      const VkInstanceCreateInfo& create_info,
+                      const VkAllocationCallbacks& allocator);
+    CreateInfoWrapper(VkPhysicalDevice physical_dev,
+                      const VkDeviceCreateInfo& create_info,
+                      const VkAllocationCallbacks& allocator);
+    ~CreateInfoWrapper();
+
+    VkResult Validate();
+
+    const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const;
+    const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const;
+
+    explicit operator const VkInstanceCreateInfo*() const;
+    explicit operator const VkDeviceCreateInfo*() const;
+
+   private:
+    struct ExtensionFilter {
+        VkExtensionProperties* exts;
+        uint32_t ext_count;
+
+        const char** names;
+        uint32_t name_count;
+    };
+
+    VkResult SanitizePNext();
+
+    VkResult SanitizeLayers();
+    VkResult SanitizeExtensions();
+
+    VkResult QueryExtensionCount(uint32_t& count) const;
+    VkResult EnumerateExtensions(uint32_t& count,
+                                 VkExtensionProperties* props) const;
+    VkResult InitExtensionFilter();
+    void FilterExtension(const char* name);
+
+    const bool is_instance_;
+    const VkAllocationCallbacks& allocator_;
+
+    union {
+        const hwvulkan_device_t* hw_dev_;
+        VkPhysicalDevice physical_dev_;
+    };
+
+    union {
+        VkInstanceCreateInfo instance_info_;
+        VkDeviceCreateInfo dev_info_;
+    };
+
+    ExtensionFilter extension_filter_;
+
+    std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions_;
+    std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions_;
+};
+
+CreateInfoWrapper::CreateInfoWrapper(const hwvulkan_device_t* hw_dev,
+                                     const VkInstanceCreateInfo& create_info,
+                                     const VkAllocationCallbacks& allocator)
+    : is_instance_(true),
+      allocator_(allocator),
+      hw_dev_(hw_dev),
+      instance_info_(create_info),
+      extension_filter_() {
+    hook_extensions_.set(ProcHook::EXTENSION_CORE);
+    hal_extensions_.set(ProcHook::EXTENSION_CORE);
+}
+
+CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev,
+                                     const VkDeviceCreateInfo& create_info,
+                                     const VkAllocationCallbacks& allocator)
+    : is_instance_(false),
+      allocator_(allocator),
+      physical_dev_(physical_dev),
+      dev_info_(create_info),
+      extension_filter_() {
+    hook_extensions_.set(ProcHook::EXTENSION_CORE);
+    hal_extensions_.set(ProcHook::EXTENSION_CORE);
+}
+
+CreateInfoWrapper::~CreateInfoWrapper() {
+    allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts);
+    allocator_.pfnFree(allocator_.pUserData, extension_filter_.names);
+}
+
+VkResult CreateInfoWrapper::Validate() {
+    VkResult result = SanitizePNext();
+    if (result == VK_SUCCESS)
+        result = SanitizeLayers();
+    if (result == VK_SUCCESS)
+        result = SanitizeExtensions();
+
+    return result;
+}
+
+const std::bitset<ProcHook::EXTENSION_COUNT>&
+CreateInfoWrapper::GetHookExtensions() const {
+    return hook_extensions_;
+}
+
+const std::bitset<ProcHook::EXTENSION_COUNT>&
+CreateInfoWrapper::GetHalExtensions() const {
+    return hal_extensions_;
+}
+
+CreateInfoWrapper::operator const VkInstanceCreateInfo*() const {
+    return &instance_info_;
+}
+
+CreateInfoWrapper::operator const VkDeviceCreateInfo*() const {
+    return &dev_info_;
+}
+
+VkResult CreateInfoWrapper::SanitizePNext() {
+    const struct StructHeader {
+        VkStructureType type;
+        const void* next;
+    } * header;
+
+    if (is_instance_) {
+        header = reinterpret_cast<const StructHeader*>(instance_info_.pNext);
+
+        // skip leading VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFOs
+        while (header &&
+               header->type == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO)
+            header = reinterpret_cast<const StructHeader*>(header->next);
+
+        instance_info_.pNext = header;
+    } else {
+        header = reinterpret_cast<const StructHeader*>(dev_info_.pNext);
+
+        // skip leading VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFOs
+        while (header &&
+               header->type == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO)
+            header = reinterpret_cast<const StructHeader*>(header->next);
+
+        dev_info_.pNext = header;
+    }
+
+    return VK_SUCCESS;
+}
+
+VkResult CreateInfoWrapper::SanitizeLayers() {
+    auto& layer_names = (is_instance_) ? instance_info_.ppEnabledLayerNames
+                                       : dev_info_.ppEnabledLayerNames;
+    auto& layer_count = (is_instance_) ? instance_info_.enabledLayerCount
+                                       : dev_info_.enabledLayerCount;
+
+    // remove all layers
+    layer_names = nullptr;
+    layer_count = 0;
+
+    return VK_SUCCESS;
+}
+
+VkResult CreateInfoWrapper::SanitizeExtensions() {
+    auto& ext_names = (is_instance_) ? instance_info_.ppEnabledExtensionNames
+                                     : dev_info_.ppEnabledExtensionNames;
+    auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount
+                                     : dev_info_.enabledExtensionCount;
+    if (!ext_count)
+        return VK_SUCCESS;
+
+    VkResult result = InitExtensionFilter();
+    if (result != VK_SUCCESS)
+        return result;
+
+    for (uint32_t i = 0; i < ext_count; i++)
+        FilterExtension(ext_names[i]);
+
+    ext_names = extension_filter_.names;
+    ext_count = extension_filter_.name_count;
+
+    return VK_SUCCESS;
+}
+
+VkResult CreateInfoWrapper::QueryExtensionCount(uint32_t& count) const {
+    if (is_instance_) {
+        return hw_dev_->EnumerateInstanceExtensionProperties(nullptr, &count,
+                                                             nullptr);
+    } else {
+        const auto& driver = GetData(physical_dev_).driver;
+        return driver.EnumerateDeviceExtensionProperties(physical_dev_, nullptr,
+                                                         &count, nullptr);
+    }
+}
+
+VkResult CreateInfoWrapper::EnumerateExtensions(
+    uint32_t& count,
+    VkExtensionProperties* props) const {
+    if (is_instance_) {
+        return hw_dev_->EnumerateInstanceExtensionProperties(nullptr, &count,
+                                                             props);
+    } else {
+        const auto& driver = GetData(physical_dev_).driver;
+        return driver.EnumerateDeviceExtensionProperties(physical_dev_, nullptr,
+                                                         &count, props);
+    }
+}
+
+VkResult CreateInfoWrapper::InitExtensionFilter() {
+    // query extension count
+    uint32_t count;
+    VkResult result = QueryExtensionCount(count);
+    if (result != VK_SUCCESS || count == 0)
+        return result;
+
+    auto& filter = extension_filter_;
+    filter.exts =
+        reinterpret_cast<VkExtensionProperties*>(allocator_.pfnAllocation(
+            allocator_.pUserData, sizeof(VkExtensionProperties) * count,
+            alignof(VkExtensionProperties),
+            VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
+    if (!filter.exts)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+    // enumerate extensions
+    result = EnumerateExtensions(count, filter.exts);
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE)
+        return result;
+
+    if (!count)
+        return VK_SUCCESS;
+
+    filter.ext_count = count;
+
+    // allocate name array
+    uint32_t enabled_ext_count = (is_instance_)
+                                     ? instance_info_.enabledExtensionCount
+                                     : dev_info_.enabledExtensionCount;
+    count = std::min(filter.ext_count, enabled_ext_count);
+    filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation(
+        allocator_.pUserData, sizeof(const char*) * count, alignof(const char*),
+        VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
+    if (!filter.names)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+    return VK_SUCCESS;
+}
+
+void CreateInfoWrapper::FilterExtension(const char* name) {
+    auto& filter = extension_filter_;
+
+    ProcHook::Extension ext_bit = GetProcHookExtension(name);
+    if (is_instance_) {
+        switch (ext_bit) {
+            case ProcHook::KHR_android_surface:
+            case ProcHook::KHR_surface:
+                hook_extensions_.set(ext_bit);
+                // return now as these extensions do not require HAL support
+                return;
+            case ProcHook::EXT_debug_report:
+                // both we and HAL can take part in
+                hook_extensions_.set(ext_bit);
+                break;
+            case ProcHook::EXTENSION_UNKNOWN:
+                // HAL's extensions
+                break;
+            default:
+                ALOGW("Ignored invalid instance extension %s", name);
+                return;
+        }
+    } else {
+        switch (ext_bit) {
+            case ProcHook::KHR_swapchain:
+                // map VK_KHR_swapchain to VK_ANDROID_native_buffer
+                name = VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
+                ext_bit = ProcHook::ANDROID_native_buffer;
+                break;
+            case ProcHook::EXTENSION_UNKNOWN:
+                // HAL's extensions
+                break;
+            default:
+                ALOGW("Ignored invalid device extension %s", name);
+                return;
+        }
+    }
+
+    for (uint32_t i = 0; i < filter.ext_count; i++) {
+        const VkExtensionProperties& props = filter.exts[i];
+        // ignore unknown extensions
+        if (strcmp(name, props.extensionName) != 0)
+            continue;
+
+        filter.names[filter.name_count++] = name;
+        if (ext_bit != ProcHook::EXTENSION_UNKNOWN) {
+            if (ext_bit == ProcHook::ANDROID_native_buffer)
+                hook_extensions_.set(ProcHook::KHR_swapchain);
+
+            hal_extensions_.set(ext_bit);
+        }
+
+        break;
+    }
+}
+
+const hwvulkan_device_t* g_hwdevice = nullptr;
+
+VKAPI_ATTR void* DefaultAllocate(void*,
+                                 size_t size,
+                                 size_t alignment,
+                                 VkSystemAllocationScope) {
+    void* ptr = nullptr;
+    // Vulkan requires 'alignment' to be a power of two, but posix_memalign
+    // additionally requires that it be at least sizeof(void*).
+    int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
+    ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment,
+                    ret, ptr);
+    return ret == 0 ? ptr : nullptr;
+}
+
+VKAPI_ATTR void* DefaultReallocate(void*,
+                                   void* ptr,
+                                   size_t size,
+                                   size_t alignment,
+                                   VkSystemAllocationScope) {
+    if (size == 0) {
+        free(ptr);
+        return nullptr;
+    }
+
+    // TODO(jessehall): Right now we never shrink allocations; if the new
+    // request is smaller than the existing chunk, we just continue using it.
+    // Right now the loader never reallocs, so this doesn't matter. If that
+    // changes, or if this code is copied into some other project, this should
+    // probably have a heuristic to allocate-copy-free when doing so will save
+    // "enough" space.
+    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
+    if (size <= old_size)
+        return ptr;
+
+    void* new_ptr = nullptr;
+    if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0)
+        return nullptr;
+    if (ptr) {
+        memcpy(new_ptr, ptr, std::min(old_size, size));
+        free(ptr);
+    }
+    return new_ptr;
+}
+
+VKAPI_ATTR void DefaultFree(void*, void* ptr) {
+    ALOGD_CALLSTACK("Free: %p", ptr);
+    free(ptr);
+}
+
+InstanceData* AllocateInstanceData(const VkAllocationCallbacks& allocator) {
+    void* data_mem = allocator.pfnAllocation(
+        allocator.pUserData, sizeof(InstanceData), alignof(InstanceData),
+        VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+    if (!data_mem)
+        return nullptr;
+
+    return new (data_mem) InstanceData(allocator);
+}
+
+void FreeInstanceData(InstanceData* data,
+                      const VkAllocationCallbacks& allocator) {
+    data->~InstanceData();
+    allocator.pfnFree(allocator.pUserData, data);
+}
+
+DeviceData* AllocateDeviceData(const VkAllocationCallbacks& allocator) {
+    void* data_mem = allocator.pfnAllocation(
+        allocator.pUserData, sizeof(DeviceData), alignof(DeviceData),
+        VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
+    if (!data_mem)
+        return nullptr;
+
+    return new (data_mem) DeviceData(allocator);
+}
+
+void FreeDeviceData(DeviceData* data, const VkAllocationCallbacks& allocator) {
+    data->~DeviceData();
+    allocator.pfnFree(allocator.pUserData, data);
+}
+
+}  // anonymous namespace
+
+bool Debuggable() {
+    return (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) >= 0);
+}
+
+bool OpenHAL() {
+    ALOG_ASSERT(!g_hwdevice, "OpenHAL called more than once");
+
+    // Use a stub device unless we successfully open a real HAL device.
+    g_hwdevice = &stubhal::kDevice;
+
+    const hwvulkan_module_t* module;
+    int result =
+        hw_get_module("vulkan", reinterpret_cast<const hw_module_t**>(&module));
+    if (result != 0) {
+        ALOGV("no Vulkan HAL present, using stub HAL");
+        return true;
+    }
+
+    hwvulkan_device_t* device;
+    result =
+        module->common.methods->open(&module->common, HWVULKAN_DEVICE_0,
+                                     reinterpret_cast<hw_device_t**>(&device));
+    if (result != 0) {
+        // Any device with a Vulkan HAL should be able to open the device.
+        ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result),
+              result);
+        return false;
+    }
+
+    g_hwdevice = device;
+
+    return true;
+}
+
+const VkAllocationCallbacks& GetDefaultAllocator() {
+    static const VkAllocationCallbacks kDefaultAllocCallbacks = {
+        .pUserData = nullptr,
+        .pfnAllocation = DefaultAllocate,
+        .pfnReallocation = DefaultReallocate,
+        .pfnFree = DefaultFree,
+    };
+
+    return kDefaultAllocCallbacks;
+}
+
+PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) {
+    const ProcHook* hook = GetProcHook(pName);
+    if (!hook)
+        return g_hwdevice->GetInstanceProcAddr(instance, pName);
+
+    if (!instance) {
+        if (hook->type == ProcHook::GLOBAL)
+            return hook->proc;
+
+        // v0 layers expect
+        //
+        //   vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDevice");
+        //
+        // to work.
+        if (strcmp(pName, "vkCreateDevice") == 0)
+            return hook->proc;
+
+        ALOGE(
+            "Invalid use of vkGetInstanceProcAddr to query %s without an "
+            "instance",
+            pName);
+
+        return nullptr;
+    }
+
+    PFN_vkVoidFunction proc;
+
+    switch (hook->type) {
+        case ProcHook::INSTANCE:
+            proc = (GetData(instance).hook_extensions[hook->extension])
+                       ? hook->proc
+                       : nullptr;
+            break;
+        case ProcHook::DEVICE:
+            proc = (hook->extension == ProcHook::EXTENSION_CORE)
+                       ? hook->proc
+                       : hook->checked_proc;
+            break;
+        default:
+            ALOGE(
+                "Invalid use of vkGetInstanceProcAddr to query %s with an "
+                "instance",
+                pName);
+            proc = nullptr;
+            break;
+    }
+
+    return proc;
+}
+
+PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName) {
+    const ProcHook* hook = GetProcHook(pName);
+    if (!hook)
+        return GetData(device).driver.GetDeviceProcAddr(device, pName);
+
+    if (hook->type != ProcHook::DEVICE) {
+        ALOGE("Invalid use of vkGetDeviceProcAddr to query %s", pName);
+        return nullptr;
+    }
+
+    return (GetData(device).hook_extensions[hook->extension]) ? hook->proc
+                                                              : nullptr;
+}
+
+VkResult EnumerateInstanceExtensionProperties(
+    const char* pLayerName,
+    uint32_t* pPropertyCount,
+    VkExtensionProperties* pProperties) {
+    static const std::array<VkExtensionProperties, 2> loader_extensions = {{
+        // WSI extensions
+        {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION},
+        {VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+         VK_KHR_ANDROID_SURFACE_SPEC_VERSION},
+    }};
+
+    // enumerate our extensions first
+    if (!pLayerName && pProperties) {
+        uint32_t count = std::min(
+            *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
+
+        std::copy_n(loader_extensions.begin(), count, pProperties);
+
+        if (count < loader_extensions.size()) {
+            *pPropertyCount = count;
+            return VK_INCOMPLETE;
+        }
+
+        pProperties += count;
+        *pPropertyCount -= count;
+    }
+
+    VkResult result = g_hwdevice->EnumerateInstanceExtensionProperties(
+        pLayerName, pPropertyCount, pProperties);
+
+    if (!pLayerName && (result == VK_SUCCESS || result == VK_INCOMPLETE))
+        *pPropertyCount += loader_extensions.size();
+
+    return result;
+}
+
+VkResult EnumerateDeviceExtensionProperties(
+    VkPhysicalDevice physicalDevice,
+    const char* pLayerName,
+    uint32_t* pPropertyCount,
+    VkExtensionProperties* pProperties) {
+    const InstanceData& data = GetData(physicalDevice);
+
+    VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+        physicalDevice, pLayerName, pPropertyCount, pProperties);
+    if (result != VK_SUCCESS && result != VK_INCOMPLETE)
+        return result;
+
+    if (!pProperties)
+        return result;
+
+    // map VK_ANDROID_native_buffer to VK_KHR_swapchain
+    for (uint32_t i = 0; i < *pPropertyCount; i++) {
+        auto& prop = pProperties[i];
+
+        if (strcmp(prop.extensionName,
+                   VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+            continue;
+
+        memcpy(prop.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+               sizeof(VK_KHR_SWAPCHAIN_EXTENSION_NAME));
+        prop.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
+    }
+
+    return result;
+}
+
+VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
+                        const VkAllocationCallbacks* pAllocator,
+                        VkInstance* pInstance) {
+    const VkAllocationCallbacks& data_allocator =
+        (pAllocator) ? *pAllocator : GetDefaultAllocator();
+
+    CreateInfoWrapper wrapper(g_hwdevice, *pCreateInfo, data_allocator);
+    VkResult result = wrapper.Validate();
+    if (result != VK_SUCCESS)
+        return result;
+
+    InstanceData* data = AllocateInstanceData(data_allocator);
+    if (!data)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+    data->hook_extensions |= wrapper.GetHookExtensions();
+
+    // call into the driver
+    VkInstance instance;
+    result = g_hwdevice->CreateInstance(
+        static_cast<const VkInstanceCreateInfo*>(wrapper), pAllocator,
+        &instance);
+    if (result != VK_SUCCESS) {
+        FreeInstanceData(data, data_allocator);
+        return result;
+    }
+
+    // initialize InstanceDriverTable
+    if (!SetData(instance, *data) ||
+        !InitDriverTable(instance, g_hwdevice->GetInstanceProcAddr,
+                         wrapper.GetHalExtensions())) {
+        data->driver.DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
+            g_hwdevice->GetInstanceProcAddr(instance, "vkDestroyInstance"));
+        if (data->driver.DestroyInstance)
+            data->driver.DestroyInstance(instance, pAllocator);
+
+        FreeInstanceData(data, data_allocator);
+
+        return VK_ERROR_INCOMPATIBLE_DRIVER;
+    }
+
+    data->get_device_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
+        g_hwdevice->GetInstanceProcAddr(instance, "vkGetDeviceProcAddr"));
+    if (!data->get_device_proc_addr) {
+        data->driver.DestroyInstance(instance, pAllocator);
+        FreeInstanceData(data, data_allocator);
+
+        return VK_ERROR_INCOMPATIBLE_DRIVER;
+    }
+
+    *pInstance = instance;
+
+    return VK_SUCCESS;
+}
+
+void DestroyInstance(VkInstance instance,
+                     const VkAllocationCallbacks* pAllocator) {
+    InstanceData& data = GetData(instance);
+    data.driver.DestroyInstance(instance, pAllocator);
+
+    VkAllocationCallbacks local_allocator;
+    if (!pAllocator) {
+        local_allocator = data.allocator;
+        pAllocator = &local_allocator;
+    }
+
+    FreeInstanceData(&data, *pAllocator);
+}
+
+VkResult CreateDevice(VkPhysicalDevice physicalDevice,
+                      const VkDeviceCreateInfo* pCreateInfo,
+                      const VkAllocationCallbacks* pAllocator,
+                      VkDevice* pDevice) {
+    const InstanceData& instance_data = GetData(physicalDevice);
+    const VkAllocationCallbacks& data_allocator =
+        (pAllocator) ? *pAllocator : instance_data.allocator;
+
+    CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator);
+    VkResult result = wrapper.Validate();
+    if (result != VK_SUCCESS)
+        return result;
+
+    DeviceData* data = AllocateDeviceData(data_allocator);
+    if (!data)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+    data->hook_extensions |= wrapper.GetHookExtensions();
+
+    // call into the driver
+    VkDevice dev;
+    result = instance_data.driver.CreateDevice(
+        physicalDevice, static_cast<const VkDeviceCreateInfo*>(wrapper),
+        pAllocator, &dev);
+    if (result != VK_SUCCESS) {
+        FreeDeviceData(data, data_allocator);
+        return result;
+    }
+
+    // initialize DeviceDriverTable
+    if (!SetData(dev, *data) ||
+        !InitDriverTable(dev, instance_data.get_device_proc_addr,
+                         wrapper.GetHalExtensions())) {
+        data->driver.DestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(
+            instance_data.get_device_proc_addr(dev, "vkDestroyDevice"));
+        if (data->driver.DestroyDevice)
+            data->driver.DestroyDevice(dev, pAllocator);
+
+        FreeDeviceData(data, data_allocator);
+
+        return VK_ERROR_INCOMPATIBLE_DRIVER;
+    }
+
+    *pDevice = dev;
+
+    return VK_SUCCESS;
+}
+
+void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
+    DeviceData& data = GetData(device);
+    data.driver.DestroyDevice(device, pAllocator);
+
+    VkAllocationCallbacks local_allocator;
+    if (!pAllocator) {
+        local_allocator = data.allocator;
+        pAllocator = &local_allocator;
+    }
+
+    FreeDeviceData(&data, *pAllocator);
+}
+
+VkResult EnumeratePhysicalDevices(VkInstance instance,
+                                  uint32_t* pPhysicalDeviceCount,
+                                  VkPhysicalDevice* pPhysicalDevices) {
+    const auto& data = GetData(instance);
+
+    VkResult result = data.driver.EnumeratePhysicalDevices(
+        instance, pPhysicalDeviceCount, pPhysicalDevices);
+    if ((result == VK_SUCCESS || result == VK_INCOMPLETE) && pPhysicalDevices) {
+        for (uint32_t i = 0; i < *pPhysicalDeviceCount; i++)
+            SetData(pPhysicalDevices[i], data);
+    }
+
+    return result;
+}
+
+void GetDeviceQueue(VkDevice device,
+                    uint32_t queueFamilyIndex,
+                    uint32_t queueIndex,
+                    VkQueue* pQueue) {
+    const auto& data = GetData(device);
+
+    data.driver.GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
+    SetData(*pQueue, data);
+}
+
+VKAPI_ATTR VkResult
+AllocateCommandBuffers(VkDevice device,
+                       const VkCommandBufferAllocateInfo* pAllocateInfo,
+                       VkCommandBuffer* pCommandBuffers) {
+    const auto& data = GetData(device);
+
+    VkResult result = data.driver.AllocateCommandBuffers(device, pAllocateInfo,
+                                                         pCommandBuffers);
+    if (result == VK_SUCCESS) {
+        for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; i++)
+            SetData(pCommandBuffers[i], data);
+    }
+
+    return result;
+}
+
+}  // namespace driver
+}  // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
new file mode 100644
index 0000000..85b36b6
--- /dev/null
+++ b/vulkan/libvulkan/driver.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2016 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 LIBVULKAN_DRIVER_H
+#define LIBVULKAN_DRIVER_H 1
+
+#include <inttypes.h>
+#include <bitset>
+#include <type_traits>
+#include <log/log.h>
+
+#include <vulkan/vulkan.h>
+#include <hardware/hwvulkan.h>
+
+#include "api_gen.h"
+#include "driver_gen.h"
+#include "debug_report.h"
+#include "swapchain.h"
+
+namespace vulkan {
+
+// This is here so that we can embed api::{Instance,Device}Data in
+// driver::{Instance,Device}Data to avoid pointer chasing.  They are
+// considered opaque to the driver layer.
+namespace api {
+
+struct InstanceData {
+    InstanceDispatchTable dispatch;
+
+    // for VkPhysicalDevice->VkInstance mapping
+    VkInstance instance;
+
+    // LayerChain::ActiveLayer array
+    void* layers;
+    uint32_t layer_count;
+
+    // debug.vulkan.enable_callback
+    PFN_vkDestroyDebugReportCallbackEXT destroy_debug_callback;
+    VkDebugReportCallbackEXT debug_callback;
+};
+
+struct DeviceData {
+    DeviceDispatchTable dispatch;
+
+    // LayerChain::ActiveLayer array
+    void* layers;
+    uint32_t layer_count;
+};
+
+}  // namespace api
+
+namespace driver {
+
+VK_DEFINE_HANDLE(InstanceDispatchable)
+VK_DEFINE_HANDLE(DeviceDispatchable)
+
+struct InstanceData {
+    InstanceData(const VkAllocationCallbacks& alloc)
+        : opaque_api_data(),
+          allocator(alloc),
+          driver(),
+          get_device_proc_addr(nullptr) {
+        hook_extensions.set(ProcHook::EXTENSION_CORE);
+    }
+
+    api::InstanceData opaque_api_data;
+
+    const VkAllocationCallbacks allocator;
+
+    std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
+
+    InstanceDriverTable driver;
+    PFN_vkGetDeviceProcAddr get_device_proc_addr;
+
+    DebugReportCallbackList debug_report_callbacks;
+};
+
+struct DeviceData {
+    DeviceData(const VkAllocationCallbacks& alloc)
+        : opaque_api_data(), allocator(alloc), driver() {
+        hook_extensions.set(ProcHook::EXTENSION_CORE);
+    }
+
+    api::DeviceData opaque_api_data;
+
+    const VkAllocationCallbacks allocator;
+
+    std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
+
+    DeviceDriverTable driver;
+};
+
+bool Debuggable();
+bool OpenHAL();
+const VkAllocationCallbacks& GetDefaultAllocator();
+
+// clang-format off
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName);
+VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName);
+VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+
+VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
+
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
+VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
+VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
+
+VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);
+VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
+VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
+// clang-format on
+
+template <typename DispatchableType>
+void StaticAssertDispatchable(DispatchableType) {
+    static_assert(
+        std::is_same<DispatchableType, VkInstance>::value ||
+            std::is_same<DispatchableType, VkPhysicalDevice>::value ||
+            std::is_same<DispatchableType, VkDevice>::value ||
+            std::is_same<DispatchableType, InstanceDispatchable>::value ||
+            std::is_same<DispatchableType, VkQueue>::value ||
+            std::is_same<DispatchableType, VkCommandBuffer>::value ||
+            std::is_same<DispatchableType, DeviceDispatchable>::value,
+        "unrecognized dispatchable type");
+}
+
+template <typename DispatchableType>
+bool SetDataInternal(DispatchableType dispatchable, const void* data) {
+    StaticAssertDispatchable(dispatchable);
+
+    hwvulkan_dispatch_t* dispatch =
+        reinterpret_cast<hwvulkan_dispatch_t*>(dispatchable);
+    // must be magic or already set
+    if (dispatch->magic != HWVULKAN_DISPATCH_MAGIC && dispatch->vtbl != data) {
+        ALOGE("invalid dispatchable object magic 0x%" PRIxPTR, dispatch->magic);
+        return false;
+    }
+
+    dispatch->vtbl = data;
+
+    return true;
+}
+
+template <typename DispatchableType>
+void* GetDataInternal(DispatchableType dispatchable) {
+    StaticAssertDispatchable(dispatchable);
+
+    const hwvulkan_dispatch_t* dispatch =
+        reinterpret_cast<const hwvulkan_dispatch_t*>(dispatchable);
+
+    return const_cast<void*>(dispatch->vtbl);
+}
+
+inline bool SetData(VkInstance instance, const InstanceData& data) {
+    return SetDataInternal(instance, &data);
+}
+
+inline bool SetData(VkPhysicalDevice physical_dev, const InstanceData& data) {
+    return SetDataInternal(physical_dev, &data);
+}
+
+inline bool SetData(InstanceDispatchable dispatchable,
+                    const InstanceData& data) {
+    return SetDataInternal(dispatchable, &data);
+}
+
+inline bool SetData(VkDevice dev, const DeviceData& data) {
+    return SetDataInternal(dev, &data);
+}
+
+inline bool SetData(VkQueue queue, const DeviceData& data) {
+    return SetDataInternal(queue, &data);
+}
+
+inline bool SetData(VkCommandBuffer cmd, const DeviceData& data) {
+    return SetDataInternal(cmd, &data);
+}
+
+inline bool SetData(DeviceDispatchable dispatchable, const DeviceData& data) {
+    return SetDataInternal(dispatchable, &data);
+}
+
+inline InstanceData& GetData(VkInstance instance) {
+    return *reinterpret_cast<InstanceData*>(GetDataInternal(instance));
+}
+
+inline InstanceData& GetData(VkPhysicalDevice physical_dev) {
+    return *reinterpret_cast<InstanceData*>(GetDataInternal(physical_dev));
+}
+
+inline InstanceData& GetData(InstanceDispatchable dispatchable) {
+    return *reinterpret_cast<InstanceData*>(GetDataInternal(dispatchable));
+}
+
+inline DeviceData& GetData(VkDevice dev) {
+    return *reinterpret_cast<DeviceData*>(GetDataInternal(dev));
+}
+
+inline DeviceData& GetData(VkQueue queue) {
+    return *reinterpret_cast<DeviceData*>(GetDataInternal(queue));
+}
+
+inline DeviceData& GetData(VkCommandBuffer cmd) {
+    return *reinterpret_cast<DeviceData*>(GetDataInternal(cmd));
+}
+
+inline DeviceData& GetData(DeviceDispatchable dispatchable) {
+    return *reinterpret_cast<DeviceData*>(GetDataInternal(dispatchable));
+}
+
+}  // namespace driver
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_DRIVER_H
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
new file mode 100644
index 0000000..5bd2159
--- /dev/null
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// WARNING: This file is generated. See ../README.md for instructions.
+
+#include <string.h>
+#include <algorithm>
+#include <log/log.h>
+
+#include "driver.h"
+
+namespace vulkan {
+namespace driver {
+
+namespace {
+
+// clang-format off
+
+VKAPI_ATTR VkResult checkedCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+        return CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
+    } else {
+        ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR void checkedDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+        DestroySwapchainKHR(device, swapchain, pAllocator);
+    } else {
+        ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+    }
+}
+
+VKAPI_ATTR VkResult checkedGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+        return GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
+    } else {
+        ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR VkResult checkedAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
+        return AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
+    } else {
+        ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR VkResult checkedQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) {
+    if (GetData(queue).hook_extensions[ProcHook::KHR_swapchain]) {
+        return QueuePresentKHR(queue, pPresentInfo);
+    } else {
+        ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+// clang-format on
+
+const ProcHook g_proc_hooks[] = {
+    // clang-format off
+    {
+        "vkAcquireImageANDROID",
+        ProcHook::DEVICE,
+        ProcHook::ANDROID_native_buffer,
+        nullptr,
+        nullptr,
+    },
+    {
+        "vkAcquireNextImageKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_swapchain,
+        reinterpret_cast<PFN_vkVoidFunction>(AcquireNextImageKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedAcquireNextImageKHR),
+    },
+    {
+        "vkAllocateCommandBuffers",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(AllocateCommandBuffers),
+        nullptr,
+    },
+    {
+        "vkCreateAndroidSurfaceKHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_android_surface,
+        reinterpret_cast<PFN_vkVoidFunction>(CreateAndroidSurfaceKHR),
+        nullptr,
+    },
+    {
+        "vkCreateDebugReportCallbackEXT",
+        ProcHook::INSTANCE,
+        ProcHook::EXT_debug_report,
+        reinterpret_cast<PFN_vkVoidFunction>(CreateDebugReportCallbackEXT),
+        nullptr,
+    },
+    {
+        "vkCreateDevice",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(CreateDevice),
+        nullptr,
+    },
+    {
+        "vkCreateInstance",
+        ProcHook::GLOBAL,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(CreateInstance),
+        nullptr,
+    },
+    {
+        "vkCreateSwapchainKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_swapchain,
+        reinterpret_cast<PFN_vkVoidFunction>(CreateSwapchainKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedCreateSwapchainKHR),
+    },
+    {
+        "vkDebugReportMessageEXT",
+        ProcHook::INSTANCE,
+        ProcHook::EXT_debug_report,
+        reinterpret_cast<PFN_vkVoidFunction>(DebugReportMessageEXT),
+        nullptr,
+    },
+    {
+        "vkDestroyDebugReportCallbackEXT",
+        ProcHook::INSTANCE,
+        ProcHook::EXT_debug_report,
+        reinterpret_cast<PFN_vkVoidFunction>(DestroyDebugReportCallbackEXT),
+        nullptr,
+    },
+    {
+        "vkDestroyDevice",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(DestroyDevice),
+        nullptr,
+    },
+    {
+        "vkDestroyInstance",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance),
+        nullptr,
+    },
+    {
+        "vkDestroySurfaceKHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_surface,
+        reinterpret_cast<PFN_vkVoidFunction>(DestroySurfaceKHR),
+        nullptr,
+    },
+    {
+        "vkDestroySwapchainKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_swapchain,
+        reinterpret_cast<PFN_vkVoidFunction>(DestroySwapchainKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedDestroySwapchainKHR),
+    },
+    {
+        "vkEnumerateDeviceExtensionProperties",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(EnumerateDeviceExtensionProperties),
+        nullptr,
+    },
+    {
+        "vkEnumerateInstanceExtensionProperties",
+        ProcHook::GLOBAL,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties),
+        nullptr,
+    },
+    {
+        "vkEnumeratePhysicalDevices",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices),
+        nullptr,
+    },
+    {
+        "vkGetDeviceProcAddr",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr),
+        nullptr,
+    },
+    {
+        "vkGetDeviceQueue",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue),
+        nullptr,
+    },
+    {
+        "vkGetInstanceProcAddr",
+        ProcHook::INSTANCE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_surface,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceCapabilitiesKHR),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceSurfaceFormatsKHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_surface,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceFormatsKHR),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceSurfacePresentModesKHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_surface,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfacePresentModesKHR),
+        nullptr,
+    },
+    {
+        "vkGetPhysicalDeviceSurfaceSupportKHR",
+        ProcHook::INSTANCE,
+        ProcHook::KHR_surface,
+        reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceSupportKHR),
+        nullptr,
+    },
+    {
+        "vkGetSwapchainGrallocUsageANDROID",
+        ProcHook::DEVICE,
+        ProcHook::ANDROID_native_buffer,
+        nullptr,
+        nullptr,
+    },
+    {
+        "vkGetSwapchainImagesKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_swapchain,
+        reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainImagesKHR),
+    },
+    {
+        "vkQueuePresentKHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_swapchain,
+        reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedQueuePresentKHR),
+    },
+    {
+        "vkQueueSignalReleaseImageANDROID",
+        ProcHook::DEVICE,
+        ProcHook::ANDROID_native_buffer,
+        nullptr,
+        nullptr,
+    },
+    // clang-format on
+};
+
+}  // anonymous
+
+const ProcHook* GetProcHook(const char* name) {
+    const auto& begin = g_proc_hooks;
+    const auto& end =
+        g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
+    const auto hook = std::lower_bound(
+        begin, end, name,
+        [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
+    return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
+}
+
+ProcHook::Extension GetProcHookExtension(const char* name) {
+    // clang-format off
+    if (strcmp(name, "VK_ANDROID_native_buffer") == 0) return ProcHook::ANDROID_native_buffer;
+    if (strcmp(name, "VK_EXT_debug_report") == 0) return ProcHook::EXT_debug_report;
+    if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
+    if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
+    if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
+    // clang-format on
+    return ProcHook::EXTENSION_UNKNOWN;
+}
+
+#define UNLIKELY(expr) __builtin_expect((expr), 0)
+
+#define INIT_PROC(obj, proc)                                           \
+    do {                                                               \
+        data.driver.proc =                                             \
+            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \
+        if (UNLIKELY(!data.driver.proc)) {                             \
+            ALOGE("missing " #obj " proc: vk" #proc);                  \
+            success = false;                                           \
+        }                                                              \
+    } while (0)
+
+#define INIT_PROC_EXT(ext, obj, proc)  \
+    do {                               \
+        if (extensions[ProcHook::ext]) \
+            INIT_PROC(obj, proc);      \
+    } while (0)
+
+bool InitDriverTable(VkInstance instance,
+                     PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(instance);
+    bool success = true;
+
+    // clang-format off
+    INIT_PROC(instance, DestroyInstance);
+    INIT_PROC(instance, EnumeratePhysicalDevices);
+    INIT_PROC(instance, GetInstanceProcAddr);
+    INIT_PROC(instance, CreateDevice);
+    INIT_PROC(instance, EnumerateDeviceLayerProperties);
+    INIT_PROC(instance, EnumerateDeviceExtensionProperties);
+    INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
+    INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
+    INIT_PROC_EXT(EXT_debug_report, instance, DebugReportMessageEXT);
+    // clang-format on
+
+    return success;
+}
+
+bool InitDriverTable(VkDevice dev,
+                     PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(dev);
+    bool success = true;
+
+    // clang-format off
+    INIT_PROC(dev, GetDeviceProcAddr);
+    INIT_PROC(dev, DestroyDevice);
+    INIT_PROC(dev, GetDeviceQueue);
+    INIT_PROC(dev, CreateImage);
+    INIT_PROC(dev, DestroyImage);
+    INIT_PROC(dev, AllocateCommandBuffers);
+    INIT_PROC_EXT(ANDROID_native_buffer, dev, GetSwapchainGrallocUsageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, dev, AcquireImageANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, dev, QueueSignalReleaseImageANDROID);
+    // clang-format on
+
+    return success;
+}
+
+}  // namespace driver
+}  // namespace vulkan
+
+// clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
new file mode 100644
index 0000000..ca17d57
--- /dev/null
+++ b/vulkan/libvulkan/driver_gen.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// WARNING: This file is generated. See ../README.md for instructions.
+
+#ifndef LIBVULKAN_DRIVER_GEN_H
+#define LIBVULKAN_DRIVER_GEN_H
+
+#include <bitset>
+#include <vulkan/vulkan.h>
+#include <vulkan/vk_android_native_buffer.h>
+
+namespace vulkan {
+namespace driver {
+
+struct ProcHook {
+    enum Type {
+        GLOBAL,
+        INSTANCE,
+        DEVICE,
+    };
+    enum Extension {
+        ANDROID_native_buffer,
+        EXT_debug_report,
+        KHR_android_surface,
+        KHR_surface,
+        KHR_swapchain,
+
+        EXTENSION_CORE,  // valid bit
+        EXTENSION_COUNT,
+        EXTENSION_UNKNOWN,
+    };
+
+    const char* name;
+    Type type;
+    Extension extension;
+
+    PFN_vkVoidFunction proc;
+    PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
+};
+
+struct InstanceDriverTable {
+    // clang-format off
+    PFN_vkDestroyInstance DestroyInstance;
+    PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
+    PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
+    PFN_vkCreateDevice CreateDevice;
+    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
+    PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
+    PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
+    PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
+    PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+    // clang-format on
+};
+
+struct DeviceDriverTable {
+    // clang-format off
+    PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
+    PFN_vkDestroyDevice DestroyDevice;
+    PFN_vkGetDeviceQueue GetDeviceQueue;
+    PFN_vkCreateImage CreateImage;
+    PFN_vkDestroyImage DestroyImage;
+    PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
+    PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
+    PFN_vkAcquireImageANDROID AcquireImageANDROID;
+    PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
+    // clang-format on
+};
+
+const ProcHook* GetProcHook(const char* name);
+ProcHook::Extension GetProcHookExtension(const char* name);
+
+bool InitDriverTable(VkInstance instance,
+                     PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+bool InitDriverTable(VkDevice dev,
+                     PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+
+}  // namespace driver
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_DRIVER_TABLE_H
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index aec0fd0..6b53a9a 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -16,7 +16,7 @@
 
 // #define LOG_NDEBUG 0
 
-#include "loader.h"
+#include "layers_extensions.h"
 #include <alloca.h>
 #include <dirent.h>
 #include <dlfcn.h>
@@ -28,8 +28,6 @@
 #include <log/log.h>
 #include <vulkan/vulkan_loader_data.h>
 
-using namespace vulkan;
-
 // TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
 // not a good long-term solution. Having a hard-coded enum of extensions is
 // bad, of course. Representing sets of extensions (requested, supported, etc.)
@@ -50,12 +48,13 @@
 // with a mask saying what kind(s) it is.
 
 namespace vulkan {
+namespace api {
+
 struct Layer {
     VkLayerProperties properties;
     size_t library_idx;
     std::vector<VkExtensionProperties> extensions;
 };
-}  // namespace vulkan
 
 namespace {
 
@@ -341,8 +340,6 @@
 
 }  // anonymous namespace
 
-namespace vulkan {
-
 void DiscoverLayers() {
     if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0))
         DiscoverLayersInDirectory("/data/local/debug/vulkan");
@@ -425,22 +422,5 @@
                         }) != layer_->extensions.cend();
 }
 
-InstanceExtension InstanceExtensionFromName(const char* name) {
-    if (strcmp(name, VK_KHR_SURFACE_EXTENSION_NAME) == 0)
-        return kKHR_surface;
-    if (strcmp(name, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME) == 0)
-        return kKHR_android_surface;
-    if (strcmp(name, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0)
-        return kEXT_debug_report;
-    return kInstanceExtensionCount;
-}
-
-DeviceExtension DeviceExtensionFromName(const char* name) {
-    if (strcmp(name, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0)
-        return kKHR_swapchain;
-    if (strcmp(name, VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) == 0)
-        return kANDROID_native_buffer;
-    return kDeviceExtensionCount;
-}
-
+}  // namespace api
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/layers_extensions.h b/vulkan/libvulkan/layers_extensions.h
new file mode 100644
index 0000000..7e7bfd3
--- /dev/null
+++ b/vulkan/libvulkan/layers_extensions.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 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 LIBVULKAN_LAYERS_EXTENSIONS_H
+#define LIBVULKAN_LAYERS_EXTENSIONS_H 1
+
+#include <vulkan/vulkan.h>
+
+namespace vulkan {
+namespace api {
+
+struct Layer;
+class LayerRef {
+   public:
+    LayerRef(Layer* layer);
+    LayerRef(LayerRef&& other);
+    ~LayerRef();
+    LayerRef(const LayerRef&) = delete;
+    LayerRef& operator=(const LayerRef&) = delete;
+
+    const char* GetName() const;
+    uint32_t GetSpecVersion();
+
+    // provides bool-like behavior
+    operator const Layer*() const { return layer_; }
+
+    PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;
+    PFN_vkGetDeviceProcAddr GetGetDeviceProcAddr() const;
+
+    bool SupportsExtension(const char* name) const;
+
+   private:
+    Layer* layer_;
+};
+
+void DiscoverLayers();
+uint32_t EnumerateInstanceLayers(uint32_t count, VkLayerProperties* properties);
+uint32_t EnumerateDeviceLayers(uint32_t count, VkLayerProperties* properties);
+void GetInstanceLayerExtensions(const char* name,
+                                const VkExtensionProperties** properties,
+                                uint32_t* count);
+void GetDeviceLayerExtensions(const char* name,
+                              const VkExtensionProperties** properties,
+                              uint32_t* count);
+LayerRef GetInstanceLayerRef(const char* name);
+LayerRef GetDeviceLayerRef(const char* name);
+
+}  // namespace api
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_LAYERS_EXTENSIONS_H
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
deleted file mode 100644
index 6531968..0000000
--- a/vulkan/libvulkan/loader.cpp
+++ /dev/null
@@ -1,1721 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-
-// module header
-#include "loader.h"
-// standard C headers
-#include <dirent.h>
-#include <dlfcn.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/prctl.h>
-// standard C++ headers
-#include <algorithm>
-#include <mutex>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-#include <vector>
-// platform/library headers
-#include <cutils/properties.h>
-#include <hardware/hwvulkan.h>
-#include <log/log.h>
-#include <vulkan/vulkan_loader_data.h>
-#include <vulkan/vk_layer_interface.h>
-
-// #define ENABLE_ALLOC_CALLSTACKS 1
-#if ENABLE_ALLOC_CALLSTACKS
-#include <utils/CallStack.h>
-#define ALOGD_CALLSTACK(...)                             \
-    do {                                                 \
-        ALOGD(__VA_ARGS__);                              \
-        android::CallStack callstack;                    \
-        callstack.update();                              \
-        callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, "  "); \
-    } while (false)
-#else
-#define ALOGD_CALLSTACK(...) \
-    do {                     \
-    } while (false)
-#endif
-
-using namespace vulkan;
-
-static const uint32_t kMaxPhysicalDevices = 4;
-
-namespace {
-
-// ----------------------------------------------------------------------------
-
-// Standard-library allocator that delegates to VkAllocationCallbacks.
-//
-// TODO(jessehall): This class currently always uses
-// VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE. The scope to use could be a template
-// parameter or a constructor parameter. The former would help catch bugs
-// where we use the wrong scope, e.g. adding a command-scope string to an
-// instance-scope vector. But that might also be pretty annoying to deal with.
-template <class T>
-class CallbackAllocator {
-   public:
-    typedef T value_type;
-
-    CallbackAllocator(const VkAllocationCallbacks* alloc_input)
-        : alloc(alloc_input) {}
-
-    template <class T2>
-    CallbackAllocator(const CallbackAllocator<T2>& other)
-        : alloc(other.alloc) {}
-
-    T* allocate(std::size_t n) {
-        void* mem =
-            alloc->pfnAllocation(alloc->pUserData, n * sizeof(T), alignof(T),
-                                 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
-        if (!mem)
-            throw std::bad_alloc();
-        return static_cast<T*>(mem);
-    }
-
-    void deallocate(T* array, std::size_t /*n*/) noexcept {
-        alloc->pfnFree(alloc->pUserData, array);
-    }
-
-    const VkAllocationCallbacks* alloc;
-};
-// These are needed in order to move Strings
-template <class T>
-bool operator==(const CallbackAllocator<T>& alloc1,
-                const CallbackAllocator<T>& alloc2) {
-    return alloc1.alloc == alloc2.alloc;
-}
-template <class T>
-bool operator!=(const CallbackAllocator<T>& alloc1,
-                const CallbackAllocator<T>& alloc2) {
-    return !(alloc1 == alloc2);
-}
-
-template <class T>
-using Vector = std::vector<T, CallbackAllocator<T>>;
-
-typedef std::basic_string<char, std::char_traits<char>, CallbackAllocator<char>>
-    String;
-
-// ----------------------------------------------------------------------------
-
-VKAPI_ATTR void* DefaultAllocate(void*,
-                                 size_t size,
-                                 size_t alignment,
-                                 VkSystemAllocationScope) {
-    void* ptr = nullptr;
-    // Vulkan requires 'alignment' to be a power of two, but posix_memalign
-    // additionally requires that it be at least sizeof(void*).
-    int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
-    ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment,
-                    ret, ptr);
-    return ret == 0 ? ptr : nullptr;
-}
-
-VKAPI_ATTR void* DefaultReallocate(void*,
-                                   void* ptr,
-                                   size_t size,
-                                   size_t alignment,
-                                   VkSystemAllocationScope) {
-    if (size == 0) {
-        free(ptr);
-        return nullptr;
-    }
-
-    // TODO(jessehall): Right now we never shrink allocations; if the new
-    // request is smaller than the existing chunk, we just continue using it.
-    // Right now the loader never reallocs, so this doesn't matter. If that
-    // changes, or if this code is copied into some other project, this should
-    // probably have a heuristic to allocate-copy-free when doing so will save
-    // "enough" space.
-    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
-    if (size <= old_size)
-        return ptr;
-
-    void* new_ptr = nullptr;
-    if (posix_memalign(&new_ptr, alignment, size) != 0)
-        return nullptr;
-    if (ptr) {
-        memcpy(new_ptr, ptr, std::min(old_size, size));
-        free(ptr);
-    }
-    return new_ptr;
-}
-
-VKAPI_ATTR void DefaultFree(void*, void* ptr) {
-    ALOGD_CALLSTACK("Free: %p", ptr);
-    free(ptr);
-}
-
-const VkAllocationCallbacks kDefaultAllocCallbacks = {
-    .pUserData = nullptr,
-    .pfnAllocation = DefaultAllocate,
-    .pfnReallocation = DefaultReallocate,
-    .pfnFree = DefaultFree,
-};
-
-// ----------------------------------------------------------------------------
-// Global Data and Initialization
-
-hwvulkan_device_t* g_hwdevice = nullptr;
-InstanceExtensionSet g_driver_instance_extensions;
-
-void LoadVulkanHAL() {
-    static const hwvulkan_module_t* module;
-    int result =
-        hw_get_module("vulkan", reinterpret_cast<const hw_module_t**>(&module));
-    if (result != 0) {
-        ALOGE("failed to load vulkan hal: %s (%d)", strerror(-result), result);
-        return;
-    }
-    result = module->common.methods->open(
-        &module->common, HWVULKAN_DEVICE_0,
-        reinterpret_cast<hw_device_t**>(&g_hwdevice));
-    if (result != 0) {
-        ALOGE("failed to open vulkan driver: %s (%d)", strerror(-result),
-              result);
-        module = nullptr;
-        return;
-    }
-
-    VkResult vkresult;
-    uint32_t count;
-    if ((vkresult = g_hwdevice->EnumerateInstanceExtensionProperties(
-             nullptr, &count, nullptr)) != VK_SUCCESS) {
-        ALOGE("driver EnumerateInstanceExtensionProperties failed: %d",
-              vkresult);
-        g_hwdevice->common.close(&g_hwdevice->common);
-        g_hwdevice = nullptr;
-        module = nullptr;
-        return;
-    }
-    VkExtensionProperties* extensions = static_cast<VkExtensionProperties*>(
-        alloca(count * sizeof(VkExtensionProperties)));
-    if ((vkresult = g_hwdevice->EnumerateInstanceExtensionProperties(
-             nullptr, &count, extensions)) != VK_SUCCESS) {
-        ALOGE("driver EnumerateInstanceExtensionProperties failed: %d",
-              vkresult);
-        g_hwdevice->common.close(&g_hwdevice->common);
-        g_hwdevice = nullptr;
-        module = nullptr;
-        return;
-    }
-    ALOGV_IF(count > 0, "Driver-supported instance extensions:");
-    for (uint32_t i = 0; i < count; i++) {
-        ALOGV("  %s (v%u)", extensions[i].extensionName,
-              extensions[i].specVersion);
-        InstanceExtension id =
-            InstanceExtensionFromName(extensions[i].extensionName);
-        if (id != kInstanceExtensionCount)
-            g_driver_instance_extensions.set(id);
-    }
-    // Ignore driver attempts to support loader extensions
-    g_driver_instance_extensions.reset(kKHR_surface);
-    g_driver_instance_extensions.reset(kKHR_android_surface);
-}
-
-bool EnsureInitialized() {
-    static std::once_flag once_flag;
-    std::call_once(once_flag, []() {
-        LoadVulkanHAL();
-        DiscoverLayers();
-    });
-    return g_hwdevice != nullptr;
-}
-
-// -----------------------------------------------------------------------------
-
-struct Instance {
-    Instance(const VkAllocationCallbacks* alloc_callbacks)
-        : dispatch_ptr(&dispatch),
-          handle(reinterpret_cast<VkInstance>(&dispatch_ptr)),
-          alloc(alloc_callbacks),
-          num_physical_devices(0),
-          active_layers(CallbackAllocator<LayerRef>(alloc)),
-          message(VK_NULL_HANDLE) {
-        memset(&dispatch, 0, sizeof(dispatch));
-        memset(physical_devices, 0, sizeof(physical_devices));
-        enabled_extensions.reset();
-        drv.instance = VK_NULL_HANDLE;
-        memset(&drv.dispatch, 0, sizeof(drv.dispatch));
-        drv.num_physical_devices = 0;
-    }
-
-    ~Instance() {}
-
-    const InstanceDispatchTable* dispatch_ptr;
-    const VkInstance handle;
-    InstanceDispatchTable dispatch;
-
-    const VkAllocationCallbacks* alloc;
-    uint32_t num_physical_devices;
-    VkPhysicalDevice physical_devices_top[kMaxPhysicalDevices];
-    VkPhysicalDevice physical_devices[kMaxPhysicalDevices];
-    DeviceExtensionSet physical_device_driver_extensions[kMaxPhysicalDevices];
-
-    Vector<LayerRef> active_layers;
-    VkDebugReportCallbackEXT message;
-    DebugReportCallbackList debug_report_callbacks;
-    InstanceExtensionSet enabled_extensions;
-
-    struct {
-        VkInstance instance;
-        DriverDispatchTable dispatch;
-        uint32_t num_physical_devices;
-    } drv;  // may eventually be an array
-};
-
-struct Device {
-    Device(Instance* instance_)
-        : instance(instance_),
-          active_layers(CallbackAllocator<LayerRef>(instance->alloc)) {
-        memset(&dispatch, 0, sizeof(dispatch));
-        enabled_extensions.reset();
-    }
-    DeviceDispatchTable dispatch;
-    Instance* instance;
-    PFN_vkGetDeviceProcAddr get_device_proc_addr;
-    Vector<LayerRef> active_layers;
-    DeviceExtensionSet enabled_extensions;
-};
-
-template <typename THandle>
-struct HandleTraits {};
-template <>
-struct HandleTraits<VkInstance> {
-    typedef Instance LoaderObjectType;
-};
-template <>
-struct HandleTraits<VkPhysicalDevice> {
-    typedef Instance LoaderObjectType;
-};
-template <>
-struct HandleTraits<VkDevice> {
-    typedef Device LoaderObjectType;
-};
-template <>
-struct HandleTraits<VkQueue> {
-    typedef Device LoaderObjectType;
-};
-template <>
-struct HandleTraits<VkCommandBuffer> {
-    typedef Device LoaderObjectType;
-};
-
-template <typename THandle>
-typename HandleTraits<THandle>::LoaderObjectType& GetDispatchParent(
-    THandle handle) {
-    // TODO(jessehall): Make Instance and Device POD types (by removing the
-    // non-default constructors), so that offsetof is actually legal to use.
-    // The specific case we're using here is safe in gcc/clang (and probably
-    // most other C++ compilers), but isn't guaranteed by C++.
-    typedef typename HandleTraits<THandle>::LoaderObjectType ObjectType;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Winvalid-offsetof"
-    const size_t kDispatchOffset = offsetof(ObjectType, dispatch);
-#pragma clang diagnostic pop
-
-    const auto& dispatch = GetDispatchTable(handle);
-    uintptr_t dispatch_addr = reinterpret_cast<uintptr_t>(&dispatch);
-    uintptr_t object_addr = dispatch_addr - kDispatchOffset;
-    return *reinterpret_cast<ObjectType*>(object_addr);
-}
-
-// -----------------------------------------------------------------------------
-
-void DestroyDevice(Device* device) {
-    const VkAllocationCallbacks* alloc = device->instance->alloc;
-    device->~Device();
-    alloc->pfnFree(alloc->pUserData, device);
-}
-
-template <class TObject>
-LayerRef GetLayerRef(const char* name);
-template <>
-LayerRef GetLayerRef<Instance>(const char* name) {
-    return GetInstanceLayerRef(name);
-}
-template <>
-LayerRef GetLayerRef<Device>(const char* name) {
-    return GetDeviceLayerRef(name);
-}
-
-template <class TObject>
-bool ActivateLayer(TObject* object, const char* name) {
-    LayerRef layer(GetLayerRef<TObject>(name));
-    if (!layer)
-        return false;
-    if (std::find(object->active_layers.begin(), object->active_layers.end(),
-                  layer) == object->active_layers.end()) {
-        try {
-            object->active_layers.push_back(std::move(layer));
-        } catch (std::bad_alloc&) {
-            // TODO(jessehall): We should fail with VK_ERROR_OUT_OF_MEMORY
-            // if we can't enable a requested layer. Callers currently ignore
-            // ActivateLayer's return value.
-            ALOGW("failed to activate layer '%s': out of memory", name);
-            return false;
-        }
-    }
-    ALOGV("activated layer '%s'", name);
-    return true;
-}
-
-struct InstanceNamesPair {
-    Instance* instance;
-    Vector<String>* layer_names;
-};
-
-void SetLayerNamesFromProperty(const char* name,
-                               const char* value,
-                               void* data) {
-    try {
-        const char prefix[] = "debug.vulkan.layer.";
-        const size_t prefixlen = sizeof(prefix) - 1;
-        if (value[0] == '\0' || strncmp(name, prefix, prefixlen) != 0)
-            return;
-        const char* number_str = name + prefixlen;
-        long layer_number = strtol(number_str, nullptr, 10);
-        if (layer_number <= 0 || layer_number == LONG_MAX) {
-            ALOGW("Cannot use a layer at number %ld from string %s",
-                  layer_number, number_str);
-            return;
-        }
-        auto instance_names_pair = static_cast<InstanceNamesPair*>(data);
-        Vector<String>* layer_names = instance_names_pair->layer_names;
-        Instance* instance = instance_names_pair->instance;
-        size_t layer_size = static_cast<size_t>(layer_number);
-        if (layer_size > layer_names->size()) {
-            layer_names->resize(
-                layer_size, String(CallbackAllocator<char>(instance->alloc)));
-        }
-        (*layer_names)[layer_size - 1] = value;
-    } catch (std::bad_alloc&) {
-        ALOGW("failed to handle property '%s'='%s': out of memory", name,
-              value);
-        return;
-    }
-}
-
-template <class TInfo, class TObject>
-VkResult ActivateAllLayers(TInfo create_info,
-                           Instance* instance,
-                           TObject* object) {
-    ALOG_ASSERT(create_info->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO ||
-                    create_info->sType == VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
-                "Cannot activate layers for unknown object %p", object);
-    CallbackAllocator<char> string_allocator(instance->alloc);
-    // Load system layers
-    if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
-        char layer_prop[PROPERTY_VALUE_MAX];
-        property_get("debug.vulkan.layers", layer_prop, "");
-        char* strtok_state;
-        char* layer_name = nullptr;
-        while ((layer_name = strtok_r(layer_name ? nullptr : layer_prop, ":",
-                                      &strtok_state))) {
-            ActivateLayer(object, layer_name);
-        }
-        Vector<String> layer_names(CallbackAllocator<String>(instance->alloc));
-        InstanceNamesPair instance_names_pair = {.instance = instance,
-                                                 .layer_names = &layer_names};
-        property_list(SetLayerNamesFromProperty,
-                      static_cast<void*>(&instance_names_pair));
-        for (auto layer_name_element : layer_names) {
-            ActivateLayer(object, layer_name_element.c_str());
-        }
-    }
-    // Load app layers
-    for (uint32_t i = 0; i < create_info->enabledLayerCount; ++i) {
-        if (!ActivateLayer(object, create_info->ppEnabledLayerNames[i])) {
-            ALOGE("requested %s layer '%s' not present",
-                  create_info->sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
-                      ? "instance"
-                      : "device",
-                  create_info->ppEnabledLayerNames[i]);
-            return VK_ERROR_LAYER_NOT_PRESENT;
-        }
-    }
-    return VK_SUCCESS;
-}
-
-template <class TCreateInfo, class TObject>
-bool AddLayersToCreateInfo(TCreateInfo& local_create_info,
-                           const TObject& object,
-                           const VkAllocationCallbacks* alloc,
-                           bool& allocatedMemory) {
-    // This should never happen and means there is a likely a bug in layer
-    // tracking
-    if (object->active_layers.size() < local_create_info.enabledLayerCount) {
-        ALOGE("Total number of layers is less than those enabled by the app!");
-        return false;
-    }
-    // Check if the total number of layers enabled is greater than those
-    // enabled by the application. If it is then we have system enabled
-    // layers which need to be added to the list of layers passed in through
-    // create.
-    if (object->active_layers.size() > local_create_info.enabledLayerCount) {
-        void* mem = alloc->pfnAllocation(
-            alloc->pUserData, object->active_layers.size() * sizeof(char*),
-            alignof(char*), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
-        if (mem) {
-            local_create_info.enabledLayerCount = 0;
-            const char** names = static_cast<const char**>(mem);
-            for (const auto& layer : object->active_layers) {
-                const char* name = layer.GetName();
-                names[local_create_info.enabledLayerCount++] = name;
-            }
-            local_create_info.ppEnabledLayerNames = names;
-        } else {
-            ALOGE("System layers cannot be enabled: memory allocation failed");
-            return false;
-        }
-        allocatedMemory = true;
-    } else {
-        allocatedMemory = false;
-    }
-    return true;
-}
-
-template <class T>
-void FreeAllocatedLayerCreateInfo(T& local_create_info,
-                                  const VkAllocationCallbacks* alloc) {
-    alloc->pfnFree(alloc->pUserData,
-                   const_cast<char**>(local_create_info.ppEnabledLayerNames));
-}
-
-template <class TCreateInfo>
-bool AddExtensionToCreateInfo(TCreateInfo& local_create_info,
-                              const char* extension_name,
-                              const VkAllocationCallbacks* alloc) {
-    uint32_t extension_count = local_create_info.enabledExtensionCount;
-    local_create_info.enabledExtensionCount++;
-    void* mem = alloc->pfnAllocation(
-        alloc->pUserData,
-        local_create_info.enabledExtensionCount * sizeof(char*), alignof(char*),
-        VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
-    if (mem) {
-        const char** enabled_extensions = static_cast<const char**>(mem);
-        for (uint32_t i = 0; i < extension_count; ++i) {
-            enabled_extensions[i] =
-                local_create_info.ppEnabledExtensionNames[i];
-        }
-        enabled_extensions[extension_count] = extension_name;
-        local_create_info.ppEnabledExtensionNames = enabled_extensions;
-    } else {
-        ALOGE("%s extension cannot be enabled: memory allocation failed",
-              extension_name);
-        return false;
-    }
-    return true;
-}
-
-template <class T>
-void FreeAllocatedExtensionCreateInfo(T& local_create_info,
-                                      const VkAllocationCallbacks* alloc) {
-    alloc->pfnFree(
-        alloc->pUserData,
-        const_cast<char**>(local_create_info.ppEnabledExtensionNames));
-}
-
-VKAPI_ATTR
-VkBool32 LogDebugMessageCallback(VkDebugReportFlagsEXT flags,
-                                 VkDebugReportObjectTypeEXT /*objectType*/,
-                                 uint64_t /*object*/,
-                                 size_t /*location*/,
-                                 int32_t message_code,
-                                 const char* layer_prefix,
-                                 const char* message,
-                                 void* /*user_data*/) {
-    if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
-        ALOGE("[%s] Code %d : %s", layer_prefix, message_code, message);
-    } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
-        ALOGW("[%s] Code %d : %s", layer_prefix, message_code, message);
-    }
-    return false;
-}
-
-VkResult Noop() {
-    return VK_SUCCESS;
-}
-
-/*
- * This function will return the pNext pointer of any
- * CreateInfo extensions that are not loader extensions.
- * This is used to skip past the loader extensions prepended
- * to the list during CreateInstance and CreateDevice.
- */
-void* StripCreateExtensions(const void* pNext) {
-    VkLayerInstanceCreateInfo* create_info =
-        const_cast<VkLayerInstanceCreateInfo*>(
-            static_cast<const VkLayerInstanceCreateInfo*>(pNext));
-
-    while (
-        create_info &&
-        (create_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO ||
-         create_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO)) {
-        create_info = const_cast<VkLayerInstanceCreateInfo*>(
-            static_cast<const VkLayerInstanceCreateInfo*>(create_info->pNext));
-    }
-
-    return create_info;
-}
-
-// Clean up and deallocate an Instance; called from both the failure paths in
-// CreateInstance_Top as well as from DestroyInstance_Top. This function does
-// not call down the dispatch chain; that should be done before calling this
-// function, iff the lower vkCreateInstance call has been made and returned
-// successfully.
-void DestroyInstance(Instance* instance,
-                     const VkAllocationCallbacks* allocator) {
-    if (instance->message) {
-        PFN_vkDestroyDebugReportCallbackEXT destroy_debug_report_callback;
-        destroy_debug_report_callback =
-            reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(
-                GetInstanceProcAddr_Top(instance->handle,
-                                        "vkDestroyDebugReportCallbackEXT"));
-        destroy_debug_report_callback(instance->handle, instance->message,
-                                      allocator);
-    }
-    instance->~Instance();
-    allocator->pfnFree(allocator->pUserData, instance);
-}
-
-}  // anonymous namespace
-
-namespace vulkan {
-
-// -----------------------------------------------------------------------------
-// "Bottom" functions. These are called at the end of the instance dispatch
-// chain.
-
-VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info,
-                               const VkAllocationCallbacks* allocator,
-                               VkInstance* vkinstance) {
-    VkResult result;
-
-    VkLayerInstanceCreateInfo* chain_info =
-        const_cast<VkLayerInstanceCreateInfo*>(
-            static_cast<const VkLayerInstanceCreateInfo*>(create_info->pNext));
-    while (
-        chain_info &&
-        !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
-          chain_info->function == VK_LAYER_FUNCTION_INSTANCE)) {
-        chain_info = const_cast<VkLayerInstanceCreateInfo*>(
-            static_cast<const VkLayerInstanceCreateInfo*>(chain_info->pNext));
-    }
-    ALOG_ASSERT(chain_info != nullptr, "Missing initialization chain info!");
-
-    Instance& instance = GetDispatchParent(
-        static_cast<VkInstance>(chain_info->u.instanceInfo.instance_info));
-
-    // Check that all enabled extensions are supported
-    uint32_t num_driver_extensions = 0;
-    for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
-        const char* name = create_info->ppEnabledExtensionNames[i];
-        InstanceExtension id = InstanceExtensionFromName(name);
-        if (id != kInstanceExtensionCount) {
-            if (g_driver_instance_extensions[id]) {
-                num_driver_extensions++;
-                instance.enabled_extensions.set(id);
-                continue;
-            }
-            if (id == kKHR_surface || id == kKHR_android_surface) {
-                instance.enabled_extensions.set(id);
-                continue;
-            }
-            // The loader natively supports debug report.
-            if (id == kEXT_debug_report) {
-                continue;
-            }
-        }
-        bool supported = false;
-        for (const auto& layer : instance.active_layers) {
-            if (layer.SupportsExtension(name))
-                supported = true;
-        }
-        if (!supported) {
-            ALOGE(
-                "requested instance extension '%s' not supported by "
-                "loader, driver, or any active layers",
-                name);
-            DestroyInstance_Bottom(instance.handle, allocator);
-            return VK_ERROR_EXTENSION_NOT_PRESENT;
-        }
-    }
-
-    VkInstanceCreateInfo driver_create_info = *create_info;
-    driver_create_info.pNext = StripCreateExtensions(create_info->pNext);
-    driver_create_info.enabledLayerCount = 0;
-    driver_create_info.ppEnabledLayerNames = nullptr;
-    driver_create_info.enabledExtensionCount = 0;
-    driver_create_info.ppEnabledExtensionNames = nullptr;
-    if (num_driver_extensions > 0) {
-        const char** names = static_cast<const char**>(
-            alloca(num_driver_extensions * sizeof(char*)));
-        for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
-            const char* name = create_info->ppEnabledExtensionNames[i];
-            InstanceExtension id = InstanceExtensionFromName(name);
-            if (id != kInstanceExtensionCount) {
-                if (g_driver_instance_extensions[id]) {
-                    names[driver_create_info.enabledExtensionCount++] = name;
-                    continue;
-                }
-            }
-        }
-        driver_create_info.ppEnabledExtensionNames = names;
-        ALOG_ASSERT(
-            driver_create_info.enabledExtensionCount == num_driver_extensions,
-            "counted enabled driver instance extensions twice and got "
-            "different answers!");
-    }
-
-    result = g_hwdevice->CreateInstance(&driver_create_info, instance.alloc,
-                                        &instance.drv.instance);
-    if (result != VK_SUCCESS) {
-        DestroyInstance_Bottom(instance.handle, allocator);
-        return result;
-    }
-
-    hwvulkan_dispatch_t* drv_dispatch =
-        reinterpret_cast<hwvulkan_dispatch_t*>(instance.drv.instance);
-    if (drv_dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
-        ALOGE("invalid VkInstance dispatch magic: 0x%" PRIxPTR,
-              drv_dispatch->magic);
-        DestroyInstance_Bottom(instance.handle, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-    // Skip setting drv_dispatch->vtbl, since we never call through it;
-    // we go through instance.drv.dispatch instead.
-
-    if (!LoadDriverDispatchTable(
-            instance.drv.instance, g_hwdevice->GetInstanceProcAddr,
-            instance.enabled_extensions, instance.drv.dispatch)) {
-        DestroyInstance_Bottom(instance.handle, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    uint32_t num_physical_devices = 0;
-    result = instance.drv.dispatch.EnumeratePhysicalDevices(
-        instance.drv.instance, &num_physical_devices, nullptr);
-    if (result != VK_SUCCESS) {
-        DestroyInstance_Bottom(instance.handle, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-    num_physical_devices = std::min(num_physical_devices, kMaxPhysicalDevices);
-    result = instance.drv.dispatch.EnumeratePhysicalDevices(
-        instance.drv.instance, &num_physical_devices,
-        instance.physical_devices);
-    if (result != VK_SUCCESS) {
-        DestroyInstance_Bottom(instance.handle, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    Vector<VkExtensionProperties> extensions(
-        Vector<VkExtensionProperties>::allocator_type(instance.alloc));
-    for (uint32_t i = 0; i < num_physical_devices; i++) {
-        hwvulkan_dispatch_t* pdev_dispatch =
-            reinterpret_cast<hwvulkan_dispatch_t*>(
-                instance.physical_devices[i]);
-        if (pdev_dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
-            ALOGE("invalid VkPhysicalDevice dispatch magic: 0x%" PRIxPTR,
-                  pdev_dispatch->magic);
-            DestroyInstance_Bottom(instance.handle, allocator);
-            return VK_ERROR_INITIALIZATION_FAILED;
-        }
-        pdev_dispatch->vtbl = instance.dispatch_ptr;
-
-        uint32_t count;
-        if ((result = instance.drv.dispatch.EnumerateDeviceExtensionProperties(
-                 instance.physical_devices[i], nullptr, &count, nullptr)) !=
-            VK_SUCCESS) {
-            ALOGW("driver EnumerateDeviceExtensionProperties(%u) failed: %d", i,
-                  result);
-            continue;
-        }
-        try {
-            extensions.resize(count);
-        } catch (std::bad_alloc&) {
-            ALOGE("instance creation failed: out of memory");
-            DestroyInstance_Bottom(instance.handle, allocator);
-            return VK_ERROR_OUT_OF_HOST_MEMORY;
-        }
-        if ((result = instance.drv.dispatch.EnumerateDeviceExtensionProperties(
-                 instance.physical_devices[i], nullptr, &count,
-                 extensions.data())) != VK_SUCCESS) {
-            ALOGW("driver EnumerateDeviceExtensionProperties(%u) failed: %d", i,
-                  result);
-            continue;
-        }
-        ALOGV_IF(count > 0, "driver gpu[%u] supports extensions:", i);
-        for (const auto& extension : extensions) {
-            ALOGV("  %s (v%u)", extension.extensionName, extension.specVersion);
-            DeviceExtension id =
-                DeviceExtensionFromName(extension.extensionName);
-            if (id == kDeviceExtensionCount) {
-                ALOGW("driver gpu[%u] extension '%s' unknown to loader", i,
-                      extension.extensionName);
-            } else {
-                instance.physical_device_driver_extensions[i].set(id);
-            }
-        }
-        // Ignore driver attempts to support loader extensions
-        instance.physical_device_driver_extensions[i].reset(kKHR_swapchain);
-    }
-    instance.drv.num_physical_devices = num_physical_devices;
-    instance.num_physical_devices = instance.drv.num_physical_devices;
-
-    *vkinstance = instance.handle;
-
-    return VK_SUCCESS;
-}
-
-VkResult CreateAndroidSurfaceKHR_Disabled(VkInstance,
-                                          const VkAndroidSurfaceCreateInfoKHR*,
-                                          const VkAllocationCallbacks*,
-                                          VkSurfaceKHR*) {
-    ALOGE(
-        "VK_KHR_android_surface not enabled. vkCreateAndroidSurfaceKHR not "
-        "executed.");
-
-    return VK_SUCCESS;
-}
-
-void DestroySurfaceKHR_Disabled(VkInstance,
-                                VkSurfaceKHR,
-                                const VkAllocationCallbacks*) {
-    ALOGE("VK_KHR_surface not enabled. vkDestroySurfaceKHR not executed.");
-}
-
-VkResult GetPhysicalDeviceSurfaceSupportKHR_Disabled(VkPhysicalDevice,
-                                                     uint32_t,
-                                                     VkSurfaceKHR,
-                                                     VkBool32*) {
-    ALOGE(
-        "VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceSupportKHR not "
-        "executed.");
-
-    return VK_SUCCESS;
-}
-
-VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR_Disabled(
-    VkPhysicalDevice,
-    VkSurfaceKHR,
-    VkSurfaceCapabilitiesKHR*) {
-    ALOGE(
-        "VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceapabilitiesKHR "
-        "not executed.");
-
-    return VK_SUCCESS;
-}
-
-VkResult GetPhysicalDeviceSurfaceFormatsKHR_Disabled(VkPhysicalDevice,
-                                                     VkSurfaceKHR,
-                                                     uint32_t*,
-                                                     VkSurfaceFormatKHR*) {
-    ALOGE(
-        "VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfaceFormatsKHR not "
-        "executed.");
-
-    return VK_SUCCESS;
-}
-
-VkResult GetPhysicalDeviceSurfacePresentModesKHR_Disabled(VkPhysicalDevice,
-                                                          VkSurfaceKHR,
-                                                          uint32_t*,
-                                                          VkPresentModeKHR*) {
-    ALOGE(
-        "VK_KHR_surface not enabled. vkGetPhysicalDeviceSurfacePresentModesKHR "
-        "not executed.");
-
-    return VK_SUCCESS;
-}
-
-PFN_vkVoidFunction GetInstanceProcAddr_Bottom(VkInstance vkinstance,
-                                              const char* name) {
-    PFN_vkVoidFunction pfn;
-
-    if (vkinstance) {
-        Instance& instance = GetDispatchParent(vkinstance);
-        if (!instance.enabled_extensions[kKHR_android_surface]) {
-            // KHR_android_surface is not enabled, use error stubs instead
-            if (strcmp(name, "vkCreateAndroidSurfaceKHR") == 0) {
-                return reinterpret_cast<PFN_vkVoidFunction>(
-                    CreateAndroidSurfaceKHR_Disabled);
-            }
-        }
-        if (!instance.enabled_extensions[kKHR_surface]) {
-            // KHR_surface is not enabled, use error stubs instead
-            if (strcmp(name, "vkDestroySurfaceKHR") == 0) {
-                return reinterpret_cast<PFN_vkVoidFunction>(
-                    DestroySurfaceKHR_Disabled);
-            }
-            if (strcmp(name, "vkGetPhysicalDeviceSurfaceSupportKHR") == 0) {
-                return reinterpret_cast<PFN_vkVoidFunction>(
-                    GetPhysicalDeviceSurfaceSupportKHR_Disabled);
-            }
-            if (strcmp(name, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR") ==
-                0) {
-                return reinterpret_cast<PFN_vkVoidFunction>(
-                    GetPhysicalDeviceSurfaceCapabilitiesKHR_Disabled);
-            }
-            if (strcmp(name, "vkGetPhysicalDeviceSurfaceFormatsKHR") == 0) {
-                return reinterpret_cast<PFN_vkVoidFunction>(
-                    GetPhysicalDeviceSurfaceFormatsKHR_Disabled);
-            }
-            if (strcmp(name, "vkGetPhysicalDeviceSurfacePresentModesKHR") ==
-                0) {
-                return reinterpret_cast<PFN_vkVoidFunction>(
-                    GetPhysicalDeviceSurfacePresentModesKHR_Disabled);
-            }
-        }
-    }
-    if ((pfn = GetLoaderBottomProcAddr(name)))
-        return pfn;
-    return nullptr;
-}
-
-VkResult EnumeratePhysicalDevices_Bottom(VkInstance vkinstance,
-                                         uint32_t* pdev_count,
-                                         VkPhysicalDevice* pdevs) {
-    Instance& instance = GetDispatchParent(vkinstance);
-    uint32_t count = instance.num_physical_devices;
-    if (pdevs) {
-        count = std::min(count, *pdev_count);
-        std::copy(instance.physical_devices, instance.physical_devices + count,
-                  pdevs);
-    }
-    *pdev_count = count;
-    return VK_SUCCESS;
-}
-
-void GetPhysicalDeviceProperties_Bottom(
-    VkPhysicalDevice pdev,
-    VkPhysicalDeviceProperties* properties) {
-    GetDispatchParent(pdev).drv.dispatch.GetPhysicalDeviceProperties(
-        pdev, properties);
-}
-
-void GetPhysicalDeviceFeatures_Bottom(VkPhysicalDevice pdev,
-                                      VkPhysicalDeviceFeatures* features) {
-    GetDispatchParent(pdev).drv.dispatch.GetPhysicalDeviceFeatures(pdev,
-                                                                   features);
-}
-
-void GetPhysicalDeviceMemoryProperties_Bottom(
-    VkPhysicalDevice pdev,
-    VkPhysicalDeviceMemoryProperties* properties) {
-    GetDispatchParent(pdev).drv.dispatch.GetPhysicalDeviceMemoryProperties(
-        pdev, properties);
-}
-
-void GetPhysicalDeviceQueueFamilyProperties_Bottom(
-    VkPhysicalDevice pdev,
-    uint32_t* pCount,
-    VkQueueFamilyProperties* properties) {
-    GetDispatchParent(pdev).drv.dispatch.GetPhysicalDeviceQueueFamilyProperties(
-        pdev, pCount, properties);
-}
-
-void GetPhysicalDeviceFormatProperties_Bottom(VkPhysicalDevice pdev,
-                                              VkFormat format,
-                                              VkFormatProperties* properties) {
-    GetDispatchParent(pdev).drv.dispatch.GetPhysicalDeviceFormatProperties(
-        pdev, format, properties);
-}
-
-VkResult GetPhysicalDeviceImageFormatProperties_Bottom(
-    VkPhysicalDevice pdev,
-    VkFormat format,
-    VkImageType type,
-    VkImageTiling tiling,
-    VkImageUsageFlags usage,
-    VkImageCreateFlags flags,
-    VkImageFormatProperties* properties) {
-    return GetDispatchParent(pdev)
-        .drv.dispatch.GetPhysicalDeviceImageFormatProperties(
-            pdev, format, type, tiling, usage, flags, properties);
-}
-
-void GetPhysicalDeviceSparseImageFormatProperties_Bottom(
-    VkPhysicalDevice pdev,
-    VkFormat format,
-    VkImageType type,
-    VkSampleCountFlagBits samples,
-    VkImageUsageFlags usage,
-    VkImageTiling tiling,
-    uint32_t* properties_count,
-    VkSparseImageFormatProperties* properties) {
-    GetDispatchParent(pdev)
-        .drv.dispatch.GetPhysicalDeviceSparseImageFormatProperties(
-            pdev, format, type, samples, usage, tiling, properties_count,
-            properties);
-}
-
-// This is a no-op, the Top function returns the aggregate layer property
-// data. This is to keep the dispatch generator happy.
-VKAPI_ATTR
-VkResult EnumerateDeviceExtensionProperties_Bottom(
-    VkPhysicalDevice /*pdev*/,
-    const char* /*layer_name*/,
-    uint32_t* /*properties_count*/,
-    VkExtensionProperties* /*properties*/) {
-    return VK_SUCCESS;
-}
-
-// This is a no-op, the Top function returns the aggregate layer property
-// data. This is to keep the dispatch generator happy.
-VKAPI_ATTR
-VkResult EnumerateDeviceLayerProperties_Bottom(
-    VkPhysicalDevice /*pdev*/,
-    uint32_t* /*properties_count*/,
-    VkLayerProperties* /*properties*/) {
-    return VK_SUCCESS;
-}
-
-VKAPI_ATTR
-VkResult CreateDevice_Bottom(VkPhysicalDevice gpu,
-                             const VkDeviceCreateInfo* create_info,
-                             const VkAllocationCallbacks* allocator,
-                             VkDevice* device_out) {
-    VkLayerDeviceCreateInfo* chain_info = const_cast<VkLayerDeviceCreateInfo*>(
-        static_cast<const VkLayerDeviceCreateInfo*>(create_info->pNext));
-    while (chain_info &&
-           !(chain_info->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO &&
-             chain_info->function == VK_LAYER_FUNCTION_DEVICE)) {
-        chain_info = const_cast<VkLayerDeviceCreateInfo*>(
-            static_cast<const VkLayerDeviceCreateInfo*>(chain_info->pNext));
-    }
-    ALOG_ASSERT(chain_info != nullptr, "Missing initialization chain info!");
-
-    Instance& instance = GetDispatchParent(gpu);
-    size_t gpu_idx = 0;
-    while (instance.physical_devices[gpu_idx] != gpu)
-        gpu_idx++;
-    Device* device = static_cast<Device*>(chain_info->u.deviceInfo.device_info);
-    PFN_vkGetInstanceProcAddr get_instance_proc_addr =
-        chain_info->u.deviceInfo.pfnNextGetInstanceProcAddr;
-
-    VkDeviceCreateInfo driver_create_info = *create_info;
-    driver_create_info.pNext = StripCreateExtensions(create_info->pNext);
-    driver_create_info.enabledLayerCount = 0;
-    driver_create_info.ppEnabledLayerNames = nullptr;
-
-    uint32_t num_driver_extensions = 0;
-    const char** driver_extensions = static_cast<const char**>(
-        alloca(create_info->enabledExtensionCount * sizeof(const char*)));
-    for (uint32_t i = 0; i < create_info->enabledExtensionCount; i++) {
-        const char* name = create_info->ppEnabledExtensionNames[i];
-        DeviceExtension id = DeviceExtensionFromName(name);
-        if (id != kDeviceExtensionCount) {
-            if (instance.physical_device_driver_extensions[gpu_idx][id]) {
-                driver_extensions[num_driver_extensions++] = name;
-                device->enabled_extensions.set(id);
-                continue;
-            }
-            // Add the VK_ANDROID_native_buffer extension to the list iff
-            // the VK_KHR_swapchain extension was requested
-            if (id == kKHR_swapchain &&
-                instance.physical_device_driver_extensions
-                    [gpu_idx][kANDROID_native_buffer]) {
-                driver_extensions[num_driver_extensions++] =
-                    VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME;
-                device->enabled_extensions.set(id);
-                continue;
-            }
-        }
-        bool supported = false;
-        for (const auto& layer : device->active_layers) {
-            if (layer.SupportsExtension(name))
-                supported = true;
-        }
-        if (!supported) {
-            ALOGE(
-                "requested device extension '%s' not supported by loader, "
-                "driver, or any active layers",
-                name);
-            return VK_ERROR_EXTENSION_NOT_PRESENT;
-        }
-    }
-
-    driver_create_info.enabledExtensionCount = num_driver_extensions;
-    driver_create_info.ppEnabledExtensionNames = driver_extensions;
-    VkDevice drv_device;
-    VkResult result = instance.drv.dispatch.CreateDevice(
-        gpu, &driver_create_info, allocator, &drv_device);
-    if (result != VK_SUCCESS) {
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    hwvulkan_dispatch_t* drv_dispatch =
-        reinterpret_cast<hwvulkan_dispatch_t*>(drv_device);
-    if (drv_dispatch->magic != HWVULKAN_DISPATCH_MAGIC) {
-        ALOGE("invalid VkDevice dispatch magic: 0x%" PRIxPTR,
-              drv_dispatch->magic);
-        PFN_vkDestroyDevice destroy_device =
-            reinterpret_cast<PFN_vkDestroyDevice>(
-                instance.drv.dispatch.GetDeviceProcAddr(drv_device,
-                                                        "vkDestroyDevice"));
-        destroy_device(drv_device, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    // Set dispatch table for newly created Device
-    // CreateDevice_Top will fill in the details
-    drv_dispatch->vtbl = &device->dispatch;
-    device->get_device_proc_addr = reinterpret_cast<PFN_vkGetDeviceProcAddr>(
-        instance.drv.dispatch.GetDeviceProcAddr(drv_device,
-                                                "vkGetDeviceProcAddr"));
-    *device_out = drv_device;
-    return VK_SUCCESS;
-}
-
-void DestroyInstance_Bottom(VkInstance vkinstance,
-                            const VkAllocationCallbacks* allocator) {
-    Instance& instance = GetDispatchParent(vkinstance);
-
-    // These checks allow us to call DestroyInstance_Bottom from any error
-    // path in CreateInstance_Bottom, before the driver instance is fully
-    // initialized.
-    if (instance.drv.instance != VK_NULL_HANDLE &&
-        instance.drv.dispatch.DestroyInstance) {
-        instance.drv.dispatch.DestroyInstance(instance.drv.instance, allocator);
-        instance.drv.instance = VK_NULL_HANDLE;
-    }
-}
-
-VkResult CreateSwapchainKHR_Disabled(VkDevice,
-                                     const VkSwapchainCreateInfoKHR*,
-                                     const VkAllocationCallbacks*,
-                                     VkSwapchainKHR*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkCreateSwapchainKHR not executed.");
-
-    return VK_SUCCESS;
-}
-
-void DestroySwapchainKHR_Disabled(VkDevice,
-                                  VkSwapchainKHR,
-                                  const VkAllocationCallbacks*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
-}
-
-VkResult GetSwapchainImagesKHR_Disabled(VkDevice,
-                                        VkSwapchainKHR,
-                                        uint32_t*,
-                                        VkImage*) {
-    ALOGE(
-        "VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
-
-    return VK_SUCCESS;
-}
-
-VkResult AcquireNextImageKHR_Disabled(VkDevice,
-                                      VkSwapchainKHR,
-                                      uint64_t,
-                                      VkSemaphore,
-                                      VkFence,
-                                      uint32_t*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
-
-    return VK_SUCCESS;
-}
-
-VkResult QueuePresentKHR_Disabled(VkQueue, const VkPresentInfoKHR*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
-
-    return VK_SUCCESS;
-}
-
-PFN_vkVoidFunction GetDeviceProcAddr_Bottom(VkDevice vkdevice,
-                                            const char* name) {
-    if (strcmp(name, "vkCreateDevice") == 0) {
-        return reinterpret_cast<PFN_vkVoidFunction>(CreateDevice_Bottom);
-    }
-
-    Device& device = GetDispatchParent(vkdevice);
-    if (!device.enabled_extensions[kKHR_swapchain]) {
-        if (strcmp(name, "vkCreateSwapchainKHR") == 0) {
-            return reinterpret_cast<PFN_vkVoidFunction>(
-                CreateSwapchainKHR_Disabled);
-        }
-        if (strcmp(name, "vkDestroySwapchainKHR") == 0) {
-            return reinterpret_cast<PFN_vkVoidFunction>(
-                DestroySwapchainKHR_Disabled);
-        }
-        if (strcmp(name, "vkGetSwapchainImagesKHR") == 0) {
-            return reinterpret_cast<PFN_vkVoidFunction>(
-                GetSwapchainImagesKHR_Disabled);
-        }
-        if (strcmp(name, "vkAcquireNextSwapchainImageKHR") == 0) {
-            return reinterpret_cast<PFN_vkVoidFunction>(
-                AcquireNextImageKHR_Disabled);
-        }
-        if (strcmp(name, "vkQueuePresentKHR") == 0) {
-            return reinterpret_cast<PFN_vkVoidFunction>(
-                QueuePresentKHR_Disabled);
-        }
-    }
-
-    // VK_ANDROID_native_buffer should be hidden from applications and layers.
-    // TODO(jessehall): Generate this as part of GetLoaderBottomProcAddr.
-    PFN_vkVoidFunction pfn;
-    if (strcmp(name, "vkGetSwapchainGrallocUsageANDROID") == 0 ||
-        strcmp(name, "vkAcquireImageANDROID") == 0 ||
-        strcmp(name, "vkQueueSignalReleaseImageANDROID") == 0) {
-        return nullptr;
-    }
-    if ((pfn = GetLoaderBottomProcAddr(name)))
-        return pfn;
-    return GetDispatchParent(vkdevice).get_device_proc_addr(vkdevice, name);
-}
-
-// -----------------------------------------------------------------------------
-// Loader top functions. These are called directly from the loader entry
-// points or from the application (via vkGetInstanceProcAddr) without going
-// through a dispatch table.
-
-VkResult EnumerateInstanceExtensionProperties_Top(
-    const char* layer_name,
-    uint32_t* properties_count,
-    VkExtensionProperties* properties) {
-    if (!EnsureInitialized())
-        return VK_ERROR_INITIALIZATION_FAILED;
-
-    const VkExtensionProperties* extensions = nullptr;
-    uint32_t num_extensions = 0;
-    if (layer_name) {
-        GetInstanceLayerExtensions(layer_name, &extensions, &num_extensions);
-    } else {
-        VkExtensionProperties* available = static_cast<VkExtensionProperties*>(
-            alloca(kInstanceExtensionCount * sizeof(VkExtensionProperties)));
-        available[num_extensions++] = VkExtensionProperties{
-            VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION};
-        available[num_extensions++] =
-            VkExtensionProperties{VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
-                                  VK_KHR_ANDROID_SURFACE_SPEC_VERSION};
-        if (g_driver_instance_extensions[kEXT_debug_report]) {
-            available[num_extensions++] =
-                VkExtensionProperties{VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
-                                      VK_EXT_DEBUG_REPORT_SPEC_VERSION};
-        }
-        // TODO(jessehall): We need to also enumerate extensions supported by
-        // implicitly-enabled layers. Currently we don't have that list of
-        // layers until instance creation.
-        extensions = available;
-    }
-
-    if (!properties || *properties_count > num_extensions)
-        *properties_count = num_extensions;
-    if (properties)
-        std::copy(extensions, extensions + *properties_count, properties);
-    return *properties_count < num_extensions ? VK_INCOMPLETE : VK_SUCCESS;
-}
-
-VkResult EnumerateInstanceLayerProperties_Top(uint32_t* properties_count,
-                                              VkLayerProperties* properties) {
-    if (!EnsureInitialized())
-        return VK_ERROR_INITIALIZATION_FAILED;
-
-    uint32_t layer_count =
-        EnumerateInstanceLayers(properties ? *properties_count : 0, properties);
-    if (!properties || *properties_count > layer_count)
-        *properties_count = layer_count;
-    return *properties_count < layer_count ? VK_INCOMPLETE : VK_SUCCESS;
-}
-
-VKAPI_ATTR
-VkResult EnumerateDeviceExtensionProperties_Top(
-    VkPhysicalDevice gpu,
-    const char* layer_name,
-    uint32_t* properties_count,
-    VkExtensionProperties* properties) {
-    const VkExtensionProperties* extensions = nullptr;
-    uint32_t num_extensions = 0;
-
-    ALOGV("EnumerateDeviceExtensionProperties_Top:");
-    if (layer_name) {
-        ALOGV("  layer %s", layer_name);
-        GetDeviceLayerExtensions(layer_name, &extensions, &num_extensions);
-    } else {
-        ALOGV("  no layer");
-        Instance& instance = GetDispatchParent(gpu);
-        size_t gpu_idx = 0;
-        while (instance.physical_devices_top[gpu_idx] != gpu)
-            gpu_idx++;
-        const DeviceExtensionSet driver_extensions =
-            instance.physical_device_driver_extensions[gpu_idx];
-
-        // We only support VK_KHR_swapchain if the GPU supports
-        // VK_ANDROID_native_buffer
-        VkExtensionProperties* available = static_cast<VkExtensionProperties*>(
-            alloca(kDeviceExtensionCount * sizeof(VkExtensionProperties)));
-        if (driver_extensions[kANDROID_native_buffer]) {
-            available[num_extensions++] = VkExtensionProperties{
-                VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_SPEC_VERSION};
-        }
-
-        // TODO(jessehall): We need to also enumerate extensions supported by
-        // implicitly-enabled layers. Currently we don't have that list of
-        // layers until instance creation.
-        extensions = available;
-    }
-
-    ALOGV("  num: %d, extensions: %p", num_extensions, extensions);
-    if (!properties || *properties_count > num_extensions)
-        *properties_count = num_extensions;
-    if (properties)
-        std::copy(extensions, extensions + *properties_count, properties);
-    return *properties_count < num_extensions ? VK_INCOMPLETE : VK_SUCCESS;
-}
-
-VkResult CreateInstance_Top(const VkInstanceCreateInfo* create_info,
-                            const VkAllocationCallbacks* allocator,
-                            VkInstance* instance_out) {
-    VkResult result;
-
-    if (!EnsureInitialized())
-        return VK_ERROR_INITIALIZATION_FAILED;
-
-    if (!allocator)
-        allocator = &kDefaultAllocCallbacks;
-
-    VkInstanceCreateInfo local_create_info = *create_info;
-    create_info = &local_create_info;
-
-    void* instance_mem = allocator->pfnAllocation(
-        allocator->pUserData, sizeof(Instance), alignof(Instance),
-        VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
-    if (!instance_mem)
-        return VK_ERROR_OUT_OF_HOST_MEMORY;
-    Instance* instance = new (instance_mem) Instance(allocator);
-
-    result = ActivateAllLayers(create_info, instance, instance);
-    if (result != VK_SUCCESS) {
-        DestroyInstance(instance, allocator);
-        return result;
-    }
-
-    uint32_t activated_layers = 0;
-    VkLayerInstanceCreateInfo chain_info;
-    VkLayerInstanceLink* layer_instance_link_info = nullptr;
-    PFN_vkGetInstanceProcAddr next_gipa = GetInstanceProcAddr_Bottom;
-    VkInstance local_instance = nullptr;
-
-    if (instance->active_layers.size() > 0) {
-        chain_info.u.pLayerInfo = nullptr;
-        chain_info.pNext = create_info->pNext;
-        chain_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
-        chain_info.function = VK_LAYER_FUNCTION_LINK;
-        local_create_info.pNext = &chain_info;
-
-        layer_instance_link_info = static_cast<VkLayerInstanceLink*>(alloca(
-            sizeof(VkLayerInstanceLink) * instance->active_layers.size()));
-        if (!layer_instance_link_info) {
-            ALOGE("Failed to alloc Instance objects for layers");
-            DestroyInstance(instance, allocator);
-            return VK_ERROR_OUT_OF_HOST_MEMORY;
-        }
-
-        /* Create instance chain of enabled layers */
-        for (auto rit = instance->active_layers.rbegin();
-             rit != instance->active_layers.rend(); ++rit) {
-            LayerRef& layer = *rit;
-            layer_instance_link_info[activated_layers].pNext =
-                chain_info.u.pLayerInfo;
-            layer_instance_link_info[activated_layers]
-                .pfnNextGetInstanceProcAddr = next_gipa;
-            chain_info.u.pLayerInfo =
-                &layer_instance_link_info[activated_layers];
-            next_gipa = layer.GetGetInstanceProcAddr();
-
-            ALOGV("Insert instance layer %s (v%u)", layer.GetName(),
-                  layer.GetSpecVersion());
-
-            activated_layers++;
-        }
-    }
-
-    PFN_vkCreateInstance create_instance =
-        reinterpret_cast<PFN_vkCreateInstance>(
-            next_gipa(VK_NULL_HANDLE, "vkCreateInstance"));
-    if (!create_instance) {
-        DestroyInstance(instance, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-    VkLayerInstanceCreateInfo instance_create_info;
-
-    instance_create_info.sType = VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
-    instance_create_info.function = VK_LAYER_FUNCTION_INSTANCE;
-
-    instance_create_info.u.instanceInfo.instance_info = instance;
-    instance_create_info.u.instanceInfo.pfnNextGetInstanceProcAddr = next_gipa;
-
-    instance_create_info.pNext = local_create_info.pNext;
-    local_create_info.pNext = &instance_create_info;
-
-    // Force enable callback extension if required
-    bool enable_callback = false;
-    if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
-        enable_callback =
-            property_get_bool("debug.vulkan.enable_callback", false);
-        if (enable_callback) {
-            if (!AddExtensionToCreateInfo(local_create_info,
-                                          "VK_EXT_debug_report", allocator)) {
-                DestroyInstance(instance, allocator);
-                return VK_ERROR_INITIALIZATION_FAILED;
-            }
-        }
-    }
-    bool allocatedLayerMem;
-    if (!AddLayersToCreateInfo(local_create_info, instance, allocator,
-                               allocatedLayerMem)) {
-        if (enable_callback) {
-            FreeAllocatedExtensionCreateInfo(local_create_info, allocator);
-        }
-        DestroyInstance(instance, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    result = create_instance(&local_create_info, allocator, &local_instance);
-
-    if (allocatedLayerMem) {
-        FreeAllocatedLayerCreateInfo(local_create_info, allocator);
-    }
-    if (enable_callback) {
-        FreeAllocatedExtensionCreateInfo(local_create_info, allocator);
-    }
-
-    if (result != VK_SUCCESS) {
-        DestroyInstance(instance, allocator);
-        return result;
-    }
-
-    const InstanceDispatchTable& instance_dispatch =
-        GetDispatchTable(local_instance);
-    if (!LoadInstanceDispatchTable(
-            local_instance, next_gipa,
-            const_cast<InstanceDispatchTable&>(instance_dispatch))) {
-        ALOGV("Failed to initialize instance dispatch table");
-        PFN_vkDestroyInstance destroy_instance =
-            reinterpret_cast<PFN_vkDestroyInstance>(
-                next_gipa(local_instance, "vkDestroyInstance"));
-        if (!destroy_instance) {
-            ALOGD("Loader unable to find DestroyInstance");
-            return VK_ERROR_INITIALIZATION_FAILED;
-        }
-        destroy_instance(local_instance, allocator);
-        DestroyInstance(instance, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    // Capture the physical devices from the top of the
-    // chain in case it has been wrapped by a layer.
-    uint32_t num_physical_devices = 0;
-    result = instance_dispatch.EnumeratePhysicalDevices(
-        local_instance, &num_physical_devices, nullptr);
-    if (result != VK_SUCCESS) {
-        DestroyInstance(instance, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-    num_physical_devices = std::min(num_physical_devices, kMaxPhysicalDevices);
-    result = instance_dispatch.EnumeratePhysicalDevices(
-        local_instance, &num_physical_devices,
-        instance->physical_devices_top);
-    if (result != VK_SUCCESS) {
-        DestroyInstance(instance, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-    *instance_out = local_instance;
-
-    if (enable_callback) {
-        const VkDebugReportCallbackCreateInfoEXT callback_create_info = {
-            .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT,
-            .flags =
-                VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
-            .pfnCallback = LogDebugMessageCallback,
-        };
-        PFN_vkCreateDebugReportCallbackEXT create_debug_report_callback =
-            reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(
-                GetInstanceProcAddr_Top(instance->handle,
-                                        "vkCreateDebugReportCallbackEXT"));
-        create_debug_report_callback(instance->handle, &callback_create_info,
-                                     allocator, &instance->message);
-    }
-
-    return result;
-}
-
-PFN_vkVoidFunction GetInstanceProcAddr_Top(VkInstance vkinstance,
-                                           const char* name) {
-    // vkGetInstanceProcAddr(NULL_HANDLE, ..) only works for global commands
-    if (!vkinstance)
-        return GetLoaderGlobalProcAddr(name);
-
-    const InstanceDispatchTable& dispatch = GetDispatchTable(vkinstance);
-    PFN_vkVoidFunction pfn;
-    // Always go through the loader-top function if there is one.
-    if ((pfn = GetLoaderTopProcAddr(name)))
-        return pfn;
-    // Otherwise, look up the handler in the instance dispatch table
-    if ((pfn = GetDispatchProcAddr(dispatch, name)))
-        return pfn;
-    // Anything not handled already must be a device-dispatched function
-    // without a loader-top. We must return a function that will dispatch based
-    // on the dispatchable object parameter -- which is exactly what the
-    // exported functions do. So just return them here.
-    return GetLoaderExportProcAddr(name);
-}
-
-void DestroyInstance_Top(VkInstance vkinstance,
-                         const VkAllocationCallbacks* allocator) {
-    if (!vkinstance)
-        return;
-    if (!allocator)
-        allocator = &kDefaultAllocCallbacks;
-    GetDispatchTable(vkinstance).DestroyInstance(vkinstance, allocator);
-    DestroyInstance(&(GetDispatchParent(vkinstance)), allocator);
-}
-
-VKAPI_ATTR
-VkResult EnumerateDeviceLayerProperties_Top(VkPhysicalDevice /*pdev*/,
-                                               uint32_t* properties_count,
-                                               VkLayerProperties* properties) {
-    uint32_t layer_count =
-        EnumerateDeviceLayers(properties ? *properties_count : 0, properties);
-    if (!properties || *properties_count > layer_count)
-        *properties_count = layer_count;
-    return *properties_count < layer_count ? VK_INCOMPLETE : VK_SUCCESS;
-}
-
-VKAPI_ATTR
-VkResult CreateDevice_Top(VkPhysicalDevice gpu,
-                          const VkDeviceCreateInfo* create_info,
-                          const VkAllocationCallbacks* allocator,
-                          VkDevice* device_out) {
-    Instance& instance = GetDispatchParent(gpu);
-    VkResult result;
-
-    // FIXME(jessehall): We don't have good conventions or infrastructure yet to
-    // do better than just using the instance allocator and scope for
-    // everything. See b/26732122.
-    if (true /*!allocator*/)
-        allocator = instance.alloc;
-
-    void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Device),
-                                         alignof(Device),
-                                         VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-    if (!mem)
-        return VK_ERROR_OUT_OF_HOST_MEMORY;
-    Device* device = new (mem) Device(&instance);
-
-    result = ActivateAllLayers(create_info, &instance, device);
-    if (result != VK_SUCCESS) {
-        DestroyDevice(device);
-        return result;
-    }
-
-    uint32_t activated_layers = 0;
-    VkLayerDeviceCreateInfo chain_info;
-    VkLayerDeviceLink* layer_device_link_info = nullptr;
-    PFN_vkGetInstanceProcAddr next_gipa = GetInstanceProcAddr_Bottom;
-    PFN_vkGetDeviceProcAddr next_gdpa = GetDeviceProcAddr_Bottom;
-    VkDeviceCreateInfo local_create_info = *create_info;
-    VkDevice local_device = nullptr;
-
-    if (device->active_layers.size() > 0) {
-        chain_info.u.pLayerInfo = nullptr;
-        chain_info.pNext = local_create_info.pNext;
-        chain_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
-        chain_info.function = VK_LAYER_FUNCTION_LINK;
-        local_create_info.pNext = &chain_info;
-
-        layer_device_link_info = static_cast<VkLayerDeviceLink*>(
-            alloca(sizeof(VkLayerDeviceLink) * device->active_layers.size()));
-        if (!layer_device_link_info) {
-            ALOGE("Failed to alloc Device objects for layers");
-            DestroyDevice(device);
-            return VK_ERROR_OUT_OF_HOST_MEMORY;
-        }
-
-        /* Create device chain of enabled layers */
-        for (auto rit = device->active_layers.rbegin();
-             rit != device->active_layers.rend(); ++rit) {
-            LayerRef& layer = *rit;
-            layer_device_link_info[activated_layers].pNext =
-                chain_info.u.pLayerInfo;
-            layer_device_link_info[activated_layers].pfnNextGetDeviceProcAddr =
-                next_gdpa;
-            layer_device_link_info[activated_layers]
-                .pfnNextGetInstanceProcAddr = next_gipa;
-            chain_info.u.pLayerInfo = &layer_device_link_info[activated_layers];
-
-            next_gipa = layer.GetGetInstanceProcAddr();
-            next_gdpa = layer.GetGetDeviceProcAddr();
-
-            ALOGV("Insert device layer %s (v%u)", layer.GetName(),
-                  layer.GetSpecVersion());
-
-            activated_layers++;
-        }
-    }
-
-    PFN_vkCreateDevice create_device = reinterpret_cast<PFN_vkCreateDevice>(
-        next_gipa(instance.handle, "vkCreateDevice"));
-    if (!create_device) {
-        ALOGE("Unable to find vkCreateDevice for driver");
-        DestroyDevice(device);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    VkLayerDeviceCreateInfo device_create_info;
-
-    device_create_info.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
-    device_create_info.function = VK_LAYER_FUNCTION_DEVICE;
-
-    device_create_info.u.deviceInfo.device_info = device;
-    device_create_info.u.deviceInfo.pfnNextGetInstanceProcAddr = next_gipa;
-
-    device_create_info.pNext = local_create_info.pNext;
-    local_create_info.pNext = &device_create_info;
-
-    bool allocatedLayerMem;
-    if (!AddLayersToCreateInfo(local_create_info, device, allocator,
-                               allocatedLayerMem)) {
-        DestroyDevice(device);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    result = create_device(gpu, &local_create_info, allocator, &local_device);
-
-    if (allocatedLayerMem) {
-        FreeAllocatedLayerCreateInfo(local_create_info, allocator);
-    }
-
-    if (result != VK_SUCCESS) {
-        DestroyDevice(device);
-        return result;
-    }
-
-    // Set dispatch table for newly created Device
-    hwvulkan_dispatch_t* vulkan_dispatch =
-        reinterpret_cast<hwvulkan_dispatch_t*>(local_device);
-    vulkan_dispatch->vtbl = &device->dispatch;
-
-    const DeviceDispatchTable& device_dispatch = GetDispatchTable(local_device);
-    if (!LoadDeviceDispatchTable(
-            local_device, next_gdpa,
-            const_cast<DeviceDispatchTable&>(device_dispatch))) {
-        ALOGV("Failed to initialize device dispatch table");
-        PFN_vkDestroyDevice destroy_device =
-            reinterpret_cast<PFN_vkDestroyDevice>(
-                next_gipa(instance.handle, "vkDestroyDevice"));
-        ALOG_ASSERT(destroy_device != nullptr,
-                    "Loader unable to find DestroyDevice");
-        destroy_device(local_device, allocator);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-    *device_out = local_device;
-
-    return VK_SUCCESS;
-}
-
-PFN_vkVoidFunction GetDeviceProcAddr_Top(VkDevice device, const char* name) {
-    PFN_vkVoidFunction pfn;
-    if (!device)
-        return nullptr;
-    if ((pfn = GetLoaderTopProcAddr(name)))
-        return pfn;
-    return GetDispatchProcAddr(GetDispatchTable(device), name);
-}
-
-void GetDeviceQueue_Top(VkDevice vkdevice,
-                        uint32_t family,
-                        uint32_t index,
-                        VkQueue* queue_out) {
-    const auto& table = GetDispatchTable(vkdevice);
-    table.GetDeviceQueue(vkdevice, family, index, queue_out);
-    hwvulkan_dispatch_t* queue_dispatch =
-        reinterpret_cast<hwvulkan_dispatch_t*>(*queue_out);
-    if (queue_dispatch->magic != HWVULKAN_DISPATCH_MAGIC &&
-        queue_dispatch->vtbl != &table)
-        ALOGE("invalid VkQueue dispatch magic: 0x%" PRIxPTR,
-              queue_dispatch->magic);
-    queue_dispatch->vtbl = &table;
-}
-
-VkResult AllocateCommandBuffers_Top(
-    VkDevice vkdevice,
-    const VkCommandBufferAllocateInfo* alloc_info,
-    VkCommandBuffer* cmdbufs) {
-    const auto& table = GetDispatchTable(vkdevice);
-    VkResult result =
-        table.AllocateCommandBuffers(vkdevice, alloc_info, cmdbufs);
-    if (result != VK_SUCCESS)
-        return result;
-    for (uint32_t i = 0; i < alloc_info->commandBufferCount; i++) {
-        hwvulkan_dispatch_t* cmdbuf_dispatch =
-            reinterpret_cast<hwvulkan_dispatch_t*>(cmdbufs[i]);
-        ALOGE_IF(cmdbuf_dispatch->magic != HWVULKAN_DISPATCH_MAGIC,
-                 "invalid VkCommandBuffer dispatch magic: 0x%" PRIxPTR,
-                 cmdbuf_dispatch->magic);
-        cmdbuf_dispatch->vtbl = &table;
-    }
-    return VK_SUCCESS;
-}
-
-void DestroyDevice_Top(VkDevice vkdevice,
-                       const VkAllocationCallbacks* /*allocator*/) {
-    if (!vkdevice)
-        return;
-    Device& device = GetDispatchParent(vkdevice);
-    device.dispatch.DestroyDevice(vkdevice, device.instance->alloc);
-    DestroyDevice(&device);
-}
-
-// -----------------------------------------------------------------------------
-
-const VkAllocationCallbacks* GetAllocator(VkInstance vkinstance) {
-    return GetDispatchParent(vkinstance).alloc;
-}
-
-const VkAllocationCallbacks* GetAllocator(VkDevice vkdevice) {
-    return GetDispatchParent(vkdevice).instance->alloc;
-}
-
-VkInstance GetDriverInstance(VkInstance instance) {
-    return GetDispatchParent(instance).drv.instance;
-}
-
-const DriverDispatchTable& GetDriverDispatch(VkInstance instance) {
-    return GetDispatchParent(instance).drv.dispatch;
-}
-
-const DriverDispatchTable& GetDriverDispatch(VkDevice device) {
-    return GetDispatchParent(device).instance->drv.dispatch;
-}
-
-const DriverDispatchTable& GetDriverDispatch(VkQueue queue) {
-    return GetDispatchParent(queue).instance->drv.dispatch;
-}
-
-DebugReportCallbackList& GetDebugReportCallbacks(VkInstance instance) {
-    return GetDispatchParent(instance).debug_report_callbacks;
-}
-
-}  // namespace vulkan
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
deleted file mode 100644
index ada66f1..0000000
--- a/vulkan/libvulkan/loader.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2015 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 LIBVULKAN_LOADER_H
-#define LIBVULKAN_LOADER_H 1
-
-#include <bitset>
-#include "dispatch_gen.h"
-#include "debug_report.h"
-
-namespace vulkan {
-
-enum InstanceExtension {
-    kKHR_surface,
-    kKHR_android_surface,
-    kEXT_debug_report,
-    kInstanceExtensionCount
-};
-typedef std::bitset<kInstanceExtensionCount> InstanceExtensionSet;
-
-enum DeviceExtension {
-    kKHR_swapchain,
-    kANDROID_native_buffer,
-    kDeviceExtensionCount
-};
-typedef std::bitset<kDeviceExtensionCount> DeviceExtensionSet;
-
-inline const InstanceDispatchTable& GetDispatchTable(VkInstance instance) {
-    return **reinterpret_cast<InstanceDispatchTable**>(instance);
-}
-
-inline const InstanceDispatchTable& GetDispatchTable(
-    VkPhysicalDevice physical_device) {
-    return **reinterpret_cast<InstanceDispatchTable**>(physical_device);
-}
-
-inline const DeviceDispatchTable& GetDispatchTable(VkDevice device) {
-    return **reinterpret_cast<DeviceDispatchTable**>(device);
-}
-
-inline const DeviceDispatchTable& GetDispatchTable(VkQueue queue) {
-    return **reinterpret_cast<DeviceDispatchTable**>(queue);
-}
-
-inline const DeviceDispatchTable& GetDispatchTable(
-    VkCommandBuffer command_buffer) {
-    return **reinterpret_cast<DeviceDispatchTable**>(command_buffer);
-}
-
-// -----------------------------------------------------------------------------
-// dispatch_gen.cpp
-
-PFN_vkVoidFunction GetLoaderExportProcAddr(const char* name);
-PFN_vkVoidFunction GetLoaderGlobalProcAddr(const char* name);
-PFN_vkVoidFunction GetLoaderTopProcAddr(const char* name);
-PFN_vkVoidFunction GetLoaderBottomProcAddr(const char* name);
-PFN_vkVoidFunction GetDispatchProcAddr(const InstanceDispatchTable& dispatch,
-                                       const char* name);
-PFN_vkVoidFunction GetDispatchProcAddr(const DeviceDispatchTable& dispatch,
-                                       const char* name);
-bool LoadInstanceDispatchTable(VkInstance instance,
-                               PFN_vkGetInstanceProcAddr get_proc_addr,
-                               InstanceDispatchTable& dispatch);
-bool LoadDeviceDispatchTable(VkDevice device,
-                             PFN_vkGetDeviceProcAddr get_proc_addr,
-                             DeviceDispatchTable& dispatch);
-bool LoadDriverDispatchTable(VkInstance instance,
-                             PFN_vkGetInstanceProcAddr get_proc_addr,
-                             const InstanceExtensionSet& extensions,
-                             DriverDispatchTable& dispatch);
-
-// -----------------------------------------------------------------------------
-// loader.cpp
-
-// clang-format off
-VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties_Top(const char* layer_name, uint32_t* count, VkExtensionProperties* properties);
-VKAPI_ATTR VkResult EnumerateInstanceLayerProperties_Top(uint32_t* count, VkLayerProperties* properties);
-VKAPI_ATTR VkResult CreateInstance_Top(const VkInstanceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkInstance* instance_out);
-VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr_Top(VkInstance instance, const char* name);
-VKAPI_ATTR void DestroyInstance_Top(VkInstance instance, const VkAllocationCallbacks* allocator);
-VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr_Top(VkDevice drv_device, const char* name);
-VKAPI_ATTR void GetDeviceQueue_Top(VkDevice drv_device, uint32_t family, uint32_t index, VkQueue* out_queue);
-VKAPI_ATTR VkResult AllocateCommandBuffers_Top(VkDevice device, const VkCommandBufferAllocateInfo* alloc_info, VkCommandBuffer* cmdbufs);
-VKAPI_ATTR VkResult EnumerateDeviceLayerProperties_Top(VkPhysicalDevice pdev, uint32_t* properties_count, VkLayerProperties* properties);
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties_Top(VkPhysicalDevice pdev, const char * layer_name, uint32_t* properties_count, VkExtensionProperties* properties);
-VKAPI_ATTR VkResult CreateDevice_Top(VkPhysicalDevice pdev, const VkDeviceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkDevice* device_out);
-VKAPI_ATTR void DestroyDevice_Top(VkDevice drv_device, const VkAllocationCallbacks* allocator);
-
-VKAPI_ATTR VkResult CreateInstance_Bottom(const VkInstanceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkInstance* vkinstance);
-VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr_Bottom(VkInstance, const char* name);
-VKAPI_ATTR VkResult EnumeratePhysicalDevices_Bottom(VkInstance vkinstance, uint32_t* pdev_count, VkPhysicalDevice* pdevs);
-VKAPI_ATTR void GetPhysicalDeviceProperties_Bottom(VkPhysicalDevice pdev, VkPhysicalDeviceProperties* properties);
-VKAPI_ATTR void GetPhysicalDeviceFeatures_Bottom(VkPhysicalDevice pdev, VkPhysicalDeviceFeatures* features);
-VKAPI_ATTR void GetPhysicalDeviceMemoryProperties_Bottom(VkPhysicalDevice pdev, VkPhysicalDeviceMemoryProperties* properties);
-VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties_Bottom(VkPhysicalDevice pdev, uint32_t* properties_count, VkQueueFamilyProperties* properties);
-VKAPI_ATTR void GetPhysicalDeviceFormatProperties_Bottom(VkPhysicalDevice pdev, VkFormat format, VkFormatProperties* properties);
-VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties_Bottom(VkPhysicalDevice pdev, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* properties);
-VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties_Bottom(VkPhysicalDevice pdev, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* properties_count, VkSparseImageFormatProperties* properties);
-VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties_Bottom(VkPhysicalDevice pdev, const char* layer_name, uint32_t* properties_count, VkExtensionProperties* properties);
-VKAPI_ATTR VkResult EnumerateDeviceLayerProperties_Bottom(VkPhysicalDevice pdev, uint32_t* properties_count, VkLayerProperties* properties);
-VKAPI_ATTR VkResult CreateDevice_Bottom(VkPhysicalDevice pdev, const VkDeviceCreateInfo* create_info, const VkAllocationCallbacks* allocator, VkDevice* device_out);
-VKAPI_ATTR void DestroyInstance_Bottom(VkInstance vkinstance, const VkAllocationCallbacks* allocator);
-VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr_Bottom(VkDevice vkdevice, const char* name);
-// clang-format on
-
-const VkAllocationCallbacks* GetAllocator(VkInstance instance);
-const VkAllocationCallbacks* GetAllocator(VkDevice device);
-VkInstance GetDriverInstance(VkInstance instance);
-const DriverDispatchTable& GetDriverDispatch(VkInstance instance);
-const DriverDispatchTable& GetDriverDispatch(VkDevice device);
-const DriverDispatchTable& GetDriverDispatch(VkQueue queue);
-DebugReportCallbackList& GetDebugReportCallbacks(VkInstance instance);
-
-// -----------------------------------------------------------------------------
-// swapchain.cpp
-
-// clang-format off
-VKAPI_ATTR VkResult CreateAndroidSurfaceKHR_Bottom(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
-VKAPI_ATTR void DestroySurfaceKHR_Bottom(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* allocator);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR_Bottom(VkPhysicalDevice pdev, uint32_t queue_family, VkSurfaceKHR surface, VkBool32* pSupported);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR_Bottom(VkPhysicalDevice pdev, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR_Bottom(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkSurfaceFormatKHR* formats);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR_Bottom(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkPresentModeKHR* modes);
-VKAPI_ATTR VkResult CreateSwapchainKHR_Bottom(VkDevice device, const VkSwapchainCreateInfoKHR* create_info, const VkAllocationCallbacks* allocator, VkSwapchainKHR* swapchain_handle);
-VKAPI_ATTR void DestroySwapchainKHR_Bottom(VkDevice device, VkSwapchainKHR swapchain_handle, const VkAllocationCallbacks* allocator);
-VKAPI_ATTR VkResult GetSwapchainImagesKHR_Bottom(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
-VKAPI_ATTR VkResult AcquireNextImageKHR_Bottom(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
-VKAPI_ATTR VkResult QueuePresentKHR_Bottom(VkQueue queue, const VkPresentInfoKHR* present_info);
-
-VKAPI_ATTR VkResult CreateAndroidSurfaceKHR_Disabled(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
-VKAPI_ATTR void DestroySurfaceKHR_Disabled(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks*);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR_Disabled(VkPhysicalDevice, uint32_t, VkSurfaceKHR, VkBool32*);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR_Disabled(VkPhysicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR*);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR_Disabled(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkSurfaceFormatKHR*);
-VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR_Disabled(VkPhysicalDevice, VkSurfaceKHR, uint32_t*, VkPresentModeKHR*);
-VKAPI_ATTR VkResult CreateSwapchainKHR_Disabled(VkDevice device, const VkSwapchainCreateInfoKHR* create_info, const VkAllocationCallbacks* allocator, VkSwapchainKHR* swapchain_handle);
-VKAPI_ATTR void DestroySwapchainKHR_Disabled(VkDevice device, VkSwapchainKHR swapchain_handle, const VkAllocationCallbacks* allocator);
-VKAPI_ATTR VkResult GetSwapchainImagesKHR_Disabled(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
-VKAPI_ATTR VkResult AcquireNextImageKHR_Disabled(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
-VKAPI_ATTR VkResult QueuePresentKHR_Disabled(VkQueue queue, const VkPresentInfoKHR* present_info);
-// clang-format on
-
-// -----------------------------------------------------------------------------
-// layers_extensions.cpp
-
-struct Layer;
-class LayerRef {
-   public:
-    LayerRef(Layer* layer);
-    LayerRef(LayerRef&& other);
-    ~LayerRef();
-    LayerRef(const LayerRef&) = delete;
-    LayerRef& operator=(const LayerRef&) = delete;
-
-    const char* GetName() const;
-    uint32_t GetSpecVersion();
-
-    // provides bool-like behavior
-    operator const Layer*() const { return layer_; }
-
-    PFN_vkGetInstanceProcAddr GetGetInstanceProcAddr() const;
-    PFN_vkGetDeviceProcAddr GetGetDeviceProcAddr() const;
-
-    bool SupportsExtension(const char* name) const;
-
-   private:
-    Layer* layer_;
-};
-
-void DiscoverLayers();
-uint32_t EnumerateInstanceLayers(uint32_t count, VkLayerProperties* properties);
-uint32_t EnumerateDeviceLayers(uint32_t count, VkLayerProperties* properties);
-void GetInstanceLayerExtensions(const char* name,
-                                const VkExtensionProperties** properties,
-                                uint32_t* count);
-void GetDeviceLayerExtensions(const char* name,
-                              const VkExtensionProperties** properties,
-                              uint32_t* count);
-LayerRef GetInstanceLayerRef(const char* name);
-LayerRef GetDeviceLayerRef(const char* name);
-
-InstanceExtension InstanceExtensionFromName(const char* name);
-DeviceExtension DeviceExtensionFromName(const char* name);
-
-}  // namespace vulkan
-
-#endif  // LIBVULKAN_LOADER_H
diff --git a/vulkan/libvulkan/stubhal.cpp b/vulkan/libvulkan/stubhal.cpp
new file mode 100644
index 0000000..a74d370
--- /dev/null
+++ b/vulkan/libvulkan/stubhal.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* NOTE:
+ * This stub HAL is only used internally by the loader when a real HAL
+ * implementation is not present, in order to avoid needing "null HAL" checks
+ * throughout the loader. It does not enumerate any physical devices, and is
+ * only as conformant to the Vulkan and Android HAL interfaces as the loader
+ * needs it to be. Do not use it as an example of a correct implementation; the
+ * code in ../null_driver is better for that.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "vkstub"
+
+#include <array>
+#include <bitset>
+#include <mutex>
+#include <hardware/hwvulkan.h>
+#include <log/log.h>
+#include "stubhal.h"
+
+namespace vulkan {
+namespace stubhal {
+
+namespace {
+
+const size_t kMaxInstances = 32;
+static std::mutex g_instance_mutex;
+static std::bitset<kMaxInstances> g_instance_used(false);
+static std::array<hwvulkan_dispatch_t, kMaxInstances> g_instances;
+
+[[noreturn]] void NoOp() {
+    LOG_ALWAYS_FATAL("invalid stub function called");
+}
+
+VKAPI_ATTR VkResult
+EnumerateInstanceExtensionProperties(const char* /*layer_name*/,
+                                     uint32_t* count,
+                                     VkExtensionProperties* /*properties*/) {
+    *count = 0;
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult
+EnumerateInstanceLayerProperties(uint32_t* count,
+                                 VkLayerProperties* /*properties*/) {
+    *count = 0;
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* /*create_info*/,
+                                   const VkAllocationCallbacks* /*allocator*/,
+                                   VkInstance* instance) {
+    std::lock_guard<std::mutex> lock(g_instance_mutex);
+    for (size_t i = 0; i < kMaxInstances; i++) {
+        if (!g_instance_used[i]) {
+            g_instance_used[i] = true;
+            g_instances[i].magic = HWVULKAN_DISPATCH_MAGIC;
+            *instance = reinterpret_cast<VkInstance>(&g_instances[i]);
+            return VK_SUCCESS;
+        }
+    }
+    ALOGE("no more instances available (max=%zu)", kMaxInstances);
+    return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+VKAPI_ATTR void DestroyInstance(VkInstance instance,
+                                const VkAllocationCallbacks* /*allocator*/) {
+    std::lock_guard<std::mutex> lock(g_instance_mutex);
+    ssize_t idx =
+        reinterpret_cast<hwvulkan_dispatch_t*>(instance) - &g_instances[0];
+    ALOG_ASSERT(idx >= 0 && static_cast<size_t>(idx) < g_instance_used.size(),
+                "DestroyInstance: invalid instance handle");
+    g_instance_used[static_cast<size_t>(idx)] = false;
+}
+
+VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance /*instance*/,
+                                             uint32_t* count,
+                                             VkPhysicalDevice* /*gpus*/) {
+    *count = 0;
+    return VK_SUCCESS;
+}
+
+VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance /*instance*/,
+                                                  const char* name) {
+    if (strcmp(name, "vkCreateInstance") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+    if (strcmp(name, "vkDestroyInstance") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance);
+    if (strcmp(name, "vkEnumerateInstanceExtensionProperties") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(
+            EnumerateInstanceExtensionProperties);
+    if (strcmp(name, "vkEnumeratePhysicalDevices") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices);
+    if (strcmp(name, "vkGetInstanceProcAddr") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr);
+
+    // None of the other Vulkan functions should ever be called, as they all
+    // take a VkPhysicalDevice or other object obtained from a physical device.
+    return reinterpret_cast<PFN_vkVoidFunction>(NoOp);
+}
+
+}  // anonymous namespace
+
+const hwvulkan_device_t kDevice = {
+    .common =
+        {
+            .tag = HARDWARE_DEVICE_TAG,
+            .version = HWVULKAN_DEVICE_API_VERSION_0_1,
+            .module = nullptr,
+            .close = nullptr,
+        },
+    .EnumerateInstanceExtensionProperties =
+        EnumerateInstanceExtensionProperties,
+    .CreateInstance = CreateInstance,
+    .GetInstanceProcAddr = GetInstanceProcAddr,
+};
+
+}  // namespace stubhal
+}  // namespace vulkan
diff --git a/vulkan/libvulkan/stubhal.h b/vulkan/libvulkan/stubhal.h
new file mode 100644
index 0000000..9ba7d04
--- /dev/null
+++ b/vulkan/libvulkan/stubhal.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 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 LIBVULKAN_STUBHAL_H
+#define LIBVULKAN_STUBHAL_H 1
+
+struct hwvulkan_device_t;
+
+namespace vulkan {
+namespace stubhal {
+
+extern const hwvulkan_device_t kDevice;
+
+}  // namespace stubhal
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_STUBHAL_H
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 7f944cf..69e8e84 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -15,98 +15,23 @@
  */
 
 #include <algorithm>
-#include <memory>
 
 #include <gui/BufferQueue.h>
 #include <log/log.h>
 #include <sync/sync.h>
+#include <utils/StrongPointer.h>
 
-#include "loader.h"
-
-using namespace vulkan;
+#include "driver.h"
 
 // TODO(jessehall): Currently we don't have a good error code for when a native
 // window operation fails. Just returning INITIALIZATION_FAILED for now. Later
 // versions (post SDK 0.9) of the API/extension have a better error code.
 // When updating to that version, audit all error returns.
+namespace vulkan {
+namespace driver {
 
 namespace {
 
-// ----------------------------------------------------------------------------
-// These functions/classes form an adaptor that allows objects to be refcounted
-// by both android::sp<> and std::shared_ptr<> simultaneously, and delegates
-// allocation of the shared_ptr<> control structure to VkAllocationCallbacks.
-// The
-// platform holds a reference to the ANativeWindow using its embedded reference
-// count, and the ANativeWindow implementation holds references to the
-// ANativeWindowBuffers using their embedded reference counts, so the
-// shared_ptr *must* cooperate with these and hold at least one reference to
-// the object using the embedded reference count.
-
-template <typename T>
-struct NativeBaseDeleter {
-    void operator()(T* obj) { obj->common.decRef(&obj->common); }
-};
-
-template <typename Host>
-struct AllocScope {};
-
-template <>
-struct AllocScope<VkInstance> {
-    static const VkSystemAllocationScope kScope =
-        VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE;
-};
-
-template <>
-struct AllocScope<VkDevice> {
-    static const VkSystemAllocationScope kScope =
-        VK_SYSTEM_ALLOCATION_SCOPE_DEVICE;
-};
-
-template <typename T>
-class VulkanAllocator {
-   public:
-    typedef T value_type;
-
-    VulkanAllocator(const VkAllocationCallbacks& allocator,
-                    VkSystemAllocationScope scope)
-        : allocator_(allocator), scope_(scope) {}
-
-    template <typename U>
-    explicit VulkanAllocator(const VulkanAllocator<U>& other)
-        : allocator_(other.allocator_), scope_(other.scope_) {}
-
-    T* allocate(size_t n) const {
-        T* p = static_cast<T*>(allocator_.pfnAllocation(
-            allocator_.pUserData, n * sizeof(T), alignof(T), scope_));
-        if (!p)
-            throw std::bad_alloc();
-        return p;
-    }
-    void deallocate(T* p, size_t) const noexcept {
-        return allocator_.pfnFree(allocator_.pUserData, p);
-    }
-
-   private:
-    template <typename U>
-    friend class VulkanAllocator;
-    const VkAllocationCallbacks& allocator_;
-    const VkSystemAllocationScope scope_;
-};
-
-template <typename T, typename Host>
-std::shared_ptr<T> InitSharedPtr(Host host, T* obj) {
-    try {
-        obj->common.incRef(&obj->common);
-        return std::shared_ptr<T>(
-            obj, NativeBaseDeleter<T>(),
-            VulkanAllocator<T>(*GetAllocator(host), AllocScope<Host>::kScope));
-    } catch (std::bad_alloc&) {
-        obj->common.decRef(&obj->common);
-        return nullptr;
-    }
-}
-
 const VkSurfaceTransformFlagsKHR kSupportedTransforms =
     VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR |
     VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
@@ -183,7 +108,7 @@
 // ----------------------------------------------------------------------------
 
 struct Surface {
-    std::shared_ptr<ANativeWindow> window;
+    android::sp<ANativeWindow> window;
 };
 
 VkSurfaceKHR HandleFromSurface(Surface* surface) {
@@ -204,7 +129,7 @@
     struct Image {
         Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
         VkImage image;
-        std::shared_ptr<ANativeWindowBuffer> buffer;
+        android::sp<ANativeWindowBuffer> buffer;
         // The fence is only valid when the buffer is dequeued, and should be
         // -1 any other time. When valid, we own the fd, and must ensure it is
         // closed: either by closing it explicitly when queueing the buffer,
@@ -224,16 +149,14 @@
 
 }  // anonymous namespace
 
-namespace vulkan {
-
 VKAPI_ATTR
-VkResult CreateAndroidSurfaceKHR_Bottom(
+VkResult CreateAndroidSurfaceKHR(
     VkInstance instance,
     const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
     const VkAllocationCallbacks* allocator,
     VkSurfaceKHR* out_surface) {
     if (!allocator)
-        allocator = GetAllocator(instance);
+        allocator = &GetData(instance).allocator;
     void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Surface),
                                          alignof(Surface),
                                          VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
@@ -241,13 +164,7 @@
         return VK_ERROR_OUT_OF_HOST_MEMORY;
     Surface* surface = new (mem) Surface;
 
-    surface->window = InitSharedPtr(instance, pCreateInfo->window);
-    if (!surface->window) {
-        ALOGE("surface creation failed: out of memory");
-        surface->~Surface();
-        allocator->pfnFree(allocator->pUserData, surface);
-        return VK_ERROR_OUT_OF_HOST_MEMORY;
-    }
+    surface->window = pCreateInfo->window;
 
     // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
     int err =
@@ -267,30 +184,30 @@
 }
 
 VKAPI_ATTR
-void DestroySurfaceKHR_Bottom(VkInstance instance,
-                              VkSurfaceKHR surface_handle,
-                              const VkAllocationCallbacks* allocator) {
+void DestroySurfaceKHR(VkInstance instance,
+                       VkSurfaceKHR surface_handle,
+                       const VkAllocationCallbacks* allocator) {
     Surface* surface = SurfaceFromHandle(surface_handle);
     if (!surface)
         return;
     native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
     surface->~Surface();
     if (!allocator)
-        allocator = GetAllocator(instance);
+        allocator = &GetData(instance).allocator;
     allocator->pfnFree(allocator->pUserData, surface);
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfaceSupportKHR_Bottom(VkPhysicalDevice /*pdev*/,
-                                                   uint32_t /*queue_family*/,
-                                                   VkSurfaceKHR /*surface*/,
-                                                   VkBool32* supported) {
+VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/,
+                                            uint32_t /*queue_family*/,
+                                            VkSurfaceKHR /*surface*/,
+                                            VkBool32* supported) {
     *supported = VK_TRUE;
     return VK_SUCCESS;
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR_Bottom(
+VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(
     VkPhysicalDevice /*pdev*/,
     VkSurfaceKHR surface,
     VkSurfaceCapabilitiesKHR* capabilities) {
@@ -357,11 +274,10 @@
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfaceFormatsKHR_Bottom(
-    VkPhysicalDevice /*pdev*/,
-    VkSurfaceKHR /*surface*/,
-    uint32_t* count,
-    VkSurfaceFormatKHR* formats) {
+VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice /*pdev*/,
+                                            VkSurfaceKHR /*surface*/,
+                                            uint32_t* count,
+                                            VkSurfaceFormatKHR* formats) {
     // TODO(jessehall): Fill out the set of supported formats. Longer term, add
     // a new gralloc method to query whether a (format, usage) pair is
     // supported, and check that for each gralloc format that corresponds to a
@@ -386,11 +302,10 @@
 }
 
 VKAPI_ATTR
-VkResult GetPhysicalDeviceSurfacePresentModesKHR_Bottom(
-    VkPhysicalDevice /*pdev*/,
-    VkSurfaceKHR /*surface*/,
-    uint32_t* count,
-    VkPresentModeKHR* modes) {
+VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice /*pdev*/,
+                                                 VkSurfaceKHR /*surface*/,
+                                                 uint32_t* count,
+                                                 VkPresentModeKHR* modes) {
     const VkPresentModeKHR kModes[] = {
         VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
     };
@@ -407,15 +322,26 @@
 }
 
 VKAPI_ATTR
-VkResult CreateSwapchainKHR_Bottom(VkDevice device,
-                                   const VkSwapchainCreateInfoKHR* create_info,
-                                   const VkAllocationCallbacks* allocator,
-                                   VkSwapchainKHR* swapchain_handle) {
+VkResult CreateSwapchainKHR(VkDevice device,
+                            const VkSwapchainCreateInfoKHR* create_info,
+                            const VkAllocationCallbacks* allocator,
+                            VkSwapchainKHR* swapchain_handle) {
     int err;
     VkResult result = VK_SUCCESS;
 
+    ALOGV("vkCreateSwapchainKHR: surface=0x%" PRIx64
+          " minImageCount=%u imageFormat=%u imageColorSpace=%u"
+          " imageExtent=%ux%u imageUsage=%#x preTransform=%u presentMode=%u"
+          " oldSwapchain=0x%" PRIx64,
+          reinterpret_cast<uint64_t>(create_info->surface),
+          create_info->minImageCount, create_info->imageFormat,
+          create_info->imageColorSpace, create_info->imageExtent.width,
+          create_info->imageExtent.height, create_info->imageUsage,
+          create_info->preTransform, create_info->presentMode,
+          reinterpret_cast<uint64_t>(create_info->oldSwapchain));
+
     if (!allocator)
-        allocator = GetAllocator(device);
+        allocator = &GetData(device).allocator;
 
     ALOGV_IF(create_info->imageArrayLayers != 1,
              "Swapchain imageArrayLayers (%u) != 1 not supported",
@@ -433,10 +359,33 @@
              "swapchain present mode %d not supported",
              create_info->presentMode);
 
+    Surface& surface = *SurfaceFromHandle(create_info->surface);
+
+    // -- Reset the native window --
+    // The native window might have been used previously, and had its properties
+    // changed from defaults. That will affect the answer we get for queries
+    // like MIN_UNDEQUED_BUFFERS. Reset to a known/default state before we
+    // attempt such queries.
+
+    err = native_window_set_buffer_count(surface.window.get(), 0);
+    if (err != 0) {
+        ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
+    err = surface.window->setSwapInterval(surface.window.get(), 1);
+    if (err != 0) {
+        // TODO(jessehall): Improve error reporting. Can we enumerate possible
+        // errors and translate them to valid Vulkan result codes?
+        ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
     // -- Configure the native window --
 
-    Surface& surface = *SurfaceFromHandle(create_info->surface);
-    const DriverDispatchTable& dispatch = GetDriverDispatch(device);
+    const auto& dispatch = GetData(device).driver;
 
     int native_format = HAL_PIXEL_FORMAT_RGBA_8888;
     switch (create_info->imageFormat) {
@@ -536,8 +485,8 @@
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
-        ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
-              err);
+        ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
+              strerror(-err), err);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
@@ -562,14 +511,14 @@
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
-    err = surface.window->setSwapInterval(
-        surface.window.get(),
-        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1);
+    int swap_interval =
+        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
+    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
-        ALOGE("native_window->setSwapInterval failed: %s (%d)", strerror(-err),
-              err);
+        ALOGE("native_window->setSwapInterval(%d) failed: %s (%d)",
+              swap_interval, strerror(-err), err);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
@@ -623,14 +572,7 @@
             result = VK_ERROR_INITIALIZATION_FAILED;
             break;
         }
-        img.buffer = InitSharedPtr(device, buffer);
-        if (!img.buffer) {
-            ALOGE("swapchain creation failed: out of memory");
-            surface.window->cancelBuffer(surface.window.get(), buffer,
-                                         img.dequeue_fence);
-            result = VK_ERROR_OUT_OF_HOST_MEMORY;
-            break;
-        }
+        img.buffer = buffer;
         img.dequeued = true;
 
         image_create.extent =
@@ -681,12 +623,12 @@
 }
 
 VKAPI_ATTR
-void DestroySwapchainKHR_Bottom(VkDevice device,
-                                VkSwapchainKHR swapchain_handle,
-                                const VkAllocationCallbacks* allocator) {
-    const DriverDispatchTable& dispatch = GetDriverDispatch(device);
+void DestroySwapchainKHR(VkDevice device,
+                         VkSwapchainKHR swapchain_handle,
+                         const VkAllocationCallbacks* allocator) {
+    const auto& dispatch = GetData(device).driver;
     Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
-    const std::shared_ptr<ANativeWindow>& window = swapchain->surface.window;
+    const android::sp<ANativeWindow>& window = swapchain->surface.window;
 
     for (uint32_t i = 0; i < swapchain->num_images; i++) {
         Swapchain::Image& img = swapchain->images[i];
@@ -702,16 +644,16 @@
     }
 
     if (!allocator)
-        allocator = GetAllocator(device);
+        allocator = &GetData(device).allocator;
     swapchain->~Swapchain();
     allocator->pfnFree(allocator->pUserData, swapchain);
 }
 
 VKAPI_ATTR
-VkResult GetSwapchainImagesKHR_Bottom(VkDevice,
-                                      VkSwapchainKHR swapchain_handle,
-                                      uint32_t* count,
-                                      VkImage* images) {
+VkResult GetSwapchainImagesKHR(VkDevice,
+                               VkSwapchainKHR swapchain_handle,
+                               uint32_t* count,
+                               VkImage* images) {
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
     VkResult result = VK_SUCCESS;
     if (images) {
@@ -728,12 +670,12 @@
 }
 
 VKAPI_ATTR
-VkResult AcquireNextImageKHR_Bottom(VkDevice device,
-                                    VkSwapchainKHR swapchain_handle,
-                                    uint64_t timeout,
-                                    VkSemaphore semaphore,
-                                    VkFence vk_fence,
-                                    uint32_t* image_index) {
+VkResult AcquireNextImageKHR(VkDevice device,
+                             VkSwapchainKHR swapchain_handle,
+                             uint64_t timeout,
+                             VkSemaphore semaphore,
+                             VkFence vk_fence,
+                             uint32_t* image_index) {
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
     ANativeWindow* window = swapchain.surface.window.get();
     VkResult result;
@@ -777,7 +719,7 @@
         }
     }
 
-    result = GetDriverDispatch(device).AcquireImageANDROID(
+    result = GetData(device).driver.AcquireImageANDROID(
         device, swapchain.images[idx].image, fence_clone, semaphore, vk_fence);
     if (result != VK_SUCCESS) {
         // NOTE: we're relying on AcquireImageANDROID to close fence_clone,
@@ -798,14 +740,13 @@
 }
 
 VKAPI_ATTR
-VkResult QueuePresentKHR_Bottom(VkQueue queue,
-                                const VkPresentInfoKHR* present_info) {
+VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
     ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
              "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
              present_info->sType);
     ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
 
-    const DriverDispatchTable& dispatch = GetDriverDispatch(queue);
+    const auto& dispatch = GetData(queue).driver;
     VkResult final_result = VK_SUCCESS;
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
@@ -858,4 +799,5 @@
     return final_result;
 }
 
+}  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
new file mode 100644
index 0000000..2c60c49
--- /dev/null
+++ b/vulkan/libvulkan/swapchain.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 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 LIBVULKAN_SWAPCHAIN_H
+#define LIBVULKAN_SWAPCHAIN_H 1
+
+#include <vulkan/vulkan.h>
+
+namespace vulkan {
+namespace driver {
+
+// clang-format off
+VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
+VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* allocator);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice pdev, uint32_t queue_family, VkSurfaceKHR surface, VkBool32* pSupported);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* capabilities);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkSurfaceFormatKHR* formats);
+VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, VkSurfaceKHR surface, uint32_t* count, VkPresentModeKHR* modes);
+VKAPI_ATTR VkResult CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* create_info, const VkAllocationCallbacks* allocator, VkSwapchainKHR* swapchain_handle);
+VKAPI_ATTR void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain_handle, const VkAllocationCallbacks* allocator);
+VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint32_t* count, VkImage* images);
+VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain_handle, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* image_index);
+VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info);
+// clang-format on
+
+}  // namespace driver
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_SWAPCHAIN_H
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 6f57238..f29cb68 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <array>
 #include <inttypes.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include <log/log.h>
@@ -186,6 +187,58 @@
         AllocHandle(type, &device->next_handle[type]));
 }
 
+VKAPI_ATTR void* DefaultAllocate(void*,
+                                 size_t size,
+                                 size_t alignment,
+                                 VkSystemAllocationScope) {
+    void* ptr = nullptr;
+    // Vulkan requires 'alignment' to be a power of two, but posix_memalign
+    // additionally requires that it be at least sizeof(void*).
+    int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size);
+    return ret == 0 ? ptr : nullptr;
+}
+
+VKAPI_ATTR void* DefaultReallocate(void*,
+                                   void* ptr,
+                                   size_t size,
+                                   size_t alignment,
+                                   VkSystemAllocationScope) {
+    if (size == 0) {
+        free(ptr);
+        return nullptr;
+    }
+
+    // TODO(jessehall): Right now we never shrink allocations; if the new
+    // request is smaller than the existing chunk, we just continue using it.
+    // The null driver never reallocs, so this doesn't matter. If that changes,
+    // or if this code is copied into some other project, this should probably
+    // have a heuristic to allocate-copy-free when doing so will save "enough"
+    // space.
+    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
+    if (size <= old_size)
+        return ptr;
+
+    void* new_ptr = nullptr;
+    if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0)
+        return nullptr;
+    if (ptr) {
+        memcpy(new_ptr, ptr, std::min(old_size, size));
+        free(ptr);
+    }
+    return new_ptr;
+}
+
+VKAPI_ATTR void DefaultFree(void*, void* ptr) {
+    free(ptr);
+}
+
+const VkAllocationCallbacks kDefaultAllocCallbacks = {
+    .pUserData = nullptr,
+    .pfnAllocation = DefaultAllocate,
+    .pfnReallocation = DefaultReallocate,
+    .pfnFree = DefaultFree,
+};
+
 }  // namespace
 
 namespace null_driver {
@@ -239,10 +292,8 @@
 VkResult CreateInstance(const VkInstanceCreateInfo* create_info,
                         const VkAllocationCallbacks* allocator,
                         VkInstance* out_instance) {
-    // Assume the loader provided alloc callbacks even if the app didn't.
-    ALOG_ASSERT(
-        allocator,
-        "Missing alloc callbacks, loader or app should have provided them");
+    if (!allocator)
+        allocator = &kDefaultAllocCallbacks;
 
     VkInstance_T* instance =
         static_cast<VkInstance_T*>(allocator->pfnAllocation(
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index e97e5f5..62d8240 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -162,8 +162,11 @@
     uint32_t num_extensions = 0;
     for (const auto& desired_ext : kDesiredExtensions) {
         bool available = HasExtension(info.extensions, desired_ext);
-        for (size_t i = 0; !available && i < info.layer_extensions.size(); i++)
-            available = HasExtension(info.layer_extensions[i], desired_ext);
+        if (options.validate) {
+            for (size_t i = 0; !available && i < info.layer_extensions.size();
+                 i++)
+                available = HasExtension(info.layer_extensions[i], desired_ext);
+        }
         if (available)
             extensions[num_extensions++] = desired_ext;
     }
@@ -179,12 +182,11 @@
     // clang-format off
     const char *kValidationLayers[] = {
         "VK_LAYER_GOOGLE_threading",
+        "VK_LAYER_LUNARG_parameter_validation",
         "VK_LAYER_LUNARG_device_limits",
-        "VK_LAYER_LUNARG_draw_state",
-        "VK_LAYER_LUNARG_image",
-        "VK_LAYER_LUNARG_mem_tracker",
         "VK_LAYER_LUNARG_object_tracker",
-        "VK_LAYER_LUNARG_param_checker",
+        "VK_LAYER_LUNARG_image",
+        "VK_LAYER_LUNARG_core_validation",
         "VK_LAYER_LUNARG_swapchain",
         "VK_LAYER_GOOGLE_unique_objects"
     };
@@ -236,8 +238,12 @@
     uint32_t num_extensions = 0;
     for (const auto& desired_ext : kDesiredExtensions) {
         bool available = HasExtension(info->extensions, desired_ext);
-        for (size_t i = 0; !available && i < info->layer_extensions.size(); i++)
-            available = HasExtension(info->layer_extensions[i], desired_ext);
+        if (options.validate) {
+            for (size_t i = 0; !available && i < info->layer_extensions.size();
+                 i++)
+                available =
+                    HasExtension(info->layer_extensions[i], desired_ext);
+        }
         if (available)
             extensions[num_extensions++] = desired_ext;
     }
@@ -245,12 +251,11 @@
     // clang-format off
     const char *kValidationLayers[] = {
         "VK_LAYER_GOOGLE_threading",
+        "VK_LAYER_LUNARG_parameter_validation",
         "VK_LAYER_LUNARG_device_limits",
-        "VK_LAYER_LUNARG_draw_state",
-        "VK_LAYER_LUNARG_image",
-        "VK_LAYER_LUNARG_mem_tracker",
         "VK_LAYER_LUNARG_object_tracker",
-        "VK_LAYER_LUNARG_param_checker",
+        "VK_LAYER_LUNARG_image",
+        "VK_LAYER_LUNARG_core_validation",
         "VK_LAYER_LUNARG_swapchain",
         "VK_LAYER_GOOGLE_unique_objects"
     };