Merge "SF: Don't pop pending state into mCurrentState" 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..b6856bb
--- /dev/null
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+// TODO: code below was copy-and-pasted from bugreport.cpp (except by the timeout value);
+// should be reused instead.
+int main() {
+
+    // 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 1;
+    }
+
+    // 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 1;
+            }
+            bytes_to_send -= bytes_written;
+        } while (bytes_written != 0 && bytes_to_send > 0);
+    }
+
+    close(s);
+    return 0;
+
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index af95d16..ea14c66 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -63,6 +63,7 @@
 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"
 
@@ -929,15 +930,17 @@
 
 static void usage() {
   fprintf(stderr,
-          "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] "
-          "[-z]] [-s] [-q] [-B] [-P] [-R] [-V version]\n"
+          "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: generates zipped file (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 "
@@ -1017,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;
@@ -1025,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 */
@@ -1062,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;
@@ -1087,6 +1083,11 @@
         exit(1);
     }
 
+    if (use_control_socket && !do_zip_file) {
+        usage();
+        exit(1);
+    }
+
     if (do_update_progress && !do_broadcast) {
         usage();
         exit(1);
@@ -1112,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;
 
@@ -1351,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 */
@@ -1398,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 baba0f9..c51c79a 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -130,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 d9738bb..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",
@@ -909,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));
@@ -930,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);
 
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 878fb2d..5c2ad2d 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -104,11 +104,16 @@
         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 StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
+                return resolved;
             }
         }
-        LOG(WARNING) << "Failed to find inode " << ce_data_inode << " for package " << package_name;
+        LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback;
         closedir(dir);
         return fallback;
     } else {
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/include/gui/Surface.h b/include/gui/Surface.h
index 5d1d7bf..f76a9be 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -122,6 +122,13 @@
     // 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);
+
 protected:
     virtual ~Surface();
 
@@ -348,6 +355,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/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 33fe26c..b221e51 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;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 76b62f1..5efc333 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -480,6 +480,8 @@
         mSharedBufferHasBeenQueued = true;
     }
 
+    mQueueBufferCondition.broadcast();
+
     return err;
 }
 
@@ -1259,6 +1261,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 f031296..418892a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -291,6 +291,9 @@
     s->w = w;
     s->h = h;
 
+    // Resizing a surface makes the transaction synchronous.
+    mForceSynchronous = true;
+
     return NO_ERROR;
 }
 
@@ -451,6 +454,7 @@
     s.viewport = layerStackRect;
     s.frame = displayRect;
     s.what |= DisplayState::eDisplayProjectionChanged;
+    mForceSynchronous = true; // TODO: do we actually still need this?
 }
 
 void Composer::setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height) {
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..613b63b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -115,6 +115,7 @@
         "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 "
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -603,6 +604,7 @@
     EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
     if (result == EGL_TRUE) {
         _s.terminate();
+        dp->removeSurface(surface);
     }
     return result;
 }
@@ -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..d849693 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -89,6 +89,31 @@
     return false;
 }
 
+void egl_display_t::addContext(egl_context_t* context) {
+    Mutex::Autolock _l(lock);
+    contexts.add(context);
+}
+
+void egl_display_t::removeContext(egl_context_t* context) {
+    Mutex::Autolock _l(lock);
+    contexts.remove(context);
+}
+
+void egl_display_t::removeSurface(EGLSurface surface) const {
+    Mutex::Autolock _l(lock);
+    for (size_t i = 0; i < contexts.size(); i++) {
+        egl_context_t* context = contexts[i];
+        if (context->read == surface) {
+            SurfaceRef _r(get_surface(context->read));
+            _r.release();
+        }
+        if (context->draw == surface) {
+            SurfaceRef _d(get_surface(context->draw));
+            _d.release();
+        }
+    }
+}
+
 EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) {
     if (uintptr_t(disp) >= NUM_DISPLAYS)
         return NULL;
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 2d86295..0ede705 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -68,6 +68,13 @@
     // add reference to this object. returns true if this is a valid object.
     bool getObject(egl_object_t* object) const;
 
+    // add context to this display's list
+    void addContext(egl_context_t* context);
+    // remove context from this display's list
+    void removeContext(egl_context_t* context);
+    // search for surface on all contexts and remove the references
+    void removeSurface(EGLSurface surface) const;
+
     // These notifications allow the display to keep track of how many window
     // surfaces exist, which it uses to decide whether to hibernate the
     // underlying EGL implementation. They can be called by any thread without
@@ -135,6 +142,7 @@
     mutable Mutex                       lock, refLock;
     mutable Condition                   refCond;
             SortedVector<egl_object_t*> objects;
+            SortedVector<egl_context_t*> contexts;
             String8 mVendorString;
             String8 mVersionString;
             String8 mClientApiString;
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 918faa8..8859387 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -91,6 +91,12 @@
         egl_connection_t const* cnx, int version) :
     egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
             config(config), read(0), draw(0), cnx(cnx), version(version) {
+    get_display_nowake(dpy)->addContext(this);
+}
+
+void egl_context_t::terminate() {
+    display->removeContext(this);
+    egl_object_t::terminate();
 }
 
 void egl_context_t::onLooseCurrent() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 17a8304..8268900 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -40,11 +40,12 @@
 class egl_display_t;
 
 class egl_object_t {
-    egl_display_t *display;
     mutable volatile int32_t count;
 
 protected:
     virtual ~egl_object_t();
+    virtual void terminate();
+    egl_display_t *display;
 
 public:
     egl_object_t(egl_display_t* display);
@@ -55,7 +56,6 @@
     inline egl_display_t* getDisplay() const { return display; }
 
 private:
-    void terminate();
     static bool get(egl_display_t const* display, egl_object_t* object);
 
 public:
@@ -143,6 +143,7 @@
 class egl_context_t: public egl_object_t {
 protected:
     ~egl_context_t() {}
+    void terminate() override;
 public:
     typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 5c78c68..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" {
@@ -303,6 +304,12 @@
     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
@@ -378,8 +385,10 @@
             mSetLayerDisplayFrame)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
             mSetLayerPlaneAlpha)) return;
-    if (!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
-            mSetLayerSidebandStream)) return;
+    if (hasCapability(Capability::SidebandStream)) {
+        if (!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
+                mSetLayerSidebandStream)) return;
+    }
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop,
             mSetLayerSourceCrop)) return;
     if (!loadFunctionPointer(FunctionDescriptor::SetLayerTransform,
@@ -973,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 7d33a0a..967add0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -89,6 +89,8 @@
     // as connected
     std::shared_ptr<Display> getDisplayById(hwc2_display_t id);
 
+    bool hasCapability(HWC2::Capability capability) const;
+
 private:
     // Initialization methods
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index cd2e05f..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",
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/api.cpp b/vulkan/libvulkan/api.cpp
index 4e19af5..e7f10b3 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -431,6 +431,11 @@
                               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,
@@ -454,12 +459,13 @@
     PFN_vkGetDeviceProcAddr get_device_proc_addr_;
 
     union {
-        VkLayerInstanceCreateInfo instance_chain_info_;
-        VkLayerDeviceCreateInfo device_chain_info_;
+        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)
@@ -472,7 +478,9 @@
       get_instance_proc_addr_(nullptr),
       get_device_proc_addr_(nullptr),
       driver_extensions_(nullptr),
-      driver_extension_count_(0) {}
+      driver_extension_count_(0) {
+    enabled_extensions_.set(driver::ProcHook::EXTENSION_CORE);
+}
 
 LayerChain::~LayerChain() {
     allocator_.pfnFree(allocator_.pUserData, driver_extensions_);
@@ -616,18 +624,19 @@
 
 void LayerChain::ModifyCreateInfo(VkInstanceCreateInfo& info) {
     if (layer_count_) {
-        const ActiveLayer& layer = layers_[0];
+        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;
 
-        instance_chain_info_.sType =
-            VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO;
-        instance_chain_info_.function = VK_LAYER_FUNCTION_LINK;
-        // TODO fix vk_layer_interface.h and get rid of const_cast?
-        instance_chain_info_.u.pLayerInfo =
-            const_cast<VkLayerInstanceLink*>(&layer.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;
 
-        // insert layer info
-        instance_chain_info_.pNext = info.pNext;
-        info.pNext = &instance_chain_info_;
+        info.pNext = &cb_info;
     }
 
     if (override_layers_.Count()) {
@@ -643,17 +652,19 @@
 
 void LayerChain::ModifyCreateInfo(VkDeviceCreateInfo& info) {
     if (layer_count_) {
-        const ActiveLayer& layer = layers_[0];
+        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;
 
-        device_chain_info_.sType = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO;
-        device_chain_info_.function = VK_LAYER_FUNCTION_LINK;
-        // TODO fix vk_layer_interface.h and get rid of const_cast?
-        device_chain_info_.u.pLayerInfo =
-            const_cast<VkLayerDeviceLink*>(&layer.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;
 
-        // insert layer info
-        device_chain_info_.pNext = info.pNext;
-        info.pNext = &device_chain_info_;
+        info.pNext = &cb_info;
     }
 
     if (override_layers_.Count()) {
@@ -686,11 +697,11 @@
 
     // initialize InstanceData
     InstanceData& data = GetData(instance);
-    memset(&data, 0, sizeof(data));
 
     data.instance = instance;
 
-    if (!InitDispatchTable(instance, get_instance_proc_addr_)) {
+    if (!InitDispatchTable(instance, get_instance_proc_addr_,
+                           enabled_extensions_)) {
         if (data.dispatch.DestroyInstance)
             data.dispatch.DestroyInstance(instance, allocator);
 
@@ -766,9 +777,8 @@
 
     // initialize DeviceData
     DeviceData& data = GetData(dev);
-    memset(&data, 0, sizeof(data));
 
-    if (!InitDispatchTable(dev, get_device_proc_addr_)) {
+    if (!InitDispatchTable(dev, get_device_proc_addr_, enabled_extensions_)) {
         if (data.dispatch.DestroyDevice)
             data.dispatch.DestroyDevice(dev, allocator);
 
@@ -808,6 +818,10 @@
             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;
@@ -841,6 +855,10 @@
             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;
@@ -890,6 +908,24 @@
     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,
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 0c4cd9a..fe4136f 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -37,14 +37,81 @@
         }                                                              \
     } while (0)
 
-// TODO do we want to point to a stub or nullptr when ext is not enabled?
-#define INIT_PROC_EXT(ext, obj, proc) \
-    do {                              \
-        INIT_PROC(obj, proc);         \
+// 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)
 
-bool InitDispatchTable(VkInstance instance,
-                       PFN_vkGetInstanceProcAddr get_proc) {
+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;
 
@@ -73,7 +140,10 @@
     return success;
 }
 
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
     auto& data = GetData(dev);
     bool success = true;
 
@@ -272,11 +342,14 @@
         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);
+        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);
 }
 
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 54be83b..779b654 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -19,7 +19,9 @@
 #ifndef LIBVULKAN_API_GEN_H
 #define LIBVULKAN_API_GEN_H
 
+#include <bitset>
 #include <vulkan/vulkan.h>
+#include "driver_gen.h"
 
 namespace vulkan {
 namespace api {
@@ -179,8 +181,14 @@
     // clang-format on
 };
 
-bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+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
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index afe0d84..3968371 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -37,7 +37,9 @@
 #ifndef LIBVULKAN_API_GEN_H
 #define LIBVULKAN_API_GEN_H

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

 namespace vulkan {«
 namespace api {«
@@ -62,8 +64,14 @@
   // clang-format on
 };

-bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+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
@@ -95,7 +103,21 @@

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

-bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc) {
+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;

@@ -110,7 +132,10 @@
     return success;
 }

-bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
     auto& data = GetData(dev);
     bool success = true;

@@ -163,6 +188,7 @@
 #ifndef LIBVULKAN_DRIVER_GEN_H
 #define LIBVULKAN_DRIVER_GEN_H

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

@@ -194,8 +220,10 @@
 const ProcHook* GetProcHook(const char* name);
 ProcHook::Extension GetProcHookExtension(const char* name);

-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+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
@@ -228,7 +256,7 @@
 // clang-format off

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

@@ -273,7 +301,8 @@

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

-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc)
+bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
 {
     auto& data = GetData(instance);
     bool success = true;
@@ -289,7 +318,8 @@
     return success;
 }

-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc)
+bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
 {
     auto& data = GetData(dev);
     bool success = true;
@@ -428,14 +458,42 @@
 -------------------------------------------------------------------------------
 */}}
 {{define "api.C++.DefineInitProcExtMacro"}}
-  // TODO do we want to point to a stub or nullptr when ext is not enabled?
+  // 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 {                    \
-      INIT_PROC(obj, proc);                                     \
+      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.
 ------------------------------------------------------------------------------
@@ -529,11 +587,25 @@
       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);
+    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}}
 
 
@@ -628,13 +700,13 @@
 
 {{/*
 ------------------------------------------------------------------------------
-  Emits true if a function needs ProcHook stubs.
+  Emits true if a function needs a ProcHook stub.
 ------------------------------------------------------------------------------
 */}}
-{{define "driver.NeedProcHookStubs"}}
+{{define "driver.NeedProcHookStub"}}
   {{AssertType $ "Function"}}
 
-  {{if (Macro "driver.IsIntercepted" $)}}
+  {{if and (Macro "driver.IsIntercepted" $) (Macro "IsDeviceDispatched" $)}}
     {{$ext := GetAnnotation $ "extension"}}
     {{if $ext}}
       {{if not (Macro "IsExtensionInternal" $ext)}}true{{end}}
@@ -672,8 +744,7 @@
       Extension extension;

       PFN_vkVoidFunction proc;
-      PFN_vkVoidFunction disabled_proc; // nullptr for global hooks
-      PFN_vkVoidFunction checked_proc;  // nullptr for global/instance hooks
+      PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
   };
 {{end}}
 
@@ -685,7 +756,7 @@
 */}}
 {{define "driver.C++.DefineInitProcExtMacro"}}
   #define INIT_PROC_EXT(ext, obj, proc) do {                    \
-      if (data.hal_extensions[ProcHook::ext])           \
+      if (extensions[ProcHook::ext])                            \
         INIT_PROC(obj, proc);                                   \
   } while(0)
 {{end}}
@@ -693,35 +764,31 @@
 
 {{/*
 -------------------------------------------------------------------------------
-  Emits definitions of stub functions for ProcHook.
+  Emits a stub for ProcHook::checked_proc.
 -------------------------------------------------------------------------------
 */}}
-{{define "driver.C++.DefineProcHookStubs"}}
+{{define "driver.C++.DefineProcHookStub"}}
   {{AssertType $ "Function"}}
 
-  {{if (Macro "driver.NeedProcHookStubs" $)}}
+  {{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}} disabled{{$base}}({{$unnamed_params}}) {
-      ALOGE("{{$ext_name}} not enabled. {{$.Name}} not executed.");
-      {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
-    }
-    {{if (Macro "IsDeviceDispatched" $)}}
-      ¶
-      VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
-        {{if not (IsVoid $.Return.Type)}}return §{{end}}
+    VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
+      {{$p0 := index $.CallParameters 0}}
+      {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}}
 
-        {{$p0 := index $.CallParameters 0}}
-        {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}}
-        (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) ? §
-          {{$base}}({{Macro "Arguments" $}}) : §
-          disabled{{$base}}({{Macro "Arguments" $}});
+      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}}
 {{end}}
@@ -748,7 +815,6 @@
     ProcHook::EXTENSION_CORE,
     reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
     nullptr,
-    nullptr,
   },
 {{end}}
 
@@ -774,17 +840,14 @@
       {{if (Macro "IsExtensionInternal" $ext)}}
         nullptr,
         nullptr,
-        nullptr,
       {{else}}
         reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-        reinterpret_cast<PFN_vkVoidFunction>(disabled{{$base}}),
         nullptr,
       {{end}}
     {{else}}
       ProcHook::EXTENSION_CORE,
       reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
       nullptr,
-      nullptr,
     {{end}}
   },
 {{end}}
@@ -811,17 +874,14 @@
       {{if (Macro "IsExtensionInternal" $ext)}}
         nullptr,
         nullptr,
-        nullptr,
       {{else}}
         reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-        reinterpret_cast<PFN_vkVoidFunction>(disabled{{$base}}),
         reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}),
       {{end}}
     {{else}}
       ProcHook::EXTENSION_CORE,
       reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
       nullptr,
-      nullptr,
     {{end}}
   },
 {{end}}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 72b0981..17ccc72 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -331,11 +331,13 @@
         if (strcmp(name, props.extensionName) != 0)
             continue;
 
-        if (ext_bit == ProcHook::ANDROID_native_buffer)
-            hook_extensions_.set(ProcHook::KHR_swapchain);
-
         filter.names[filter.name_count++] = name;
-        hal_extensions_.set(ext_bit);
+        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;
     }
@@ -497,7 +499,7 @@
         case ProcHook::INSTANCE:
             proc = (GetData(instance).hook_extensions[hook->extension])
                        ? hook->proc
-                       : hook->disabled_proc;
+                       : nullptr;
             break;
         case ProcHook::DEVICE:
             proc = (hook->extension == ProcHook::EXTENSION_CORE)
@@ -526,9 +528,8 @@
         return nullptr;
     }
 
-    return (GetData(device).hook_extensions[hook->extension])
-               ? hook->proc
-               : hook->disabled_proc;
+    return (GetData(device).hook_extensions[hook->extension]) ? hook->proc
+                                                              : nullptr;
 }
 
 VkResult EnumerateInstanceExtensionProperties(
@@ -614,7 +615,6 @@
         return VK_ERROR_OUT_OF_HOST_MEMORY;
 
     data->hook_extensions |= wrapper.GetHookExtensions();
-    data->hal_extensions |= wrapper.GetHalExtensions();
 
     // call into the driver
     VkInstance instance;
@@ -628,7 +628,8 @@
 
     // initialize InstanceDriverTable
     if (!SetData(instance, *data) ||
-        !InitDriverTable(instance, g_hwdevice->GetInstanceProcAddr)) {
+        !InitDriverTable(instance, g_hwdevice->GetInstanceProcAddr,
+                         wrapper.GetHalExtensions())) {
         data->driver.DestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
             g_hwdevice->GetInstanceProcAddr(instance, "vkDestroyInstance"));
         if (data->driver.DestroyInstance)
@@ -685,7 +686,6 @@
         return VK_ERROR_OUT_OF_HOST_MEMORY;
 
     data->hook_extensions |= wrapper.GetHookExtensions();
-    data->hal_extensions |= wrapper.GetHalExtensions();
 
     // call into the driver
     VkDevice dev;
@@ -699,7 +699,8 @@
 
     // initialize DeviceDriverTable
     if (!SetData(dev, *data) ||
-        !InitDriverTable(dev, instance_data.get_device_proc_addr)) {
+        !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)
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 22db93f..85b36b6 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -64,6 +64,9 @@
 
 namespace driver {
 
+VK_DEFINE_HANDLE(InstanceDispatchable)
+VK_DEFINE_HANDLE(DeviceDispatchable)
+
 struct InstanceData {
     InstanceData(const VkAllocationCallbacks& alloc)
         : opaque_api_data(),
@@ -71,7 +74,6 @@
           driver(),
           get_device_proc_addr(nullptr) {
         hook_extensions.set(ProcHook::EXTENSION_CORE);
-        hal_extensions.set(ProcHook::EXTENSION_CORE);
     }
 
     api::InstanceData opaque_api_data;
@@ -79,7 +81,6 @@
     const VkAllocationCallbacks allocator;
 
     std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
-    std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions;
 
     InstanceDriverTable driver;
     PFN_vkGetDeviceProcAddr get_device_proc_addr;
@@ -91,7 +92,6 @@
     DeviceData(const VkAllocationCallbacks& alloc)
         : opaque_api_data(), allocator(alloc), driver() {
         hook_extensions.set(ProcHook::EXTENSION_CORE);
-        hal_extensions.set(ProcHook::EXTENSION_CORE);
     }
 
     api::DeviceData opaque_api_data;
@@ -99,7 +99,6 @@
     const VkAllocationCallbacks allocator;
 
     std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
-    std::bitset<ProcHook::EXTENSION_COUNT> hal_extensions;
 
     DeviceDriverTable driver;
 };
@@ -127,12 +126,15 @@
 
 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, VkQueue>::value ||
-                      std::is_same<DispatchableType, VkCommandBuffer>::value,
-                  "unrecognized dispatchable type");
+    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>
@@ -170,6 +172,11 @@
     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);
 }
@@ -182,6 +189,10 @@
     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));
 }
@@ -190,6 +201,10 @@
     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));
 }
@@ -202,6 +217,10 @@
     return *reinterpret_cast<DeviceData*>(GetDataInternal(cmd));
 }
 
+inline DeviceData& GetData(DeviceDispatchable dispatchable) {
+    return *reinterpret_cast<DeviceData*>(GetDataInternal(dispatchable));
+}
+
 }  // namespace driver
 }  // namespace vulkan
 
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 8b816ba..5bd2159 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -29,90 +29,48 @@
 
 // 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 VkResult checkedCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
-    return (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain) : disabledCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
-}
-
-VKAPI_ATTR void disabledDestroySwapchainKHR(VkDevice, VkSwapchainKHR, const VkAllocationCallbacks*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkDestroySwapchainKHR not executed.");
+    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) {
-    (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? DestroySwapchainKHR(device, swapchain, pAllocator) : disabledDestroySwapchainKHR(device, swapchain, pAllocator);
-}
-
-VKAPI_ATTR VkResult disabledGetSwapchainImagesKHR(VkDevice, VkSwapchainKHR, uint32_t*, VkImage*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkGetSwapchainImagesKHR not executed.");
-    return VK_SUCCESS;
+    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) {
-    return (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages) : disabledGetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages);
-}
-
-VKAPI_ATTR VkResult disabledAcquireNextImageKHR(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkAcquireNextImageKHR not executed.");
-    return VK_SUCCESS;
+    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) {
-    return (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) ? AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex) : disabledAcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
-}
-
-VKAPI_ATTR VkResult disabledQueuePresentKHR(VkQueue, const VkPresentInfoKHR*) {
-    ALOGE("VK_KHR_swapchain not enabled. vkQueuePresentKHR not executed.");
-    return VK_SUCCESS;
+    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) {
-    return (GetData(queue).hook_extensions[ProcHook::KHR_swapchain]) ? QueuePresentKHR(queue, pPresentInfo) : disabledQueuePresentKHR(queue, pPresentInfo);
-}
-
-VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
-    ALOGE("VK_KHR_android_surface not enabled. vkCreateAndroidSurfaceKHR not executed.");
-    return VK_SUCCESS;
-}
-
-VKAPI_ATTR VkResult disabledCreateDebugReportCallbackEXT(VkInstance, const VkDebugReportCallbackCreateInfoEXT*, const VkAllocationCallbacks*, VkDebugReportCallbackEXT*) {
-    ALOGE("VK_EXT_debug_report not enabled. vkCreateDebugReportCallbackEXT not executed.");
-    return VK_SUCCESS;
-}
-
-VKAPI_ATTR void disabledDestroyDebugReportCallbackEXT(VkInstance, VkDebugReportCallbackEXT, const VkAllocationCallbacks*) {
-    ALOGE("VK_EXT_debug_report not enabled. vkDestroyDebugReportCallbackEXT not executed.");
-}
-
-VKAPI_ATTR void disabledDebugReportMessageEXT(VkInstance, VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, const char*) {
-    ALOGE("VK_EXT_debug_report not enabled. vkDebugReportMessageEXT not executed.");
+    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
@@ -125,14 +83,12 @@
         ProcHook::ANDROID_native_buffer,
         nullptr,
         nullptr,
-        nullptr,
     },
     {
         "vkAcquireNextImageKHR",
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
         reinterpret_cast<PFN_vkVoidFunction>(AcquireNextImageKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledAcquireNextImageKHR),
         reinterpret_cast<PFN_vkVoidFunction>(checkedAcquireNextImageKHR),
     },
     {
@@ -141,14 +97,12 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(AllocateCommandBuffers),
         nullptr,
-        nullptr,
     },
     {
         "vkCreateAndroidSurfaceKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_android_surface,
         reinterpret_cast<PFN_vkVoidFunction>(CreateAndroidSurfaceKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledCreateAndroidSurfaceKHR),
         nullptr,
     },
     {
@@ -156,7 +110,6 @@
         ProcHook::INSTANCE,
         ProcHook::EXT_debug_report,
         reinterpret_cast<PFN_vkVoidFunction>(CreateDebugReportCallbackEXT),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledCreateDebugReportCallbackEXT),
         nullptr,
     },
     {
@@ -165,7 +118,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(CreateDevice),
         nullptr,
-        nullptr,
     },
     {
         "vkCreateInstance",
@@ -173,14 +125,12 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(CreateInstance),
         nullptr,
-        nullptr,
     },
     {
         "vkCreateSwapchainKHR",
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
         reinterpret_cast<PFN_vkVoidFunction>(CreateSwapchainKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledCreateSwapchainKHR),
         reinterpret_cast<PFN_vkVoidFunction>(checkedCreateSwapchainKHR),
     },
     {
@@ -188,7 +138,6 @@
         ProcHook::INSTANCE,
         ProcHook::EXT_debug_report,
         reinterpret_cast<PFN_vkVoidFunction>(DebugReportMessageEXT),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledDebugReportMessageEXT),
         nullptr,
     },
     {
@@ -196,7 +145,6 @@
         ProcHook::INSTANCE,
         ProcHook::EXT_debug_report,
         reinterpret_cast<PFN_vkVoidFunction>(DestroyDebugReportCallbackEXT),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledDestroyDebugReportCallbackEXT),
         nullptr,
     },
     {
@@ -205,7 +153,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(DestroyDevice),
         nullptr,
-        nullptr,
     },
     {
         "vkDestroyInstance",
@@ -213,14 +160,12 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance),
         nullptr,
-        nullptr,
     },
     {
         "vkDestroySurfaceKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
         reinterpret_cast<PFN_vkVoidFunction>(DestroySurfaceKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledDestroySurfaceKHR),
         nullptr,
     },
     {
@@ -228,7 +173,6 @@
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
         reinterpret_cast<PFN_vkVoidFunction>(DestroySwapchainKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledDestroySwapchainKHR),
         reinterpret_cast<PFN_vkVoidFunction>(checkedDestroySwapchainKHR),
     },
     {
@@ -237,7 +181,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(EnumerateDeviceExtensionProperties),
         nullptr,
-        nullptr,
     },
     {
         "vkEnumerateInstanceExtensionProperties",
@@ -245,7 +188,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties),
         nullptr,
-        nullptr,
     },
     {
         "vkEnumeratePhysicalDevices",
@@ -253,7 +195,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices),
         nullptr,
-        nullptr,
     },
     {
         "vkGetDeviceProcAddr",
@@ -261,7 +202,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr),
         nullptr,
-        nullptr,
     },
     {
         "vkGetDeviceQueue",
@@ -269,7 +209,6 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue),
         nullptr,
-        nullptr,
     },
     {
         "vkGetInstanceProcAddr",
@@ -277,14 +216,12 @@
         ProcHook::EXTENSION_CORE,
         reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr),
         nullptr,
-        nullptr,
     },
     {
         "vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
         reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceCapabilitiesKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfaceCapabilitiesKHR),
         nullptr,
     },
     {
@@ -292,7 +229,6 @@
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
         reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceFormatsKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfaceFormatsKHR),
         nullptr,
     },
     {
@@ -300,7 +236,6 @@
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
         reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfacePresentModesKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfacePresentModesKHR),
         nullptr,
     },
     {
@@ -308,7 +243,6 @@
         ProcHook::INSTANCE,
         ProcHook::KHR_surface,
         reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSurfaceSupportKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledGetPhysicalDeviceSurfaceSupportKHR),
         nullptr,
     },
     {
@@ -317,14 +251,12 @@
         ProcHook::ANDROID_native_buffer,
         nullptr,
         nullptr,
-        nullptr,
     },
     {
         "vkGetSwapchainImagesKHR",
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
         reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledGetSwapchainImagesKHR),
         reinterpret_cast<PFN_vkVoidFunction>(checkedGetSwapchainImagesKHR),
     },
     {
@@ -332,7 +264,6 @@
         ProcHook::DEVICE,
         ProcHook::KHR_swapchain,
         reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR),
-        reinterpret_cast<PFN_vkVoidFunction>(disabledQueuePresentKHR),
         reinterpret_cast<PFN_vkVoidFunction>(checkedQueuePresentKHR),
     },
     {
@@ -341,7 +272,6 @@
         ProcHook::ANDROID_native_buffer,
         nullptr,
         nullptr,
-        nullptr,
     },
     // clang-format on
 };
@@ -381,13 +311,15 @@
         }                                                              \
     } while (0)
 
-#define INIT_PROC_EXT(ext, obj, proc)           \
-    do {                                        \
-        if (data.hal_extensions[ProcHook::ext]) \
-            INIT_PROC(obj, proc);               \
+#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) {
+bool InitDriverTable(VkInstance instance,
+                     PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
     auto& data = GetData(instance);
     bool success = true;
 
@@ -406,7 +338,9 @@
     return success;
 }
 
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+bool InitDriverTable(VkDevice dev,
+                     PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
     auto& data = GetData(dev);
     bool success = true;
 
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 1eb7d79..ca17d57 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -19,6 +19,7 @@
 #ifndef LIBVULKAN_DRIVER_GEN_H
 #define LIBVULKAN_DRIVER_GEN_H
 
+#include <bitset>
 #include <vulkan/vulkan.h>
 #include <vulkan/vk_android_native_buffer.h>
 
@@ -48,8 +49,7 @@
     Extension extension;
 
     PFN_vkVoidFunction proc;
-    PFN_vkVoidFunction disabled_proc;  // nullptr for global hooks
-    PFN_vkVoidFunction checked_proc;   // nullptr for global/instance hooks
+    PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
 };
 
 struct InstanceDriverTable {
@@ -83,8 +83,12 @@
 const ProcHook* GetProcHook(const char* name);
 ProcHook::Extension GetProcHookExtension(const char* name);
 
-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);
+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