Merge changes I8cc3ce7d,Ie1a7bfd2,Id472ed4e into nyc-dev

* changes:
  libgui: Allow for pending releases in GLConsumer
  SF: HWC2 C++ shim
  SF: HWC2On1Adapter
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index a4b5ff1..a588ef4 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -18,7 +18,7 @@
 # ZipArchive support, the order matters here to get all symbols.
 LOCAL_STATIC_LIBRARIES := libziparchive libz libbase libmincrypt
 LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -std=gnu99
+LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
 LOCAL_INIT_RC := dumpstate.rc
 
 include $(BUILD_EXECUTABLE)
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 128681f..0471fc0 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -598,7 +598,6 @@
     dump_file("BUDDYINFO", "/proc/buddyinfo");
     dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
 
-    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
     dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
     dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
     dump_file("KERNEL SYNC", "/d/sync");
@@ -1352,6 +1351,7 @@
                  "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
                  "--ei", "android.intent.extra.ID", std::to_string(id),
                  "--ei", "android.intent.extra.PID", std::to_string(getpid()),
+                 "--ei", "android.intent.extra.MAX", std::to_string(weight_total),
                  "--es", "android.intent.extra.BUGREPORT", path,
                  "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
             };
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 9d42939..282a772 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -324,10 +324,10 @@
     // field 42 is iotime
     unsigned long long utime = 0, stime = 0, iotime = 0;
     if (sscanf(buffer,
-               "%*llu %*s %*s %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld "
-               "%*lld %*lld %llu %llu %*lld %*lld %*lld %*lld %*lld %*lld "
-               "%*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld "
-               "%*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %llu ",
+               "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d "
+               "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d "
+               "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+               "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ",
                &utime, &stime, &iotime) != 3) {
         return;
     }
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index 866203f..c47b0c8 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -206,8 +206,8 @@
 
 static const ShaderDesc shaders[] = {
     {
-        name: "Blit",
-        vertexShader: {
+        .name="Blit",
+        .vertexShader={
             "precision mediump float;",
             "",
             "attribute vec4 position;",
@@ -223,7 +223,7 @@
             "    texCoords = uvToTex * uv;",
             "}",
         },
-        fragmentShader: {
+        .fragmentShader={
             "#extension GL_OES_EGL_image_external : require",
             "precision mediump float;",
             "",
@@ -240,8 +240,8 @@
     },
 
     {
-        name: "Gradient",
-        vertexShader: {
+        .name="Gradient",
+        .vertexShader={
             "precision mediump float;",
             "",
             "attribute vec4 position;",
@@ -257,7 +257,7 @@
             "    interp = (uvToInterp * uv).x;",
             "}",
         },
-        fragmentShader: {
+        .fragmentShader={
             "precision mediump float;",
             "",
             "varying float interp;",
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index e54407c..e31c6e2 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -24,6 +24,7 @@
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/xattr.h>
 #include <unistd.h>
 
@@ -56,6 +57,17 @@
 
 #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
 
+typedef int fd_t;
+
+static bool property_get_bool(const char* property_name, bool default_value = false) {
+    char tmp_property_value[kPropertyValueMax];
+    bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
+    if (!have_property) {
+        return default_value;
+    }
+    return strcmp(tmp_property_value, "true") == 0;
+}
+
 int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
         appid_t appid, const char* seinfo, int target_sdk_version) {
     uid_t uid = multiuser_get_uid(userid, appid);
@@ -83,6 +95,24 @@
             // TODO: include result once 25796509 is fixed
             return 0;
         }
+
+        if (property_get_bool("dalvik.vm.usejitprofiles")) {
+            const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
+            // read-write-execute only for the app user.
+            if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
+                PLOG(ERROR) << "Failed to prepare " << profile_path;
+                return -1;
+            }
+            const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
+            // dex2oat/profman runs under the shared app gid and it needs to read/write reference
+            // profiles.
+            appid_t shared_app_gid = multiuser_get_shared_app_gid(uid);
+            if (fs_prepare_dir_strict(
+                    ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
+                PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
+                return -1;
+            }
+        }
     }
     return 0;
 }
@@ -125,6 +155,36 @@
     return 0;
 }
 
+// Keep profile paths in sync with ActivityThread.
+constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
+static std::string create_primary_profile(const std::string& profile_dir) {
+    return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
+}
+
+static void unlink_reference_profile(const char* pkgname) {
+    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
+    std::string reference_profile = create_primary_profile(reference_profile_dir);
+    if (unlink(reference_profile.c_str()) != 0) {
+        PLOG(WARNING) << "Could not unlink " << reference_profile;
+    }
+}
+
+static void unlink_current_profiles(const char* pkgname) {
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
+        std::string profile = create_primary_profile(profile_dir);
+        if (unlink(profile.c_str()) != 0) {
+            PLOG(WARNING) << "Could not unlink " << profile;
+        }
+    }
+}
+
+static void unlink_all_profiles(const char* pkgname) {
+    unlink_reference_profile(pkgname);
+    unlink_current_profiles(pkgname);
+}
+
 int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
     std::string suffix = "";
     if (flags & FLAG_CLEAR_CACHE_ONLY) {
@@ -146,6 +206,7 @@
             // TODO: include result once 25796509 is fixed
             delete_dir_contents(path);
         }
+        unlink_all_profiles(pkgname);
     }
     return res;
 }
@@ -160,6 +221,7 @@
         // TODO: include result once 25796509 is fixed
         delete_dir_contents_and_dir(
                 create_data_user_de_package_path(uuid, userid, pkgname));
+        unlink_all_profiles(pkgname);
     }
     return res;
 }
@@ -289,11 +351,13 @@
     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) {
@@ -630,19 +694,10 @@
     ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
 }
 
-static bool check_boolean_property(const char* property_name, bool default_value = false) {
-    char tmp_property_value[kPropertyValueMax];
-    bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
-    if (!have_property) {
-        return default_value;
-    }
-    return strcmp(tmp_property_value, "true") == 0;
-}
-
 static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_file_name,
-    const char* output_file_name, int swap_fd, const char *instruction_set,
-    bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only,
-    const std::vector<int>& profile_files_fd, const std::vector<int>& reference_profile_files_fd) {
+        const char* output_file_name, int swap_fd, const char *instruction_set,
+        bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only,
+        int profile_fd) {
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
     if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
@@ -651,12 +706,6 @@
         return;
     }
 
-    if (profile_files_fd.size() != reference_profile_files_fd.size()) {
-        ALOGE("Invalid configuration of profile files: pf_size (%zu) != rpf_size (%zu)",
-              profile_files_fd.size(), reference_profile_files_fd.size());
-        return;
-    }
-
     char dex2oat_Xms_flag[kPropertyValueMax];
     bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
 
@@ -705,7 +754,7 @@
                              (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
                              (strcmp(vold_decrypt, "1") == 0)));
 
-    bool generate_debug_info = check_boolean_property("debug.generate-debug-info");
+    bool generate_debug_info = property_get_bool("debug.generate-debug-info");
 
     char app_image_format[kPropertyValueMax];
     char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
@@ -779,18 +828,12 @@
                 (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
                 (prop_buf[0] == '1');
     }
-    std::vector<std::string> profile_file_args(profile_files_fd.size());
-    std::vector<std::string> reference_profile_file_args(profile_files_fd.size());
-    // "reference-profile-file-fd" is longer than "profile-file-fd" so we can
-    // use it to set the max length.
-    char profile_buf[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
-    for (size_t k = 0; k < profile_files_fd.size(); k++) {
-        sprintf(profile_buf, "--profile-file-fd=%d", profile_files_fd[k]);
-        profile_file_args[k].assign(profile_buf);
-        sprintf(profile_buf, "--reference-profile-file-fd=%d", reference_profile_files_fd[k]);
-        reference_profile_file_args[k].assign(profile_buf);
+    char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
+    if (profile_fd != -1) {
+        sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
     }
 
+
     ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
 
     const char* argv[7  // program name, mandatory arguments and the final NULL
@@ -807,8 +850,7 @@
                      + (debuggable ? 1 : 0)
                      + (have_app_image_format ? 1 : 0)
                      + dex2oat_flags_count
-                     + profile_files_fd.size()
-                     + reference_profile_files_fd.size()];
+                     + (profile_fd == -1 ? 0 : 1)];
     int i = 0;
     argv[i++] = DEX2OAT_BIN;
     argv[i++] = zip_fd_arg;
@@ -858,9 +900,8 @@
         argv[i++] = RUNTIME_ARG;
         argv[i++] = dex2oat_norelocation;
     }
-    for (size_t k = 0; k < profile_file_args.size(); k++) {
-        argv[i++] = profile_file_args[k].c_str();
-        argv[i++] = reference_profile_file_args[k].c_str();
+    if (profile_fd != -1) {
+        argv[i++] = profile_arg;
     }
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
@@ -906,7 +947,7 @@
         return true;
     }
 
-    bool is_low_mem = check_boolean_property("ro.config.low_ram");
+    bool is_low_mem = property_get_bool("ro.config.low_ram");
     if (is_low_mem) {
         return true;
     }
@@ -928,10 +969,7 @@
     }
 }
 
-constexpr const char* PROFILE_FILE_EXTENSION = ".prof";
-constexpr const char* REFERENCE_PROFILE_FILE_EXTENSION = ".prof.ref";
-
-static void close_all_fds(const std::vector<int>& fds, const char* description) {
+static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
     for (size_t i = 0; i < fds.size(); i++) {
         if (close(fds[i]) != 0) {
             PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
@@ -939,92 +977,224 @@
     }
 }
 
-static int open_code_cache_for_user(userid_t user, const char* volume_uuid, const char* pkgname) {
-    std::string code_cache_path =
-        create_data_user_package_path(volume_uuid, user, pkgname) + CODE_CACHE_DIR_POSTFIX;
-
+static fd_t open_profile_dir(const std::string& profile_dir) {
     struct stat buffer;
-    // Check that the code cache exists. If not, return and don't log an error.
-    if (TEMP_FAILURE_RETRY(lstat(code_cache_path.c_str(), &buffer)) == -1) {
+    if (TEMP_FAILURE_RETRY(lstat(profile_dir.c_str(), &buffer)) == -1) {
+        PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
+        return -1;
+    }
+
+    fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
+            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
+    if (profile_dir_fd < 0) {
+        PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir;
+    }
+    return profile_dir_fd;
+}
+
+static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
+    fd_t profile_dir_fd  = open_profile_dir(profile_dir);
+    if (profile_dir_fd < 0) {
+        return -1;
+    }
+
+    fd_t profile_fd = -1;
+    std::string profile_file = create_primary_profile(profile_dir);
+
+    profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW));
+    if (profile_fd == -1) {
+        // It's not an error if the profile file does not exist.
         if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to lstat code_cache: " << code_cache_path;
+            PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
+        }
+    }
+    // TODO(calin): use AutoCloseFD instead of closing the fd manually.
+    if (close(profile_dir_fd) != 0) {
+        PLOG(WARNING) << "Could not close profile dir " << profile_dir;
+    }
+    return profile_fd;
+}
+
+static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
+    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
+    return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
+}
+
+static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
+    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
+    int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
+    fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
+    if (fd < 0) {
+        return -1;
+    }
+    if (read_write) {
+        // Fix the owner.
+        if (fchown(fd, uid, uid) < 0) {
+            close(fd);
             return -1;
         }
     }
-
-    int code_cache_fd = open(code_cache_path.c_str(),
-            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
-    if (code_cache_fd < 0) {
-        PLOG(ERROR) << "Failed to open code_cache: " << code_cache_path;
-    }
-    return code_cache_fd;
+    return fd;
 }
 
-// Keep profile paths in sync with ActivityThread.
-static void open_profile_files_for_user(uid_t uid, const char* pkgname, int code_cache_fd,
-            /*out*/ int* profile_fd, /*out*/ int* reference_profile_fd) {
-    *profile_fd = -1;
-    *reference_profile_fd = -1;
-    std::string profile_file(pkgname);
-    profile_file += PROFILE_FILE_EXTENSION;
-
-    // Check if the profile exists. If not, early return and don't log an error.
-    struct stat buffer;
-    if (TEMP_FAILURE_RETRY(fstatat(
-            code_cache_fd, profile_file.c_str(), &buffer, AT_SYMLINK_NOFOLLOW)) == -1) {
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to fstatat profile file: " << profile_file;
-            return;
-        }
-    }
-
-    // Open in read-write to allow transfer of information from the current profile
-    // to the reference profile.
-    *profile_fd = openat(code_cache_fd, profile_file.c_str(), O_RDWR | O_NOFOLLOW);
-    if (*profile_fd < 0) {
-        PLOG(ERROR) << "Failed to open profile file: " << profile_file;
-        return;
-    }
-
-    std::string reference_profile(pkgname);
-    reference_profile += REFERENCE_PROFILE_FILE_EXTENSION;
-    // Give read-write permissions just for the user (changed with fchown after opening).
-    // We need write permission because dex2oat will update the reference profile files
-    // with the content of the corresponding current profile files.
-    *reference_profile_fd = openat(code_cache_fd, reference_profile.c_str(),
-            O_CREAT | O_RDWR | O_NOFOLLOW, S_IWUSR | S_IRUSR);
+static void open_profile_files(uid_t uid, const char* pkgname,
+            /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
+    // Open the reference profile in read-write mode as profman might need to save the merge.
+    *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
     if (*reference_profile_fd < 0) {
-        close(*profile_fd);
+        // We can't access the reference profile file.
         return;
     }
-    if (fchown(*reference_profile_fd, uid, uid) < 0) {
-        PLOG(ERROR) << "Cannot change reference profile file owner: " << reference_profile;
-        close(*profile_fd);
-        *profile_fd = -1;
-        *reference_profile_fd = -1;
+
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        fd_t profile_fd = open_primary_profile_file(user, pkgname);
+        // Add to the lists only if both fds are valid.
+        if (profile_fd >= 0) {
+            profiles_fd->push_back(profile_fd);
+        }
     }
 }
 
-static void open_profile_files(const char* volume_uuid, uid_t uid, const char* pkgname,
-            std::vector<int>* profile_fds, std::vector<int>* reference_profile_fds) {
-    std::vector<userid_t> users = get_known_users(volume_uuid);
-    for (auto user : users) {
-        int code_cache_fd  = open_code_cache_for_user(user, volume_uuid, pkgname);
-        if (code_cache_fd < 0) {
-            continue;
-        }
-        int profile_fd = -1;
-        int reference_profile_fd = -1;
-        open_profile_files_for_user(
-            uid, pkgname, code_cache_fd, &profile_fd, &reference_profile_fd);
-        close(code_cache_fd);
+static void drop_capabilities(uid_t uid) {
+    if (setgid(uid) != 0) {
+        ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
+        exit(64);
+    }
+    if (setuid(uid) != 0) {
+        ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
+        exit(65);
+    }
+    // drop capabilities
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    if (capset(&capheader, &capdata[0]) < 0) {
+        ALOGE("capset failed: %s\n", strerror(errno));
+        exit(66);
+    }
+}
 
-        // Add to the lists only if both fds are valid.
-        if ((profile_fd >= 0) && (reference_profile_fd >= 0)) {
-            profile_fds->push_back(profile_fd);
-            reference_profile_fds->push_back(reference_profile_fd);
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+
+static void run_profman(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
+    static const size_t MAX_INT_LEN = 32;
+    static const char* PROFMAN_BIN = "/system/bin/profman";
+
+    std::vector<std::string> profile_args(profiles_fd.size());
+    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
+    for (size_t k = 0; k < profiles_fd.size(); k++) {
+        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
+        profile_args[k].assign(profile_buf);
+    }
+    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
+    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
+
+    // program name, reference profile fd, the final NULL and the profile fds
+    const char* argv[3 + profiles_fd.size()];
+    int i = 0;
+    argv[i++] = PROFMAN_BIN;
+    argv[i++] = reference_profile_arg;
+    for (size_t k = 0; k < profile_args.size(); k++) {
+        argv[i++] = profile_args[k].c_str();
+    }
+    // Do not add after dex2oat_flags, they should override others for debugging.
+    argv[i] = NULL;
+
+    execv(PROFMAN_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+    exit(68);   /* only get here on exec failure */
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// Returns true if there is enough information in the current profiles that worth
+// a re-compilation of the package.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+static bool analyse_profiles(uid_t uid, const char* pkgname) {
+    std::vector<fd_t> profiles_fd;
+    fd_t reference_profile_fd = -1;
+    open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
+    if (profiles_fd.empty() || (reference_profile_fd == -1)) {
+        // Skip profile guided compilation because no profiles were found.
+        // Or if the reference profile info couldn't be opened.
+        close_all_fds(profiles_fd, "profiles_fd");
+        if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
+            PLOG(WARNING) << "Failed to close fd for reference profile";
+        }
+        return false;
+    }
+
+    ALOGV("PROFMAN: --- BEGIN '%s' ---\n", pkgname);
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+        run_profman(profiles_fd, reference_profile_fd);
+        exit(68);   /* only get here on exec failure */
+    }
+    /* parent */
+    int return_code = wait_child(pid);
+    bool need_to_compile = false;
+    bool delete_current_profiles = false;
+    bool delete_reference_profile = false;
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
+    } else {
+        return_code = WEXITSTATUS(return_code);
+        switch (return_code) {
+            case PROFMAN_BIN_RETURN_CODE_COMPILE:
+                need_to_compile = true;
+                delete_current_profiles = true;
+                delete_reference_profile = false;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
+                need_to_compile = false;
+                delete_current_profiles = false;
+                delete_reference_profile = false;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
+                LOG(WARNING) << "Bad profiles for package " << pkgname;
+                need_to_compile = false;
+                delete_current_profiles = true;
+                delete_reference_profile = true;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_ERROR_IO:  // fall-through
+            case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
+                // Temporary IO problem (e.g. locking). Ignore but log a warning.
+                LOG(WARNING) << "IO error while reading profiles for package " << pkgname;
+                need_to_compile = false;
+                delete_current_profiles = false;
+                delete_reference_profile = false;
+                break;
+           default:
+                // Unknown return code or error. Unlink profiles.
+                LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname
+                        << ": " << return_code;
+                need_to_compile = false;
+                delete_current_profiles = true;
+                delete_reference_profile = true;
+                break;
         }
     }
+    close_all_fds(profiles_fd, "profiles_fd");
+    if (close(reference_profile_fd) != 0) {
+        PLOG(WARNING) << "Failed to close fd for reference profile";
+    }
+    if (delete_current_profiles) {
+        unlink_current_profiles(pkgname);
+    }
+    if (delete_reference_profile) {
+        unlink_reference_profile(pkgname);
+    }
+    return need_to_compile;
 }
 
 static void trim_extension(char* path) {
@@ -1066,9 +1236,36 @@
     return true;
 }
 
+static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
+            const char* oat_dir, /*out*/ char* out_path) {
+    // Early best-effort check whether we can fit the the path into our buffers.
+    // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
+    // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
+    // extension to the cache path (5 bytes).
+    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
+        ALOGE("apk_path too long '%s'\n", apk_path);
+        return false;
+    }
+
+    if (oat_dir != NULL && oat_dir[0] != '!') {
+        if (validate_apk_path(oat_dir)) {
+            ALOGE("invalid oat_dir '%s'\n", oat_dir);
+            return false;
+        }
+        if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) {
+            return false;
+        }
+    } else {
+        if (!create_cache_path(out_path, apk_path, instruction_set)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
-           int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* volume_uuid,
-           bool use_profiles)
+           int dexopt_needed, const char* oat_dir, int dexopt_flags,
+           const char* volume_uuid ATTRIBUTE_UNUSED, bool use_profiles)
 {
     struct utimbuf ut;
     struct stat input_stat;
@@ -1077,19 +1274,25 @@
     char image_path[PKG_PATH_MAX];
     const char *input_file;
     char in_odex_path[PKG_PATH_MAX];
-    int res, input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1;
+    int res;
+    fd_t input_fd=-1, out_fd=-1, image_fd=-1, swap_fd=-1;
     bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0;
     bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
     bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
     bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
     bool extract_only = (dexopt_flags & DEXOPT_EXTRACTONLY) != 0;
-    std::vector<int> profile_files_fd;
-    std::vector<int> reference_profile_files_fd;
+    fd_t reference_profile_fd = -1;
     if (use_profiles) {
-        open_profile_files(volume_uuid, uid, pkgname,
-                &profile_files_fd, &reference_profile_files_fd);
-        if (profile_files_fd.empty()) {
-            // Skip profile guided compilation because no profiles were found.
+        if (analyse_profiles(uid, pkgname)) {
+            // Open again reference profile in read only mode as dex2oat does not get write
+            // permissions.
+            reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
+            if (reference_profile_fd == -1) {
+                PLOG(WARNING) << "Couldn't open reference profile in read only mode " << pkgname;
+                exit(72);
+            }
+        } else {
+            // No need to (re)compile. Return early.
             return 0;
         }
     }
@@ -1098,26 +1301,8 @@
         LOG_FATAL("dexopt flags contains unknown fields\n");
     }
 
-    // Early best-effort check whether we can fit the the path into our buffers.
-    // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
-    // without a swap file, if necessary.
-    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
-        ALOGE("apk_path too long '%s'\n", apk_path);
-        return -1;
-    }
-
-    if (oat_dir != NULL && oat_dir[0] != '!') {
-        if (validate_apk_path(oat_dir)) {
-            ALOGE("invalid oat_dir '%s'\n", oat_dir);
-            return -1;
-        }
-        if (!calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) {
-            return -1;
-        }
-    } else {
-        if (!create_cache_path(out_path, apk_path, instruction_set)) {
-            return -1;
-        }
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
+        return false;
     }
 
     switch (dexopt_needed) {
@@ -1207,24 +1392,8 @@
     pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
-        if (setgid(uid) != 0) {
-            ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
-            exit(64);
-        }
-        if (setuid(uid) != 0) {
-            ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
-            exit(65);
-        }
-        // drop capabilities
-        struct __user_cap_header_struct capheader;
-        struct __user_cap_data_struct capdata[2];
-        memset(&capheader, 0, sizeof(capheader));
-        memset(&capdata, 0, sizeof(capdata));
-        capheader.version = _LINUX_CAPABILITY_VERSION_3;
-        if (capset(&capheader, &capdata[0]) < 0) {
-            ALOGE("capset failed: %s\n", strerror(errno));
-            exit(66);
-        }
+        drop_capabilities(uid);
+
         SetDex2OatAndPatchOatScheduling(boot_complete);
         if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) {
             ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
@@ -1244,7 +1413,7 @@
             }
             run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd,
                         instruction_set, vm_safe_mode, debuggable, boot_complete, extract_only,
-                        profile_files_fd, reference_profile_files_fd);
+                        reference_profile_fd);
         } else {
             ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
             exit(73);
@@ -1269,9 +1438,8 @@
     if (swap_fd >= 0) {
         close(swap_fd);
     }
-    if (use_profiles != 0) {
-        close_all_fds(profile_files_fd, "profile_files_fd");
-        close_all_fds(reference_profile_files_fd, "reference_profile_files_fd");
+    if (reference_profile_fd >= 0) {
+        close(reference_profile_fd);
     }
     if (image_fd >= 0) {
         close(image_fd);
@@ -1286,9 +1454,11 @@
     if (input_fd >= 0) {
         close(input_fd);
     }
-    if (use_profiles != 0) {
-        close_all_fds(profile_files_fd, "profile_files_fd");
-        close_all_fds(reference_profile_files_fd, "reference_profile_files_fd");
+    if (reference_profile_fd >= 0) {
+        close(reference_profile_fd);
+        // We failed to compile. Unlink the reference profile. Current profiles are already unlinked
+        // when profmoan advises compilation.
+        unlink_reference_profile(pkgname);
     }
     if (swap_fd >= 0) {
         close(swap_fd);
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index bee2790..6a67e29 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -39,6 +39,7 @@
 dir_rec_t android_data_dir;
 dir_rec_t android_media_dir;
 dir_rec_t android_mnt_expand_dir;
+dir_rec_t android_profiles_dir;
 
 dir_rec_array_t android_system_dirs;
 
@@ -99,6 +100,11 @@
         return false;
     }
 
+    // Get the android profiles directory.
+    if (copy_and_append(&android_profiles_dir, &android_data_dir, PROFILES_SUBDIR) < 0) {
+        return false;
+    }
+
     // Take note of the system and vendor directories.
     android_system_dirs.count = 4;
 
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index 2e61f85..3e52346 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -43,6 +43,7 @@
 extern dir_rec_t android_data_dir;
 extern dir_rec_t android_media_dir;
 extern dir_rec_t android_mnt_expand_dir;
+extern dir_rec_t android_profiles_dir;
 
 extern dir_rec_array_t android_system_dirs;
 
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 4e1d38f..8f6e928 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -4,7 +4,7 @@
 **
 ** 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 
+** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
@@ -41,6 +41,8 @@
 
 constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA
 
+constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA
+
 /* other handy constants */
 
 constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 27f7939..dd1d9af 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -235,10 +235,48 @@
             }
         }
 
-        // Prepare and run dex2oat.
+        // Prepare to create.
         // TODO: Delete files, just for a blank slate.
         const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName);
 
+        std::string preopted_boot_art_path = StringPrintf("%s/system/framework/%s/boot.art",
+                                                          b_mount_path_.c_str(),
+                                                          isa);
+        if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
+          return PatchoatBootImage(art_path, isa);
+        } else {
+          // No preopted boot image. Try to compile.
+          return Dex2oatBootImage(boot_cp, art_path, oat_path, isa);
+        }
+    }
+
+    bool PatchoatBootImage(const std::string& art_path, const char* isa) {
+        // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
+
+        std::vector<std::string> cmd;
+        cmd.push_back(b_mount_path_ + "/system/bin/patchoat");
+
+        cmd.push_back("--input-image-location=/system/framework/boot.art");
+        cmd.push_back(StringPrintf("--output-image-file=%s", art_path.c_str()));
+
+        cmd.push_back(StringPrintf("--instruction-set=%s", isa));
+
+        int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
+                                                          ART_BASE_ADDRESS_MAX_DELTA);
+        cmd.push_back(StringPrintf("--base-offset-delta=0x%x", ART_BASE_ADDRESS + base_offset));
+
+        std::string error_msg;
+        bool result = Exec(cmd, &error_msg);
+        if (!result) {
+            LOG(ERROR) << "Could not generate boot image: " << error_msg;
+        }
+        return result;
+    }
+
+    bool Dex2oatBootImage(const std::string& boot_cp,
+                          const std::string& art_path,
+                          const std::string& oat_path,
+                          const char* isa) {
         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
         std::vector<std::string> cmd;
         cmd.push_back(b_mount_path_ + "/system/bin/dex2oat");
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index d25bf71..74f4264 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -59,6 +59,11 @@
     }
 }
 
+static void check_package_name(const char* package_name) {
+    CHECK(is_valid_filename(package_name));
+    CHECK(is_valid_package_name(package_name) == 0);
+}
+
 /**
  * Create the path name where package app contents should be stored for
  * the given volume UUID and package name.  An empty UUID is assumed to
@@ -66,9 +71,7 @@
  */
 std::string create_data_app_package_path(const char* volume_uuid,
         const char* package_name) {
-    CHECK(is_valid_filename(package_name));
-    CHECK(is_valid_package_name(package_name) == 0);
-
+    check_package_name(package_name);
     return StringPrintf("%s/%s",
             create_data_app_path(volume_uuid).c_str(), package_name);
 }
@@ -80,18 +83,14 @@
  */
 std::string create_data_user_package_path(const char* volume_uuid,
         userid_t user, const char* package_name) {
-    CHECK(is_valid_filename(package_name));
-    CHECK(is_valid_package_name(package_name) == 0);
-
+    check_package_name(package_name);
     return StringPrintf("%s/%s",
             create_data_user_path(volume_uuid, user).c_str(), package_name);
 }
 
 std::string create_data_user_de_package_path(const char* volume_uuid,
         userid_t user, const char* package_name) {
-    CHECK(is_valid_filename(package_name));
-    CHECK(is_valid_package_name(package_name) == 0);
-
+    check_package_name(package_name);
     return StringPrintf("%s/%s",
             create_data_user_de_path(volume_uuid, user).c_str(), package_name);
 }
@@ -161,6 +160,20 @@
     return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
 }
 
+std::string create_data_user_profiles_path(userid_t userid) {
+    return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
+}
+
+std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/%s",create_data_user_profiles_path(user).c_str(), package_name);
+}
+
+std::string create_data_ref_profile_package_path(const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
+}
+
 std::vector<userid_t> get_known_users(const char* volume_uuid) {
     std::vector<userid_t> users;
 
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 2d9573e..9bbddca 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -84,6 +84,10 @@
 
 std::string create_data_media_path(const char* volume_uuid, 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);
+
 std::vector<userid_t> get_known_users(const char* volume_uuid);
 
 int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
@@ -135,6 +139,7 @@
 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/Android.mk b/cmds/servicemanager/Android.mk
index 7ee0dd1..73c0367 100644
--- a/cmds/servicemanager/Android.mk
+++ b/cmds/servicemanager/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 
 svc_c_flags =	\
-	-Wall -Wextra \
+	-Wall -Wextra -Werror \
 
 ifneq ($(TARGET_USES_64_BIT_BINDER),true)
 ifneq ($(TARGET_IS_64_BIT),true)
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
index fd91633..6466654 100644
--- a/cmds/servicemanager/bctest.c
+++ b/cmds/servicemanager/bctest.c
@@ -58,7 +58,6 @@
 
 int main(int argc, char **argv)
 {
-    int fd;
     struct binder_state *bs;
     uint32_t svcmgr = BINDER_SERVICE_MANAGER;
     uint32_t handle;
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 3d14451..9e99085 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -449,7 +449,7 @@
 }
 
 void binder_done(struct binder_state *bs,
-                 struct binder_io *msg,
+                 __unused struct binder_io *msg,
                  struct binder_io *reply)
 {
     struct {
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 8596f96..21fdff0 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -178,7 +178,7 @@
 };
 
 
-uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid)
+uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
 {
     struct svcinfo *si = find_svc(s, len);
 
@@ -304,7 +304,7 @@
         if (s == NULL) {
             return -1;
         }
-        handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
+        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
         if (!handle)
             break;
         bio_put_ref(reply, handle);
@@ -349,7 +349,7 @@
 }
 
 
-static int audit_callback(void *data, security_class_t cls, char *buf, size_t len)
+static int audit_callback(void *data, __unused security_class_t cls, char *buf, size_t len)
 {
     struct audit_data *ad = (struct audit_data *)data;
 
@@ -362,7 +362,7 @@
     return 0;
 }
 
-int main(int argc, char **argv)
+int main()
 {
     struct binder_state *bs;
 
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
index 34ce0f0..69dc9a7 100644
--- a/include/binder/IProcessInfoService.h
+++ b/include/binder/IProcessInfoService.h
@@ -44,16 +44,6 @@
 
 // ----------------------------------------------------------------------
 
-class BnProcessInfoService : public BnInterface<IProcessInfoService> {
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
 }; // namespace android
 
 #endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 5956e13..44fd59e 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -126,6 +126,8 @@
 
     status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
     status_t            writeByteVector(const std::vector<int8_t>& val);
+    status_t            writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val);
+    status_t            writeByteVector(const std::vector<uint8_t>& val);
     status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
     status_t            writeInt32Vector(const std::vector<int32_t>& val);
     status_t            writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val);
@@ -271,6 +273,8 @@
 
     status_t            readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const;
     status_t            readByteVector(std::vector<int8_t>* val) const;
+    status_t            readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const;
+    status_t            readByteVector(std::vector<uint8_t>* val) const;
     status_t            readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const;
     status_t            readInt32Vector(std::vector<int32_t>* val) const;
     status_t            readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const;
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index a515f39..6f45181 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -119,8 +119,10 @@
     // previous frame
     Region mSurfaceDamage;
 
-    // Indicates that the BufferQueue is in single buffer mode
-    bool mSingleBufferMode;
+    // Indicates that the consumer should acquire the next frame as soon as it
+    // can and not wait for a frame to become available. This is only relevant
+    // in single buffer mode.
+    bool mAutoRefresh;
 
     // Indicates that this buffer was queued by the producer. When in single
     // buffer mode acquire() can return a BufferItem that wasn't in the queue.
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index e2e73a0..d10ba2f 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -292,6 +292,11 @@
     // consumer and producer to access the same buffer simultaneously.
     bool mSingleBufferMode;
 
+    // When single buffer mode is enabled, this indicates whether the consumer
+    // should acquire buffers even if BufferQueue doesn't indicate that they are
+    // available.
+    bool mAutoRefresh;
+
     // When single buffer mode is enabled, this tracks which slot contains the
     // shared buffer.
     int mSingleBufferSlot;
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index dc05e98..312f323 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -176,6 +176,9 @@
     // See IGraphicBufferProducer::setSingleBufferMode
     virtual status_t setSingleBufferMode(bool singleBufferMode) override;
 
+    // See IGraphicBufferProducer::setAutoRefresh
+    virtual status_t setAutoRefresh(bool autoRefresh) override;
+
     // See IGraphicBufferProducer::setDequeueTimeout
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
 
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 9307a26..1b63552 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -26,6 +26,8 @@
 #include <utils/threads.h>
 #include <gui/IConsumerListener.h>
 
+#include <queue>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -108,18 +110,18 @@
     // from the derived class.
     virtual void onLastStrongRef(const void* id);
 
-    // Implementation of the IConsumerListener interface.  These
-    // calls are used to notify the ConsumerBase of asynchronous events in the
-    // BufferQueue.  The onFrameAvailable, onFrameReplaced, and
-    // onBuffersReleased methods should not need to be overridden by derived
-    // classes, but if they are overridden the ConsumerBase implementation must
-    // be called from the derived class. The ConsumerBase version of
-    // onSidebandStreamChanged does nothing and can be overriden by derived
-    // classes if they want the notification.
-    virtual void onFrameAvailable(const BufferItem& item) override;
-    virtual void onFrameReplaced(const BufferItem& item) override;
-    virtual void onBuffersReleased() override;
-    virtual void onSidebandStreamChanged() override;
+    // Handlers for the IConsumerListener interface, these will be called from
+    // the message queue thread. These calls are used to notify the ConsumerBase
+    // of asynchronous events in the BufferQueue.  The onFrameAvailableHandler,
+    // onFrameReplacedHandler, and onBuffersReleasedHandler methods should not
+    // need to be overridden by derived classes, but if they are overridden the
+    // ConsumerBase implementation must be called from the derived class. The
+    // ConsumerBase version of onSidebandStreamChangedHandler does nothing and
+    // can be overriden by derived classes if they want the notification.
+    virtual void onFrameAvailableHandler(const BufferItem& item);
+    virtual void onFrameReplacedHandler(const BufferItem& item);
+    virtual void onBuffersReleasedHandler();
+    virtual void onSidebandStreamChangedHandler();
 
     // freeBufferLocked frees up the given buffer slot.  If the slot has been
     // initialized this will release the reference to the GraphicBuffer in that
@@ -244,6 +246,35 @@
     //
     // This mutex is intended to be locked by derived classes.
     mutable Mutex mMutex;
+
+    // Implements the ConsumerListener interface
+    virtual void onFrameAvailable(const BufferItem& item) override;
+    virtual void onFrameReplaced(const BufferItem& item) override;
+    virtual void onBuffersReleased() override;
+    virtual void onSidebandStreamChanged() override;
+
+    enum MessageType {
+        ON_FRAME_AVAILABLE,
+        ON_FRAME_REPLACED,
+        ON_BUFFERS_RELEASED,
+        ON_SIDEBAND_STREAM_CHANGED,
+        EXIT,
+    };
+
+    mutable Mutex mMessageQueueLock;
+    Condition mMessageAvailable;
+    std::queue<std::pair<MessageType, BufferItem>> mMessageQueue;
+
+    class MessageThread : public Thread {
+    public:
+        MessageThread(ConsumerBase* consumerBase) :
+            mConsumerBase(consumerBase) {};
+    protected:
+        virtual bool threadLoop() override;
+        ConsumerBase* mConsumerBase;
+    };
+
+    sp<MessageThread> mMessageThread;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 265728f..f6b4230 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -531,6 +531,14 @@
     // the producer and consumer to simultaneously access the same buffer.
     virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
 
+    // Used to enable/disable auto-refresh.
+    //
+    // Auto refresh has no effect outside of single buffer mode. In single
+    // buffer mode, when enabled, it indicates to the consumer that it should
+    // attempt to acquire buffers even if it is not aware of any being
+    // available.
+    virtual status_t setAutoRefresh(bool autoRefresh) = 0;
+
     // Sets how long dequeueBuffer will wait for a buffer to become available
     // before returning an error (TIMED_OUT).
     //
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index f79210b..3afdaae 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -168,6 +168,7 @@
     int dispatchSetBuffersDataSpace(va_list args);
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSingleBufferMode(va_list args);
+    int dispatchSetAutoRefresh(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -197,6 +198,7 @@
     virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual int setAsyncMode(bool async);
     virtual int setSingleBufferMode(bool singleBufferMode);
+    virtual int setAutoRefresh(bool autoRefresh);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
 
@@ -331,6 +333,19 @@
     // Stores the current generation number. See setGenerationNumber and
     // IGraphicBufferProducer::setGenerationNumber for more information.
     uint32_t mGenerationNumber;
+
+    // Caches the values that have been passed to the producer.
+    bool mSingleBufferMode;
+    bool mAutoRefresh;
+
+    // If in single buffer mode and auto refresh is enabled, store the shared
+    // buffer slot and return it for all calls to queue/dequeue without going
+    // over Binder.
+    int mSharedBufferSlot;
+
+    // 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;
 };
 
 }; // namespace android
diff --git a/include/media/hardware/HardwareAPI.h b/include/media/hardware/HardwareAPI.h
index 9ba5f7f..752b004 100644
--- a/include/media/hardware/HardwareAPI.h
+++ b/include/media/hardware/HardwareAPI.h
@@ -27,6 +27,11 @@
 
 namespace android {
 
+// This structure is used to enable Android native buffer use for either
+// graphic buffers or secure buffers.
+//
+// TO CONTROL ANDROID GRAPHIC BUFFER USAGE:
+//
 // A pointer to this struct is passed to the OMX_SetParameter when the extension
 // index for the 'OMX.google.android.index.enableAndroidNativeBuffers' extension
 // is given.
@@ -45,6 +50,25 @@
 // 'OMX.google.android.index.useAndroidNativeBuffer2' extension, it should
 // expect to receive UseAndroidNativeBuffer calls (via OMX_SetParameter) rather
 // than UseBuffer calls for that port.
+//
+// TO CONTROL ANDROID SECURE BUFFER USAGE:
+//
+// A pointer to this struct is passed to the OMX_SetParameter when the extension
+// index for the 'OMX.google.android.index.allocateNativeHandle' extension
+// is given.
+//
+// When native handle use is disabled for a port (the default state),
+// the OMX node should operate as normal, and expect AllocateBuffer calls to
+// return buffer pointers. This is the mode that will be used for non-secure
+// buffers if component requires allocate buffers instead of use buffers.
+//
+// When native handle use has been enabled for a given port, the component
+// shall allocate native_buffer_t objects containing  that can be passed between
+// processes using binder. This is the mode that will be used for secure buffers.
+// When an OMX component allocates native handle for buffers, it must close and
+// delete that handle when it frees those buffers. Even though pBuffer will point
+// to a native handle, nFilledLength, nAllocLength and nOffset will correspond
+// to the data inside the opaque buffer.
 struct EnableAndroidNativeBuffersParams {
     OMX_U32 nSize;
     OMX_VERSIONTYPE nVersion;
@@ -52,6 +76,8 @@
     OMX_BOOL enable;
 };
 
+typedef struct EnableAndroidNativeBuffersParams AllocateNativeHandleParams;
+
 // A pointer to this struct is passed to OMX_SetParameter() when the extension index
 // "OMX.google.android.index.storeMetaDataInBuffers" or
 // "OMX.google.android.index.storeANWBufferInMetadata" is given.
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index e9891a8..fb8d620 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -26,6 +26,7 @@
 #include <sys/mman.h>
 
 #include <binder/IMemory.h>
+#include <cutils/log.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 #include <utils/Atomic.h>
@@ -187,15 +188,26 @@
             if (heap != 0) {
                 mHeap = interface_cast<IMemoryHeap>(heap);
                 if (mHeap != 0) {
-                    mOffset = o;
-                    mSize = s;
+                    size_t heapSize = mHeap->getSize();
+                    if (s <= heapSize
+                            && o >= 0
+                            && (static_cast<size_t>(o) <= heapSize - s)) {
+                        mOffset = o;
+                        mSize = s;
+                    } else {
+                        // Hm.
+                        android_errorWriteWithInfoLog(0x534e4554,
+                            "26877992", -1, NULL, 0);
+                        mOffset = 0;
+                        mSize = 0;
+                    }
                 }
             }
         }
     }
     if (offset) *offset = mOffset;
     if (size) *size = mSize;
-    return mHeap;
+    return (mSize > 0) ? mHeap : 0;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index c37920d..76508b8 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -88,72 +88,4 @@
 
 // ----------------------------------------------------------------------
 
-status_t BnProcessInfoService::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case GET_PROCESS_STATES_FROM_PIDS: {
-            CHECK_INTERFACE(IProcessInfoService, data, reply);
-            int32_t arrayLen = data.readInt32();
-            if (arrayLen <= 0) {
-                reply->writeNoException();
-                reply->writeInt32(0);
-                reply->writeInt32(NOT_ENOUGH_DATA);
-                return NO_ERROR;
-            }
-
-            size_t len = static_cast<size_t>(arrayLen);
-            int32_t pids[len];
-            status_t res = data.read(pids, len * sizeof(*pids));
-
-            // Ignore output array length returned in the parcel here, as the states array must
-            // always be the same length as the input PIDs array.
-            int32_t states[len];
-            for (size_t i = 0; i < len; i++) states[i] = -1;
-            if (res == NO_ERROR) {
-                res = getProcessStatesFromPids(len, /*in*/ pids, /*out*/ states);
-            }
-            reply->writeNoException();
-            reply->writeInt32Array(len, states);
-            reply->writeInt32(res);
-            return NO_ERROR;
-        } break;
-        case GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS: {
-            CHECK_INTERFACE(IProcessInfoService, data, reply);
-            int32_t arrayLen = data.readInt32();
-            if (arrayLen <= 0) {
-                reply->writeNoException();
-                reply->writeInt32(0);
-                reply->writeInt32(NOT_ENOUGH_DATA);
-                return NO_ERROR;
-            }
-
-            size_t len = static_cast<size_t>(arrayLen);
-            int32_t pids[len];
-            status_t res = data.read(pids, len * sizeof(*pids));
-
-            // Ignore output array length returned in the parcel here, as the
-            // states array must always be the same length as the input PIDs array.
-            int32_t states[len];
-            int32_t scores[len];
-            for (size_t i = 0; i < len; i++) {
-                states[i] = -1;
-                scores[i] = -10000;
-            }
-            if (res == NO_ERROR) {
-                res = getProcessStatesAndOomScoresFromPids(
-                        len, /*in*/ pids, /*out*/ states, /*out*/ scores);
-            }
-            reply->writeNoException();
-            reply->writeInt32Array(len, states);
-            reply->writeInt32Array(len, scores);
-            reply->writeInt32(res);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------
-
 }; // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 1008f02..678d98b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -808,16 +808,10 @@
   return writeUtf8AsUtf16(*str);
 }
 
-status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
-{
-    if (!val) {
-        return writeInt32(-1);
-    }
+namespace {
 
-    return writeByteVector(*val);
-}
-
-status_t Parcel::writeByteVector(const std::vector<int8_t>& val)
+template<typename T>
+status_t writeByteVectorInternal(Parcel* parcel, const std::vector<T>& val)
 {
     status_t status;
     if (val.size() > std::numeric_limits<int32_t>::max()) {
@@ -825,12 +819,12 @@
         return status;
     }
 
-    status = writeInt32(val.size());
+    status = parcel->writeInt32(val.size());
     if (status != OK) {
         return status;
     }
 
-    void* data = writeInplace(val.size());
+    void* data = parcel->writeInplace(val.size());
     if (!data) {
         status = BAD_VALUE;
         return status;
@@ -840,6 +834,37 @@
     return status;
 }
 
+template<typename T>
+status_t writeByteVectorInternalPtr(Parcel* parcel,
+                                    const std::unique_ptr<std::vector<T>>& val)
+{
+    if (!val) {
+        return parcel->writeInt32(-1);
+    }
+
+    return writeByteVectorInternal(parcel, *val);
+}
+
+}  // namespace
+
+status_t Parcel::writeByteVector(const std::vector<int8_t>& val) {
+    return writeByteVectorInternal(this, val);
+}
+
+status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
+{
+    return writeByteVectorInternalPtr(this, val);
+}
+
+status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) {
+    return writeByteVectorInternal(this, val);
+}
+
+status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val)
+{
+    return writeByteVectorInternalPtr(this, val);
+}
+
 status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val)
 {
     return writeTypedVector(val, &Parcel::writeInt32);
@@ -1406,11 +1431,15 @@
     return err;
 }
 
-status_t Parcel::readByteVector(std::vector<int8_t>* val) const {
+namespace {
+
+template<typename T>
+status_t readByteVectorInternal(const Parcel* parcel,
+                                std::vector<T>* val) {
     val->clear();
 
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = parcel->readInt32(&size);
 
     if (status != OK) {
         return status;
@@ -1420,12 +1449,12 @@
         status = UNEXPECTED_NULL;
         return status;
     }
-    if (size_t(size) > dataAvail()) {
+    if (size_t(size) > parcel->dataAvail()) {
         status = BAD_VALUE;
         return status;
     }
 
-    const void* data = readInplace(size);
+    const void* data = parcel->readInplace(size);
     if (!data) {
         status = BAD_VALUE;
         return status;
@@ -1436,20 +1465,23 @@
     return status;
 }
 
-status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
-    const int32_t start = dataPosition();
+template<typename T>
+status_t readByteVectorInternalPtr(
+        const Parcel* parcel,
+        std::unique_ptr<std::vector<T>>* val) {
+    const int32_t start = parcel->dataPosition();
     int32_t size;
-    status_t status = readInt32(&size);
+    status_t status = parcel->readInt32(&size);
     val->reset();
 
     if (status != OK || size < 0) {
         return status;
     }
 
-    setDataPosition(start);
-    val->reset(new std::vector<int8_t>());
+    parcel->setDataPosition(start);
+    val->reset(new std::vector<T>());
 
-    status = readByteVector(val->get());
+    status = readByteVectorInternal(parcel, val->get());
 
     if (status != OK) {
         val->reset();
@@ -1458,6 +1490,24 @@
     return status;
 }
 
+}  // namespace
+
+status_t Parcel::readByteVector(std::vector<int8_t>* val) const {
+    return readByteVectorInternal(this, val);
+}
+
+status_t Parcel::readByteVector(std::vector<uint8_t>* val) const {
+    return readByteVectorInternal(this, val);
+}
+
+status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
+    return readByteVectorInternalPtr(this, val);
+}
+
+status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const {
+    return readByteVectorInternalPtr(this, val);
+}
+
 status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const {
     return readNullableTypedVector(val, &Parcel::readInt32);
 }
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 036ef1e..5e3924a 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -38,7 +38,7 @@
     mAcquireCalled(false),
     mTransformToDisplayInverse(false),
     mSurfaceDamage(),
-    mSingleBufferMode(false),
+    mAutoRefresh(false),
     mQueuedBuffer(true),
     mIsStale(false) {
 }
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 92285e5..4029496 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -32,6 +32,10 @@
 #include <gui/IConsumerListener.h>
 #include <gui/IProducerListener.h>
 
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
+
 namespace android {
 
 BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
@@ -67,7 +71,7 @@
         }
 
         bool sharedBufferAvailable = mCore->mSingleBufferMode &&
-                mCore->mSingleBufferSlot !=
+                mCore->mAutoRefresh && mCore->mSingleBufferSlot !=
                 BufferQueueCore::INVALID_BUFFER_SLOT;
 
         // In asynchronous mode the list is guaranteed to be one buffer deep,
@@ -214,16 +218,15 @@
                     (mCore->mSingleBufferCache.transform &
                     NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
             outBuffer->mSurfaceDamage = Region::INVALID_REGION;
-            outBuffer->mSingleBufferMode = true;
             outBuffer->mQueuedBuffer = false;
             outBuffer->mIsStale = false;
+            outBuffer->mAutoRefresh = mCore->mSingleBufferMode &&
+                    mCore->mAutoRefresh;
         } else {
             slot = front->mSlot;
             *outBuffer = *front;
         }
 
-        outBuffer->mSingleBufferMode = mCore->mSingleBufferMode;
-
         ATRACE_BUFFER_INDEX(slot);
 
         BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
@@ -713,7 +716,18 @@
 }
 
 void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
-    mCore->dump(result, prefix);
+    const IPCThreadState* ipc = IPCThreadState::self();
+    const pid_t pid = ipc->getCallingPid();
+    const uid_t uid = ipc->getCallingUid();
+    if ((uid != AID_SHELL)
+            && !PermissionCache::checkPermission(String16(
+            "android.permission.DUMP"), pid, uid)) {
+        result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
+                "from pid=%d, uid=%d\n", pid, uid);
+        android_errorWriteWithInfoLog(0x534e4554, "27046057", uid, NULL, 0);
+    } else {
+        mCore->dump(result, prefix);
+    }
 }
 
 } // namespace android
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index f785db0..ba07362 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -79,6 +79,7 @@
     mGenerationNumber(0),
     mAsyncMode(false),
     mSingleBufferMode(false),
+    mAutoRefresh(false),
     mSingleBufferSlot(INVALID_BUFFER_SLOT),
     mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
             HAL_DATASPACE_UNKNOWN)
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 136a14a..17d4a2c 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -806,8 +806,8 @@
                 mCore->mDequeueBufferCannotBlock ||
                 (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot);
         item.mSurfaceDamage = surfaceDamage;
-        item.mSingleBufferMode = mCore->mSingleBufferMode;
         item.mQueuedBuffer = true;
+        item.mAutoRefresh = mCore->mSingleBufferMode && mCore->mAutoRefresh;
 
         mStickyTransform = stickyTransform;
 
@@ -1309,6 +1309,16 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::setAutoRefresh(bool autoRefresh) {
+    ATRACE_CALL();
+    BQ_LOGV("setAutoRefresh: %d", autoRefresh);
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    mCore->mAutoRefresh = autoRefresh;
+    return NO_ERROR;
+}
+
 status_t BufferQueueProducer::setDequeueTimeout(nsecs_t timeout) {
     ATRACE_CALL();
     BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index d01187f..a22b81b 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -74,12 +74,26 @@
     } else {
         mConsumer->setConsumerName(mName);
     }
+
+    mMessageThread = new MessageThread(this);
+    mMessageThread->run();
 }
 
 ConsumerBase::~ConsumerBase() {
     CB_LOGV("~ConsumerBase");
-    Mutex::Autolock lock(mMutex);
 
+    mMessageThread->requestExit();
+    {
+        Mutex::Autolock lock(mMessageQueueLock);
+        mMessageQueue.emplace(std::piecewise_construct,
+                std::forward_as_tuple(EXIT),
+                std::forward_as_tuple());
+        mMessageAvailable.signal();
+    }
+
+    mMessageThread->join();
+
+    Mutex::Autolock lock(mMutex);
     // Verify that abandon() has been called before we get here.  This should
     // be done by ConsumerBase::onLastStrongRef(), but it's possible for a
     // derived class to override that method and not call
@@ -100,6 +114,13 @@
 }
 
 void ConsumerBase::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_FRAME_AVAILABLE),
+            std::forward_as_tuple(item));
+    mMessageAvailable.signal();
+}
+void ConsumerBase::onFrameAvailableHandler(const BufferItem& item) {
     CB_LOGV("onFrameAvailable");
 
     sp<FrameAvailableListener> listener;
@@ -115,6 +136,14 @@
 }
 
 void ConsumerBase::onFrameReplaced(const BufferItem &item) {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_FRAME_REPLACED),
+            std::forward_as_tuple(item));
+    mMessageAvailable.signal();
+}
+
+void ConsumerBase::onFrameReplacedHandler(const BufferItem &item) {
     CB_LOGV("onFrameReplaced");
 
     sp<FrameAvailableListener> listener;
@@ -130,6 +159,14 @@
 }
 
 void ConsumerBase::onBuffersReleased() {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_BUFFERS_RELEASED),
+            std::forward_as_tuple());
+    mMessageAvailable.signal();
+}
+
+void ConsumerBase::onBuffersReleasedHandler() {
     Mutex::Autolock lock(mMutex);
 
     CB_LOGV("onBuffersReleased");
@@ -149,6 +186,45 @@
 }
 
 void ConsumerBase::onSidebandStreamChanged() {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_SIDEBAND_STREAM_CHANGED),
+            std::forward_as_tuple());
+    mMessageAvailable.signal();
+}
+
+void ConsumerBase::onSidebandStreamChangedHandler() {
+}
+
+bool ConsumerBase::MessageThread::threadLoop() {
+    Mutex::Autolock lock(mConsumerBase->mMessageQueueLock);
+
+    if (mConsumerBase->mMessageQueue.empty()) {
+        mConsumerBase->mMessageAvailable.wait(mConsumerBase->mMessageQueueLock);
+    }
+
+    while (!mConsumerBase->mMessageQueue.empty()) {
+        auto nextMessage = mConsumerBase->mMessageQueue.front();
+
+        switch (nextMessage.first) {
+            case ON_FRAME_AVAILABLE:
+                mConsumerBase->onFrameAvailableHandler(nextMessage.second);
+                break;
+            case ON_FRAME_REPLACED:
+                mConsumerBase->onFrameReplacedHandler(nextMessage.second);
+                break;
+            case ON_BUFFERS_RELEASED:
+                mConsumerBase->onBuffersReleasedHandler();
+                break;
+            case ON_SIDEBAND_STREAM_CHANGED:
+                mConsumerBase->onSidebandStreamChangedHandler();
+                break;
+            case EXIT:
+                break;
+        }
+        mConsumerBase->mMessageQueue.pop();
+    }
+    return true;
 }
 
 void ConsumerBase::abandon() {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 70c3dfa..149f5bd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -408,7 +408,7 @@
     }
 
     // Do whatever sync ops we need to do before releasing the old slot.
-    if (!item.mSingleBufferMode || slot != mCurrentTexture) {
+    if (slot != mCurrentTexture) {
         err = syncForReleaseLocked(mEglDisplay);
         if (err != NO_ERROR) {
             // Release the buffer we just acquired.  It's not safe to
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 2478601..c66694d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -52,6 +52,7 @@
     SET_ASYNC_MODE,
     GET_NEXT_FRAME_NUMBER,
     SET_SINGLE_BUFFER_MODE,
+    SET_AUTO_REFRESH,
     SET_DEQUEUE_TIMEOUT,
 };
 
@@ -355,6 +356,18 @@
         return result;
     }
 
+    virtual status_t setAutoRefresh(bool autoRefresh) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(autoRefresh);
+        status_t result = remote()->transact(SET_AUTO_REFRESH, data, &reply);
+        if (result == NO_ERROR) {
+            result = reply.readInt32();
+        }
+        return result;
+    }
+
     virtual status_t setDequeueTimeout(nsecs_t timeout) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
@@ -562,6 +575,13 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case SET_AUTO_REFRESH: {
+            CHECK_INTERFACE(IGraphicBuffer, data, reply);
+            bool autoRefresh = data.readInt32();
+            status_t result = setAutoRefresh(autoRefresh);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
         case SET_DEQUEUE_TIMEOUT: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             nsecs_t timeout = data.readInt64();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6fc55c3..c295684 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -44,7 +44,11 @@
         bool controlledByApp)
     : mGraphicBufferProducer(bufferProducer),
       mCrop(Rect::EMPTY_RECT),
-      mGenerationNumber(0)
+      mGenerationNumber(0),
+      mSingleBufferMode(false),
+      mAutoRefresh(false),
+      mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
+      mSharedBufferHasBeenQueued(false)
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -232,6 +236,16 @@
 
         reqFormat = mReqFormat;
         reqUsage = mReqUsage;
+
+        if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot !=
+                BufferItem::INVALID_BUFFER_SLOT) {
+            sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer);
+            if (gbuf != NULL) {
+                *buffer = gbuf.get();
+                *fenceFd = -1;
+                return OK;
+            }
+        }
     } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffer
 
     int buf = -1;
@@ -279,6 +293,15 @@
     }
 
     *buffer = gbuf.get();
+
+    if (mSingleBufferMode && mAutoRefresh) {
+        mSharedBufferSlot = buf;
+        mSharedBufferHasBeenQueued = false;
+    } else if (mSharedBufferSlot == buf) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+        mSharedBufferHasBeenQueued = false;
+    }
+
     return OK;
 }
 
@@ -294,8 +317,19 @@
         }
         return i;
     }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
     sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
     mGraphicBufferProducer->cancelBuffer(i, fence);
+
+    if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+        mSharedBufferHasBeenQueued = true;
+    }
+
     return OK;
 }
 
@@ -323,6 +357,7 @@
     Mutex::Autolock lock(mMutex);
     int64_t timestamp;
     bool isAutoTimestamp = false;
+
     if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
         timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         isAutoTimestamp = true;
@@ -338,6 +373,12 @@
         }
         return i;
     }
+    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
+        if (fenceFd >= 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
 
 
     // Make sure the crop rectangle is entirely inside the buffer.
@@ -417,6 +458,7 @@
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
+
     uint32_t numPendingBuffers = 0;
     uint32_t hint = 0;
     output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
@@ -434,6 +476,10 @@
         mDirtyRegion = Region::INVALID_REGION;
     }
 
+    if (mSingleBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
+        mSharedBufferHasBeenQueued = true;
+    }
+
     return err;
 }
 
@@ -557,6 +603,9 @@
     case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE:
         res = dispatchSetSingleBufferMode(args);
         break;
+    case NATIVE_WINDOW_SET_AUTO_REFRESH:
+        res = dispatchSetAutoRefresh(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -669,8 +718,12 @@
 
 int Surface::dispatchSetSingleBufferMode(va_list args) {
     bool singleBufferMode = va_arg(args, int);
-    setSingleBufferMode(singleBufferMode);
-    return NO_ERROR;
+    return setSingleBufferMode(singleBufferMode);
+}
+
+int Surface::dispatchSetAutoRefresh(va_list args) {
+    bool autoRefresh = va_arg(args, int);
+    return setAutoRefresh(autoRefresh);
 }
 
 int Surface::connect(int api) {
@@ -714,6 +767,8 @@
     ATRACE_CALL();
     ALOGV("Surface::disconnect");
     Mutex::Autolock lock(mMutex);
+    mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    mSharedBufferHasBeenQueued = false;
     freeAllBuffers();
     int err = mGraphicBufferProducer->disconnect(api);
     if (!err) {
@@ -796,6 +851,9 @@
 {
     ALOGV("Surface::setUsage");
     Mutex::Autolock lock(mMutex);
+    if (reqUsage != mReqUsage) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqUsage = reqUsage;
     return OK;
 }
@@ -838,10 +896,6 @@
         }
     }
 
-    if (err == NO_ERROR) {
-        freeAllBuffers();
-    }
-
     ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
              bufferCount, strerror(-err));
 
@@ -858,10 +912,6 @@
     ALOGE_IF(err, "IGraphicBufferProducer::setMaxDequeuedBufferCount(%d) "
             "returned %s", maxDequeuedBuffers, strerror(-err));
 
-    if (err == NO_ERROR) {
-        freeAllBuffers();
-    }
-
     return err;
 }
 
@@ -874,10 +924,6 @@
     ALOGE_IF(err, "IGraphicBufferProducer::setAsyncMode(%d) returned %s",
             async, strerror(-err));
 
-    if (err == NO_ERROR) {
-        freeAllBuffers();
-    }
-
     return err;
 }
 
@@ -888,12 +934,29 @@
 
     status_t err = mGraphicBufferProducer->setSingleBufferMode(
             singleBufferMode);
-    ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned"
+    if (err == NO_ERROR) {
+        mSingleBufferMode = singleBufferMode;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setSingleBufferMode(%d) returned"
             "%s", singleBufferMode, strerror(-err));
 
     return err;
 }
 
+int Surface::setAutoRefresh(bool autoRefresh) {
+    ATRACE_CALL();
+    ALOGV("Surface::setAutoRefresh (%d)", autoRefresh);
+    Mutex::Autolock lock(mMutex);
+
+    status_t err = mGraphicBufferProducer->setAutoRefresh(autoRefresh);
+    if (err == NO_ERROR) {
+        mAutoRefresh = autoRefresh;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setAutoRefresh(%d) returned %s",
+            autoRefresh, strerror(-err));
+    return err;
+}
+
 int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
 {
     ATRACE_CALL();
@@ -903,6 +966,9 @@
         return BAD_VALUE;
 
     Mutex::Autolock lock(mMutex);
+    if (width != mReqWidth || height != mReqHeight) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqWidth = width;
     mReqHeight = height;
     return NO_ERROR;
@@ -917,6 +983,9 @@
         return BAD_VALUE;
 
     Mutex::Autolock lock(mMutex);
+    if (width != mUserWidth || height != mUserHeight) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mUserWidth = width;
     mUserHeight = height;
     return NO_ERROR;
@@ -927,6 +996,9 @@
     ALOGV("Surface::setBuffersFormat");
 
     Mutex::Autolock lock(mMutex);
+    if (format != mReqFormat) {
+        mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
+    }
     mReqFormat = format;
     return NO_ERROR;
 }
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index f4c47ed..b6af166 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -65,6 +65,27 @@
         BufferQueue::createBufferQueue(&mProducer, &mConsumer);
     }
 
+    void testBufferItem(const IGraphicBufferProducer::QueueBufferInput& input,
+            const BufferItem& item) {
+        int64_t timestamp;
+        bool isAutoTimestamp;
+        android_dataspace dataSpace;
+        Rect crop;
+        int scalingMode;
+        uint32_t transform;
+        sp<Fence> fence;
+
+        input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop,
+                &scalingMode, &transform, &fence, NULL);
+        ASSERT_EQ(timestamp, item.mTimestamp);
+        ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp);
+        ASSERT_EQ(dataSpace, item.mDataSpace);
+        ASSERT_EQ(crop, item.mCrop);
+        ASSERT_EQ(static_cast<uint32_t>(scalingMode), item.mScalingMode);
+        ASSERT_EQ(transform, item.mTransform);
+        ASSERT_EQ(fence, item.mFence);
+    }
+
     sp<IGraphicBufferProducer> mProducer;
     sp<IGraphicBufferConsumer> mConsumer;
 };
@@ -521,7 +542,7 @@
     ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
 }
 
-TEST_F(BufferQueueTest, TestSingleBufferMode) {
+TEST_F(BufferQueueTest, TestSingleBufferModeWithoutAutoRefresh) {
     createBufferQueue();
     sp<DummyConsumer> dc(new DummyConsumer);
     ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
@@ -545,19 +566,66 @@
             NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
     ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
 
+    // Repeatedly queue and dequeue a buffer from the producer side, it should
+    // always return the same one. And we won't run out of buffers because it's
+    // always the same one and because async mode gets enabled.
+    int slot;
+    for (int i = 0; i < 5; i++) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(singleSlot, slot);
+        ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+    }
+
+    // acquire the buffer
+    BufferItem item;
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(singleSlot, item.mSlot);
+    testBufferItem(input, item);
+    ASSERT_EQ(true, item.mQueuedBuffer);
+    ASSERT_EQ(false, item.mAutoRefresh);
+
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+    // attempt to acquire a second time should return no buffer available
+    ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
+            mConsumer->acquireBuffer(&item, 0));
+}
+
+TEST_F(BufferQueueTest, TestSingleBufferModeWithAutoRefresh) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+
+    ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+    ASSERT_EQ(OK, mProducer->setAutoRefresh(true));
+
+    // Get a buffer
+    int singleSlot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+    // Queue the buffer
+    IGraphicBufferProducer::QueueBufferInput input(0, false,
+            HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+            NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
     // Repeatedly acquire and release a buffer from the consumer side, it should
     // always return the same one.
     BufferItem item;
     for (int i = 0; i < 5; i++) {
         ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
         ASSERT_EQ(singleSlot, item.mSlot);
-        ASSERT_EQ(0, item.mTimestamp);
-        ASSERT_EQ(false, item.mIsAutoTimestamp);
-        ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
-        ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
-        ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
-        ASSERT_EQ(0u, item.mTransform);
-        ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+        testBufferItem(input, item);
+        ASSERT_EQ(i == 0, item.mQueuedBuffer);
+        ASSERT_EQ(true, item.mAutoRefresh);
 
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
@@ -585,6 +653,8 @@
         ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
         ASSERT_EQ(0u, item.mTransform);
         ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+        ASSERT_EQ(i == 0, item.mQueuedBuffer);
+        ASSERT_EQ(true, item.mAutoRefresh);
 
         ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
                 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
diff --git a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
index c28b4d1..b8a7a90 100644
--- a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
@@ -192,6 +192,10 @@
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     mProducerEglSurface = EGL_NO_SURFACE;
 
+    // sleep for 10ms to allow any asynchronous operations to complete before
+    // checking the reference counts
+    usleep(10000);
+
     // This test should have the only reference to buffer 0.
     EXPECT_EQ(1, buffers[0]->getStrongCount());
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6f0104a..0de60c9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -232,4 +232,30 @@
     EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
 }
 
+TEST_F(SurfaceTest, DynamicSetBufferCount) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setConsumerName(String8("TestConsumer"));
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(),
+            NATIVE_WINDOW_API_CPU));
+    native_window_set_buffer_count(window.get(), 4);
+
+    int fence;
+    ANativeWindowBuffer* buffer;
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    native_window_set_buffer_count(window.get(), 3);
+    ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+    native_window_set_buffer_count(window.get(), 2);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+}
+
 }
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index fe649fb..0627ca6 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -608,7 +608,7 @@
         return NULL;
     }
     if (numKeys > MAX_KEYS) {
-        ALOGE("Too many keys in KeyCharacterMap (%d > %d)", numKeys, MAX_KEYS);
+        ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS);
         return NULL;
     }
 
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index c7e2afb..05700f8 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1197,6 +1197,7 @@
     //XXX: temporary hack for the EGL hook-up for single buffer mode
     if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER ||
             value == EGL_SINGLE_BUFFER)) {
+        native_window_set_auto_refresh(s->win.get(), true);
         return (native_window_set_single_buffer_mode(s->win.get(),
                 value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE;
     }
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index d84ce42..ef17630 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -20,6 +20,8 @@
 
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
+LOCAL_CFLAGS += -Wall -Werror
+
 LOCAL_CFLAGS += -fvisibility=hidden
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/sensorservice/Fusion.cpp b/services/sensorservice/Fusion.cpp
index 359d289..e92b23d 100644
--- a/services/sensorservice/Fusion.cpp
+++ b/services/sensorservice/Fusion.cpp
@@ -72,8 +72,6 @@
  */
 static const float NOMINAL_GRAVITY = 9.81f;
 static const float FREE_FALL_THRESHOLD = 0.1f * (NOMINAL_GRAVITY);
-static const float FREE_FALL_THRESHOLD_SQ =
-        FREE_FALL_THRESHOLD*FREE_FALL_THRESHOLD;
 
 /*
  * The geomagnetic-field should be between 30uT and 60uT.
@@ -104,7 +102,6 @@
 static const float MIN_VALID_CROSS_PRODUCT_MAG_SQ =
     MIN_VALID_CROSS_PRODUCT_MAG*MIN_VALID_CROSS_PRODUCT_MAG;
 
-static const float W_EPS = 1e-4f;
 static const float SQRT_3 = 1.732f;
 static const float WVEC_EPS = 1e-4f/SQRT_3;
 // -----------------------------------------------------------------------
diff --git a/services/sensorservice/MostRecentEventLogger.cpp b/services/sensorservice/MostRecentEventLogger.cpp
index 0bd0e17..a5d8456 100644
--- a/services/sensorservice/MostRecentEventLogger.cpp
+++ b/services/sensorservice/MostRecentEventLogger.cpp
@@ -16,10 +16,12 @@
 
 #include "MostRecentEventLogger.h"
 
+#include <inttypes.h>
+
 namespace android {
 
 SensorService::MostRecentEventLogger::MostRecentEventLogger(int sensorType) :
-        mSensorType(sensorType), mNextInd(0) {
+        mNextInd(0), mSensorType(sensorType) {
 
     mBufSize = (sensorType == SENSOR_TYPE_STEP_COUNTER ||
                 sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
@@ -61,13 +63,13 @@
         }
         result.appendFormat("%d) ", eventNum++);
         if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
-            result.appendFormat("%llu,", mTrimmedSensorEventArr[i]->mStepCounter);
+            result.appendFormat("%" PRIu64 ",", mTrimmedSensorEventArr[i]->mStepCounter);
         } else {
             for (int j = 0; j < numData; ++j) {
                 result.appendFormat("%5.1f,", mTrimmedSensorEventArr[i]->mData[j]);
             }
         }
-        result.appendFormat("%lld %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
+        result.appendFormat("%" PRId64 " %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
                 mTrimmedSensorEventArr[i]->mHour, mTrimmedSensorEventArr[i]->mMin,
                 mTrimmedSensorEventArr[i]->mSec);
         i = (i + 1) % mBufSize;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 64c1dd9..1a0d689 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -547,8 +547,14 @@
     return 0;
 }
 
-status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) {
-    return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode);
+status_t VirtualDisplaySurface::setSingleBufferMode(bool /*singleBufferMode*/) {
+    ALOGE("setSingleBufferMode not supported on VirtualDisplaySurface");
+    return INVALID_OPERATION;
+}
+
+status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) {
+    ALOGE("setAutoRefresh not supported on VirtualDisplaySurface");
+    return INVALID_OPERATION;
 }
 
 status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 7f451a9..ede204c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -121,6 +121,7 @@
     virtual String8 getConsumerName() const override;
     virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+    virtual status_t setAutoRefresh(bool autoRefresh) override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
 
     //
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 28e5e43..9dca798 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -88,7 +88,7 @@
         mQueueItems(),
         mLastFrameNumberReceived(0),
         mUpdateTexImageFailed(false),
-        mSingleBufferMode(false)
+        mAutoRefresh(false)
 {
     mCurrentCrop.makeInvalid();
     mFlinger->getRenderEngine().genTextures(1, &mTextureName);
@@ -1263,7 +1263,7 @@
 // ----------------------------------------------------------------------------
 
 bool Layer::shouldPresentNow(const DispSync& dispSync) const {
-    if (mSidebandStreamChanged || mSingleBufferMode) {
+    if (mSidebandStreamChanged || mAutoRefresh) {
         return true;
     }
 
@@ -1287,7 +1287,7 @@
 
 bool Layer::onPreComposition() {
     mRefreshPending = false;
-    return mQueuedFrames > 0 || mSidebandStreamChanged || mSingleBufferMode;
+    return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
 }
 
 void Layer::onPostComposition() {
@@ -1344,7 +1344,7 @@
     }
 
     Region outDirtyRegion;
-    if (mQueuedFrames > 0 || mSingleBufferMode) {
+    if (mQueuedFrames > 0 || mAutoRefresh) {
 
         // if we've already called updateTexImage() without going through
         // a composition step, we have to skip this layer at this point
@@ -1510,7 +1510,7 @@
         // buffer mode.
         bool queuedBuffer = false;
         status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
-                mFlinger->mPrimaryDispSync, &mSingleBufferMode, &queuedBuffer,
+                mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
                 mLastFrameNumberReceived);
         if (updateResult == BufferQueue::PRESENT_LATER) {
             // Producer doesn't want buffer to be displayed yet.  Signal a
@@ -1566,7 +1566,7 @@
         // Decrement the queued-frames count.  Signal another event if we
         // have more frames pending.
         if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1)
-                || mSingleBufferMode) {
+                || mAutoRefresh) {
             mFlinger->signalLayerUpdate();
         }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d91e94e..1773daf 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -307,7 +307,7 @@
      * Returns if a frame is queued.
      */
     bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
-            mSidebandStreamChanged || mSingleBufferMode; }
+            mSidebandStreamChanged || mAutoRefresh; }
 
     // -----------------------------------------------------------------------
 
@@ -493,7 +493,7 @@
     std::atomic<uint64_t> mLastFrameNumberReceived;
     bool mUpdateTexImageFailed; // This is only modified from the main thread
 
-    bool mSingleBufferMode;
+    bool mAutoRefresh;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index efc44ab..e161d9f 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -135,6 +135,10 @@
     return mProducer->setSingleBufferMode(singleBufferMode);
 }
 
+status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
+    return mProducer->setAutoRefresh(autoRefresh);
+}
+
 status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
     return mProducer->setDequeueTimeout(timeout);
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index aea2e39..35ce558 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -60,7 +60,8 @@
     virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
     virtual IBinder* onAsBinder();
-    virtual status_t setSingleBufferMode(bool singleBufferMode);
+    virtual status_t setSingleBufferMode(bool singleBufferMode) override;
+    virtual status_t setAutoRefresh(bool autoRefresh) override;
 
 private:
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index 5722fb4..d3b66e6 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -32,7 +32,7 @@
 // ---------------------------------------------------------------------------
 
 status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter,
-        const DispSync& dispSync, bool* singleBufferMode, bool* queuedBuffer,
+        const DispSync& dispSync, bool* autoRefresh, bool* queuedBuffer,
         uint64_t maxFrameNumber)
 {
     ATRACE_CALL();
@@ -78,8 +78,8 @@
         return BUFFER_REJECTED;
     }
 
-    if (singleBufferMode) {
-        *singleBufferMode = item.mSingleBufferMode;
+    if (autoRefresh) {
+        *autoRefresh = item.mAutoRefresh;
     }
 
     if (queuedBuffer) {
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 207c243..f3942ab 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -57,7 +57,7 @@
     // this does not guarantee that the buffer has been bound to the GL
     // texture.
     status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync,
-            bool* singleBufferMode, bool* queuedBuffer,
+            bool* autoRefresh, bool* queuedBuffer,
             uint64_t maxFrameNumber = 0);
 
     // See GLConsumer::bindTextureImageLocked().
diff --git a/vulkan/libvulkan/dispatch.tmpl b/vulkan/libvulkan/dispatch.tmpl
index 1a584e3..306aae4 100644
--- a/vulkan/libvulkan/dispatch.tmpl
+++ b/vulkan/libvulkan/dispatch.tmpl
@@ -465,7 +465,7 @@
 {{define "IsInstanceDispatched"}}
   {{AssertType $ "Function"}}
   {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}}
-    {{if (ne $.Name "vkGetInstanceProcAddr")}}true{{end}}
+    {{if and (ne $.Name "vkEnumerateDeviceLayerProperties") (ne $.Name "vkGetInstanceProcAddr")}}true{{end}}
   {{end}}
 {{end}}
 
@@ -561,6 +561,8 @@
   {{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. */}}
diff --git a/vulkan/libvulkan/dispatch_gen.cpp b/vulkan/libvulkan/dispatch_gen.cpp
index 9028c3f..b41efb8 100644
--- a/vulkan/libvulkan/dispatch_gen.cpp
+++ b/vulkan/libvulkan/dispatch_gen.cpp
@@ -215,6 +215,8 @@
     {"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))},
@@ -296,7 +298,6 @@
     {"vkDestroyInstance", offsetof(InstanceDispatchTable, DestroyInstance)},
     {"vkDestroySurfaceKHR", offsetof(InstanceDispatchTable, DestroySurfaceKHR)},
     {"vkEnumerateDeviceExtensionProperties", offsetof(InstanceDispatchTable, EnumerateDeviceExtensionProperties)},
-    {"vkEnumerateDeviceLayerProperties", offsetof(InstanceDispatchTable, EnumerateDeviceLayerProperties)},
     {"vkEnumeratePhysicalDevices", offsetof(InstanceDispatchTable, EnumeratePhysicalDevices)},
     {"vkGetPhysicalDeviceFeatures", offsetof(InstanceDispatchTable, GetPhysicalDeviceFeatures)},
     {"vkGetPhysicalDeviceFormatProperties", offsetof(InstanceDispatchTable, GetPhysicalDeviceFormatProperties)},
@@ -522,11 +523,6 @@
         ALOGE("missing instance proc: %s", "vkCreateDevice");
         success = false;
     }
-    dispatch.EnumerateDeviceLayerProperties = reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(get_proc_addr(instance, "vkEnumerateDeviceLayerProperties"));
-    if (UNLIKELY(!dispatch.EnumerateDeviceLayerProperties)) {
-        ALOGE("missing instance proc: %s", "vkEnumerateDeviceLayerProperties");
-        success = false;
-    }
     dispatch.EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(get_proc_addr(instance, "vkEnumerateDeviceExtensionProperties"));
     if (UNLIKELY(!dispatch.EnumerateDeviceExtensionProperties)) {
         ALOGE("missing instance proc: %s", "vkEnumerateDeviceExtensionProperties");
@@ -1271,11 +1267,6 @@
         ALOGE("missing driver proc: %s", "vkCreateDevice");
         success = false;
     }
-    dispatch.EnumerateDeviceLayerProperties = reinterpret_cast<PFN_vkEnumerateDeviceLayerProperties>(get_proc_addr(instance, "vkEnumerateDeviceLayerProperties"));
-    if (UNLIKELY(!dispatch.EnumerateDeviceLayerProperties)) {
-        ALOGE("missing driver proc: %s", "vkEnumerateDeviceLayerProperties");
-        success = false;
-    }
     dispatch.EnumerateDeviceExtensionProperties = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(get_proc_addr(instance, "vkEnumerateDeviceExtensionProperties"));
     if (UNLIKELY(!dispatch.EnumerateDeviceExtensionProperties)) {
         ALOGE("missing driver proc: %s", "vkEnumerateDeviceExtensionProperties");
@@ -1422,12 +1413,12 @@
 
 __attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
-    return GetDispatchTable(physicalDevice).EnumerateDeviceLayerProperties(physicalDevice, pPropertyCount, 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 GetDispatchTable(physicalDevice).EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pPropertyCount, pProperties);
+    return EnumerateDeviceExtensionProperties_Top(physicalDevice, pLayerName, pPropertyCount, pProperties);
 }
 
 __attribute__((visibility("default")))
diff --git a/vulkan/libvulkan/dispatch_gen.h b/vulkan/libvulkan/dispatch_gen.h
index 14a4b95..7bab6ca 100644
--- a/vulkan/libvulkan/dispatch_gen.h
+++ b/vulkan/libvulkan/dispatch_gen.h
@@ -34,7 +34,6 @@
     PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
     PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
     PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
     PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
@@ -190,7 +189,6 @@
     PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
     PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
     PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index a0c142e..1a57c22 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -454,12 +454,6 @@
 bool AddExtensionToCreateInfo(TCreateInfo& local_create_info,
                               const char* extension_name,
                               const VkAllocationCallbacks* alloc) {
-    for (uint32_t i = 0; i < local_create_info.enabledExtensionCount; ++i) {
-        if (!strcmp(extension_name,
-                    local_create_info.ppEnabledExtensionNames[i])) {
-            return false;
-        }
-    }
     uint32_t extension_count = local_create_info.enabledExtensionCount;
     local_create_info.enabledExtensionCount++;
     void* mem = alloc->pfnAllocation(
@@ -821,55 +815,25 @@
             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 gpu,
-    const char* layer_name,
-    uint32_t* properties_count,
-    VkExtensionProperties* properties) {
-    const VkExtensionProperties* extensions = nullptr;
-    uint32_t num_extensions = 0;
-    if (layer_name) {
-        GetDeviceLayerExtensions(layer_name, &extensions, &num_extensions);
-    } else {
-        Instance& instance = GetDispatchParent(gpu);
-        size_t gpu_idx = 0;
-        while (instance.physical_devices[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;
-    }
-
-    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;
+    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) {
-    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;
+VkResult EnumerateDeviceLayerProperties_Bottom(
+    VkPhysicalDevice /*pdev*/,
+    uint32_t* /*properties_count*/,
+    VkLayerProperties* /*properties*/) {
+    return VK_SUCCESS;
 }
 
 VKAPI_ATTR
@@ -1061,6 +1025,51 @@
     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[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) {
@@ -1149,8 +1158,23 @@
     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) {
+            enable_callback = AddExtensionToCreateInfo(
+                local_create_info, "VK_EXT_debug_report", instance->alloc);
+        }
+    }
+
     result = create_instance(&local_create_info, allocator, &local_instance);
 
+    if (enable_callback) {
+        FreeAllocatedCreateInfo(local_create_info, allocator);
+    }
+
     if (result != VK_SUCCESS) {
         DestroyInstance_Bottom(instance->handle, allocator);
         TeardownInstance(instance->handle, allocator);
@@ -1177,20 +1201,7 @@
     }
     *instance_out = local_instance;
 
-    // Force enable callback extension if required
-    bool enable_callback = false;
-    bool enable_logging = false;
-    if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
-        enable_callback =
-            property_get_bool("debug.vulkan.enable_callback", false);
-        enable_logging = enable_callback;
-        if (enable_callback) {
-            enable_callback = AddExtensionToCreateInfo(
-                local_create_info, "VK_EXT_debug_report", instance->alloc);
-        }
-    }
-
-    if (enable_logging) {
+    if (enable_callback) {
         const VkDebugReportCallbackCreateInfoEXT callback_create_info = {
             .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT,
             .flags =
@@ -1239,6 +1250,17 @@
 }
 
 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,
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index 8081c0e..77c8ebe 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -94,6 +94,8 @@
 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);
 
diff --git a/vulkan/patches/README b/vulkan/patches/README
deleted file mode 100644
index d424dd8..0000000
--- a/vulkan/patches/README
+++ /dev/null
@@ -1,26 +0,0 @@
-frameworks/native/vulkan/patches
-================================
-Each subdirectory corresponds to a sequence of patches. These are
-"virtual branches": we only have one shared branch, so these let us
-share experimental or auxiliary changes without disturbing the main
-branch.
-
-To apply:
-$ cd <somewhere in target git repo>
-$ git am $VULKAN_PATCHES/$PATCH_DIR/*
-
-
-frameworks_base-apk_library_dir
--------------------------------
-This branch is for $TOP/frameworks/base. It modifies the framework to
-inform the Vulkan loader, during activity startup, where the
-activity's native library directory. The loader will search this
-directory for layer libraries. Without this change, layers will only
-be loaded from a global location under /data.
-
-
-build-install_libvulkan
------------------------
-This branch is for $TOP/build. It adds libvulkan.so to the base
-PRODUCT_PACKAGES variable, so it will be built and installed on the system
-partition by default.
diff --git a/vulkan/patches/build-install_libvulkan/0001-Add-libvulkan-to-base-PRODUCT_PACKAGES.patch b/vulkan/patches/build-install_libvulkan/0001-Add-libvulkan-to-base-PRODUCT_PACKAGES.patch
deleted file mode 100644
index 9d214bd..0000000
--- a/vulkan/patches/build-install_libvulkan/0001-Add-libvulkan-to-base-PRODUCT_PACKAGES.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From a0aa01fb36a2769b7113316c86e902def62001d9 Mon Sep 17 00:00:00 2001
-From: Jesse Hall <jessehall@google.com>
-Date: Wed, 14 Oct 2015 15:20:34 -0700
-Subject: [PATCH] Add libvulkan to base PRODUCT_PACKAGES
-
-Change-Id: I6c3ad4732148888a88fe980bf8e2bedf26ee74c8
----
- target/product/base.mk | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/target/product/base.mk b/target/product/base.mk
-index 1699156..4b9ce92 100644
---- a/target/product/base.mk
-+++ b/target/product/base.mk
-@@ -94,6 +94,7 @@ PRODUCT_PACKAGES += \
-     libvisualizer \
-     libvorbisidec \
-     libmediandk \
-+    libvulkan \
-     libwifi-service \
-     media \
-     media_cmd \
--- 
-2.6.0.rc2.230.g3dd15c0
-
diff --git a/vulkan/patches/frameworks_base-apk_library_dir/0001-Adding-plumbing-for-passing-the-lib-directory.patch b/vulkan/patches/frameworks_base-apk_library_dir/0001-Adding-plumbing-for-passing-the-lib-directory.patch
deleted file mode 100644
index 81022d6..0000000
--- a/vulkan/patches/frameworks_base-apk_library_dir/0001-Adding-plumbing-for-passing-the-lib-directory.patch
+++ /dev/null
@@ -1,133 +0,0 @@
-From 5c7e465f1d11bccecdc5cacce87d1fd7deeb5adb Mon Sep 17 00:00:00 2001
-From: Michael Lentine <mlentine@google.com>
-Date: Mon, 14 Sep 2015 13:28:25 -0500
-Subject: [PATCH] Adding plumbing for passing the lib directory.
-
-Added call in handleBindApplication which will pass the library path into
-HardwareRender which then passes it to libvulkan through ThreadedRenderer's
-jni interface.
-
-Change-Id: Ie5709ac46f47c4af5c020d604a479e78745d7777
----
- core/java/android/app/ActivityThread.java    |  7 +++++--
- core/java/android/view/HardwareRenderer.java | 11 +++++++++++
- core/java/android/view/ThreadedRenderer.java |  1 +
- core/jni/Android.mk                          |  2 ++
- core/jni/android_view_ThreadedRenderer.cpp   | 15 +++++++++++++++
- 5 files changed, 34 insertions(+), 2 deletions(-)
-
-diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
-index da21eaf..76608c6 100644
---- a/core/java/android/app/ActivityThread.java
-+++ b/core/java/android/app/ActivityThread.java
-@@ -4520,8 +4520,11 @@ public final class ActivityThread {
-             } else {
-                 Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
-             }
--        }
--
-+        } 
-+        
-+        // Add the lib dir path to hardware renderer so that vulkan layers
-+        // can be searched for within that directory.
-+        HardwareRenderer.setLibDir(data.info.getLibDir());
- 
-         final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));
-         DateFormat.set24HourTimePref(is24Hr);
-diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
-index 5e58250..ed99115 100644
---- a/core/java/android/view/HardwareRenderer.java
-+++ b/core/java/android/view/HardwareRenderer.java
-@@ -301,6 +301,17 @@ public abstract class HardwareRenderer {
-     }
- 
-     /**
-+     * Sets the library directory to use as a search path for vulkan layers.
-+     *
-+     * @param libDir A directory that contains vulkan layers
-+     *
-+     * @hide
-+     */
-+    public static void setLibDir(String libDir) {
-+        ThreadedRenderer.setupVulkanLayerPath(libDir);
-+    }
-+
-+    /**
-      * Indicates that the specified hardware layer needs to be updated
-      * as soon as possible.
-      *
-diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
-index f6119e2..d3e5175 100644
---- a/core/java/android/view/ThreadedRenderer.java
-+++ b/core/java/android/view/ThreadedRenderer.java
-@@ -492,6 +492,7 @@ public class ThreadedRenderer extends HardwareRenderer {
-     }
- 
-     static native void setupShadersDiskCache(String cacheFile);
-+    static native void setupVulkanLayerPath(String layerPath);
- 
-     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
-     private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
-diff --git a/core/jni/Android.mk b/core/jni/Android.mk
-index 6b07a47..438e95b 100644
---- a/core/jni/Android.mk
-+++ b/core/jni/Android.mk
-@@ -177,6 +177,7 @@ LOCAL_C_INCLUDES += \
-     $(LOCAL_PATH)/android/graphics \
-     $(LOCAL_PATH)/../../libs/hwui \
-     $(LOCAL_PATH)/../../../native/opengl/libs \
-+    $(LOCAL_PATH)/../../../native/vulkan/include \
-     $(call include-path-for, bluedroid) \
-     $(call include-path-for, libhardware)/hardware \
-     $(call include-path-for, libhardware_legacy)/hardware_legacy \
-@@ -225,6 +226,7 @@ LOCAL_SHARED_LIBRARIES := \
-     libEGL \
-     libGLESv1_CM \
-     libGLESv2 \
-+    libvulkan \
-     libETC1 \
-     libhardware \
-     libhardware_legacy \
-diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
-index 47132f4..69e8ca6 100644
---- a/core/jni/android_view_ThreadedRenderer.cpp
-+++ b/core/jni/android_view_ThreadedRenderer.cpp
-@@ -27,6 +27,7 @@
- #include <EGL/egl.h>
- #include <EGL/eglext.h>
- #include <EGL/egl_cache.h>
-+#include <vulkan/vulkan_loader_data.h>
- 
- #include <utils/StrongPointer.h>
- #include <android_runtime/android_view_Surface.h>
-@@ -448,6 +449,18 @@ static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, job
- }
- 
- // ----------------------------------------------------------------------------
-+// Layers
-+// ----------------------------------------------------------------------------
-+
-+static void android_view_ThreadedRenderer_setupVulkanLayerPath(JNIEnv* env, jobject clazz,
-+        jstring layerPath) {
-+
-+    const char* layerArray = env->GetStringUTFChars(layerPath, NULL);
-+    vulkan::LoaderData::GetInstance().layer_path = layerArray;
-+    env->ReleaseStringUTFChars(layerPath, layerArray);
-+}
-+
-+// ----------------------------------------------------------------------------
- // JNI Glue
- // ----------------------------------------------------------------------------
- 
-@@ -487,6 +500,8 @@ static JNINativeMethod gMethods[] = {
-     { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
-     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
-                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
-+    { "setupVulkanLayerPath", "(Ljava/lang/String;)V",
-+                (void*) android_view_ThreadedRenderer_setupVulkanLayerPath },
- };
- 
- int register_android_view_ThreadedRenderer(JNIEnv* env) {
--- 
-2.6.0.rc2.230.g3dd15c0
-
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 42bdb9d..e650a27 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -29,6 +29,13 @@
 
 namespace {
 
+struct Options {
+    bool layer_description;
+    bool layer_extensions;
+    bool unsupported_features;
+    bool validate;
+};
+
 struct GpuInfo {
     VkPhysicalDeviceProperties properties;
     VkPhysicalDeviceMemoryProperties memory;
@@ -116,7 +123,9 @@
         die("vkEnumerateDeviceExtensionProperties (data)", result);
 }
 
-void GatherGpuInfo(VkPhysicalDevice gpu, GpuInfo& info) {
+void GatherGpuInfo(VkPhysicalDevice gpu,
+                   const Options &options,
+                   GpuInfo& info) {
     VkResult result;
     uint32_t count;
 
@@ -168,12 +177,28 @@
         .queueCount = 1,
         queue_priorities
     };
+    // clang-format off
+    const char *kValidationLayers[] = {
+        "VK_LAYER_GOOGLE_threading",
+        "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_swapchain",
+        "VK_LAYER_GOOGLE_unique_objects"
+    };
+    // clang-format on
+    uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
     const VkDeviceCreateInfo create_info = {
         .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
         .queueCreateInfoCount = 1,
         .pQueueCreateInfos = &queue_create_info,
         .enabledExtensionCount = num_extensions,
         .ppEnabledExtensionNames = extensions,
+        .enabledLayerCount = (options.validate) ? num_layers : 0,
+        .ppEnabledLayerNames = kValidationLayers,
         .pEnabledFeatures = &info.features,
     };
     result = vkCreateDevice(gpu, &create_info, nullptr, &device);
@@ -182,7 +207,7 @@
     vkDestroyDevice(device, nullptr);
 }
 
-void GatherInfo(VulkanInfo* info) {
+void GatherInfo(VulkanInfo* info, const Options& options) {
     VkResult result;
     uint32_t count;
 
@@ -218,10 +243,27 @@
             extensions[num_extensions++] = desired_ext;
     }
 
+    // clang-format off
+    const char *kValidationLayers[] = {
+        "VK_LAYER_GOOGLE_threading",
+        "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_swapchain",
+        "VK_LAYER_GOOGLE_unique_objects"
+    };
+    // clang-format on
+    uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
+
     const VkInstanceCreateInfo create_info = {
         .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
         .enabledExtensionCount = num_extensions,
         .ppEnabledExtensionNames = extensions,
+        .enabledLayerCount = (options.validate) ? num_layers : 0,
+        .ppEnabledLayerNames = kValidationLayers,
     };
     VkInstance instance;
     result = vkCreateInstance(&create_info, nullptr, &instance);
@@ -242,18 +284,13 @@
 
     info->gpus.resize(num_gpus);
     for (size_t i = 0; i < gpus.size(); i++)
-        GatherGpuInfo(gpus[i], info->gpus.at(i));
+        GatherGpuInfo(gpus[i], options, info->gpus.at(i));
 
     vkDestroyInstance(instance, nullptr);
 }
 
 // ----------------------------------------------------------------------------
 
-struct Options {
-    bool layer_description;
-    bool layer_extensions;
-};
-
 const size_t kMaxIndent = 8;
 const size_t kIndentSize = 3;
 std::array<char, kMaxIndent * kIndentSize + 1> kIndent;
@@ -325,6 +362,128 @@
     }
 }
 
+void PrintAllFeatures(const char* indent,
+                      const VkPhysicalDeviceFeatures& features) {
+    // clang-format off
+    printf("%srobustBufferAccess: %s\n", indent, features.robustBufferAccess ? "YES" : "NO");
+    printf("%sfullDrawIndexUint32: %s\n", indent, features.fullDrawIndexUint32 ? "YES" : "NO");
+    printf("%simageCubeArray: %s\n", indent, features.imageCubeArray ? "YES" : "NO");
+    printf("%sindependentBlend: %s\n", indent, features.independentBlend ? "YES" : "NO");
+    printf("%sgeometryShader: %s\n", indent, features.geometryShader ? "YES" : "NO");
+    printf("%stessellationShader: %s\n", indent, features.tessellationShader ? "YES" : "NO");
+    printf("%ssampleRateShading: %s\n", indent, features.sampleRateShading ? "YES" : "NO");
+    printf("%sdualSrcBlend: %s\n", indent, features.dualSrcBlend ? "YES" : "NO");
+    printf("%slogicOp: %s\n", indent, features.logicOp ? "YES" : "NO");
+    printf("%smultiDrawIndirect: %s\n", indent, features.multiDrawIndirect ? "YES" : "NO");
+    printf("%sdrawIndirectFirstInstance: %s\n", indent, features.drawIndirectFirstInstance ? "YES" : "NO");
+    printf("%sdepthClamp: %s\n", indent, features.depthClamp ? "YES" : "NO");
+    printf("%sdepthBiasClamp: %s\n", indent, features.depthBiasClamp ? "YES" : "NO");
+    printf("%sfillModeNonSolid: %s\n", indent, features.fillModeNonSolid ? "YES" : "NO");
+    printf("%sdepthBounds: %s\n", indent, features.depthBounds ? "YES" : "NO");
+    printf("%swideLines: %s\n", indent, features.wideLines ? "YES" : "NO");
+    printf("%slargePoints: %s\n", indent, features.largePoints ? "YES" : "NO");
+    printf("%salphaToOne: %s\n", indent, features.alphaToOne ? "YES" : "NO");
+    printf("%smultiViewport: %s\n", indent, features.multiViewport ? "YES" : "NO");
+    printf("%ssamplerAnisotropy: %s\n", indent, features.samplerAnisotropy ? "YES" : "NO");
+    printf("%stextureCompressionETC2: %s\n", indent, features.textureCompressionETC2 ? "YES" : "NO");
+    printf("%stextureCompressionASTC_LDR: %s\n", indent, features.textureCompressionASTC_LDR ? "YES" : "NO");
+    printf("%stextureCompressionBC: %s\n", indent, features.textureCompressionBC ? "YES" : "NO");
+    printf("%socclusionQueryPrecise: %s\n", indent, features.occlusionQueryPrecise ? "YES" : "NO");
+    printf("%spipelineStatisticsQuery: %s\n", indent, features.pipelineStatisticsQuery ? "YES" : "NO");
+    printf("%svertexPipelineStoresAndAtomics: %s\n", indent, features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
+    printf("%sfragmentStoresAndAtomics: %s\n", indent, features.fragmentStoresAndAtomics ? "YES" : "NO");
+    printf("%sshaderTessellationAndGeometryPointSize: %s\n", indent, features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
+    printf("%sshaderImageGatherExtended: %s\n", indent, features.shaderImageGatherExtended ? "YES" : "NO");
+    printf("%sshaderStorageImageExtendedFormats: %s\n", indent, features.shaderStorageImageExtendedFormats ? "YES" : "NO");
+    printf("%sshaderStorageImageMultisample: %s\n", indent, features.shaderStorageImageMultisample ? "YES" : "NO");
+    printf("%sshaderStorageImageReadWithoutFormat: %s\n", indent, features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
+    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", indent, features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
+    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", indent, features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", indent, features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", indent, features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", indent, features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderClipDistance: %s\n", indent, features.shaderClipDistance ? "YES" : "NO");
+    printf("%sshaderCullDistance: %s\n", indent, features.shaderCullDistance ? "YES" : "NO");
+    printf("%sshaderFloat64: %s\n", indent, features.shaderFloat64 ? "YES" : "NO");
+    printf("%sshaderInt64: %s\n", indent, features.shaderInt64 ? "YES" : "NO");
+    printf("%sshaderInt16: %s\n", indent, features.shaderInt16 ? "YES" : "NO");
+    printf("%sshaderResourceResidency: %s\n", indent, features.shaderResourceResidency ? "YES" : "NO");
+    printf("%sshaderResourceMinLod: %s\n", indent, features.shaderResourceMinLod ? "YES" : "NO");
+    printf("%ssparseBinding: %s\n", indent, features.sparseBinding ? "YES" : "NO");
+    printf("%ssparseResidencyBuffer: %s\n", indent, features.sparseResidencyBuffer ? "YES" : "NO");
+    printf("%ssparseResidencyImage2D: %s\n", indent, features.sparseResidencyImage2D ? "YES" : "NO");
+    printf("%ssparseResidencyImage3D: %s\n", indent, features.sparseResidencyImage3D ? "YES" : "NO");
+    printf("%ssparseResidency2Samples: %s\n", indent, features.sparseResidency2Samples ? "YES" : "NO");
+    printf("%ssparseResidency4Samples: %s\n", indent, features.sparseResidency4Samples ? "YES" : "NO");
+    printf("%ssparseResidency8Samples: %s\n", indent, features.sparseResidency8Samples ? "YES" : "NO");
+    printf("%ssparseResidency16Samples: %s\n", indent, features.sparseResidency16Samples ? "YES" : "NO");
+    printf("%ssparseResidencyAliased: %s\n", indent, features.sparseResidencyAliased ? "YES" : "NO");
+    printf("%svariableMultisampleRate: %s\n", indent, features.variableMultisampleRate ? "YES" : "NO");
+    printf("%sinheritedQueries: %s\n", indent, features.inheritedQueries ? "YES" : "NO");
+    // clang-format on
+}
+
+void PrintSupportedFeatures(const char* indent,
+                            const VkPhysicalDeviceFeatures& features) {
+    // clang-format off
+    if (features.robustBufferAccess) printf("%srobustBufferAccess\n", indent);
+    if (features.fullDrawIndexUint32) printf("%sfullDrawIndexUint32\n", indent);
+    if (features.imageCubeArray) printf("%simageCubeArray\n", indent);
+    if (features.independentBlend) printf("%sindependentBlend\n", indent);
+    if (features.geometryShader) printf("%sgeometryShader\n", indent);
+    if (features.tessellationShader) printf("%stessellationShader\n", indent);
+    if (features.sampleRateShading) printf("%ssampleRateShading\n", indent);
+    if (features.dualSrcBlend) printf("%sdualSrcBlend\n", indent);
+    if (features.logicOp) printf("%slogicOp\n", indent);
+    if (features.multiDrawIndirect) printf("%smultiDrawIndirect\n", indent);
+    if (features.drawIndirectFirstInstance) printf("%sdrawIndirectFirstInstance\n", indent);
+    if (features.depthClamp) printf("%sdepthClamp\n", indent);
+    if (features.depthBiasClamp) printf("%sdepthBiasClamp\n", indent);
+    if (features.fillModeNonSolid) printf("%sfillModeNonSolid\n", indent);
+    if (features.depthBounds) printf("%sdepthBounds\n", indent);
+    if (features.wideLines) printf("%swideLines\n", indent);
+    if (features.largePoints) printf("%slargePoints\n", indent);
+    if (features.alphaToOne) printf("%salphaToOne\n", indent);
+    if (features.multiViewport) printf("%smultiViewport\n", indent);
+    if (features.samplerAnisotropy) printf("%ssamplerAnisotropy\n", indent);
+    if (features.textureCompressionETC2) printf("%stextureCompressionETC2\n", indent);
+    if (features.textureCompressionASTC_LDR) printf("%stextureCompressionASTC_LDR\n", indent);
+    if (features.textureCompressionBC) printf("%stextureCompressionBC\n", indent);
+    if (features.occlusionQueryPrecise) printf("%socclusionQueryPrecise\n", indent);
+    if (features.pipelineStatisticsQuery) printf("%spipelineStatisticsQuery\n", indent);
+    if (features.vertexPipelineStoresAndAtomics) printf("%svertexPipelineStoresAndAtomics\n", indent);
+    if (features.fragmentStoresAndAtomics) printf("%sfragmentStoresAndAtomics\n", indent);
+    if (features.shaderTessellationAndGeometryPointSize) printf("%sshaderTessellationAndGeometryPointSize\n", indent);
+    if (features.shaderImageGatherExtended) printf("%sshaderImageGatherExtended\n", indent);
+    if (features.shaderStorageImageExtendedFormats) printf("%sshaderStorageImageExtendedFormats\n", indent);
+    if (features.shaderStorageImageMultisample) printf("%sshaderStorageImageMultisample\n", indent);
+    if (features.shaderStorageImageReadWithoutFormat) printf("%sshaderStorageImageReadWithoutFormat\n", indent);
+    if (features.shaderStorageImageWriteWithoutFormat) printf("%sshaderStorageImageWriteWithoutFormat\n", indent);
+    if (features.shaderUniformBufferArrayDynamicIndexing) printf("%sshaderUniformBufferArrayDynamicIndexing\n", indent);
+    if (features.shaderSampledImageArrayDynamicIndexing) printf("%sshaderSampledImageArrayDynamicIndexing\n", indent);
+    if (features.shaderStorageBufferArrayDynamicIndexing) printf("%sshaderStorageBufferArrayDynamicIndexing\n", indent);
+    if (features.shaderStorageImageArrayDynamicIndexing) printf("%sshaderStorageImageArrayDynamicIndexing\n", indent);
+    if (features.shaderClipDistance) printf("%sshaderClipDistance\n", indent);
+    if (features.shaderCullDistance) printf("%sshaderCullDistance\n", indent);
+    if (features.shaderFloat64) printf("%sshaderFloat64\n", indent);
+    if (features.shaderInt64) printf("%sshaderInt64\n", indent);
+    if (features.shaderInt16) printf("%sshaderInt16\n", indent);
+    if (features.shaderResourceResidency) printf("%sshaderResourceResidency\n", indent);
+    if (features.shaderResourceMinLod) printf("%sshaderResourceMinLod\n", indent);
+    if (features.sparseBinding) printf("%ssparseBinding\n", indent);
+    if (features.sparseResidencyBuffer) printf("%ssparseResidencyBuffer\n", indent);
+    if (features.sparseResidencyImage2D) printf("%ssparseResidencyImage2D\n", indent);
+    if (features.sparseResidencyImage3D) printf("%ssparseResidencyImage3D\n", indent);
+    if (features.sparseResidency2Samples) printf("%ssparseResidency2Samples\n", indent);
+    if (features.sparseResidency4Samples) printf("%ssparseResidency4Samples\n", indent);
+    if (features.sparseResidency8Samples) printf("%ssparseResidency8Samples\n", indent);
+    if (features.sparseResidency16Samples) printf("%ssparseResidency16Samples\n", indent);
+    if (features.sparseResidencyAliased) printf("%ssparseResidencyAliased\n", indent);
+    if (features.variableMultisampleRate) printf("%svariableMultisampleRate\n", indent);
+    if (features.inheritedQueries) printf("%sinheritedQueries\n", indent);
+    // clang-format on
+}
+
 void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) {
     VkResult result;
     std::ostringstream strbuf;
@@ -389,64 +548,12 @@
             qprops.minImageTransferGranularity.depth);
     }
 
-    // clang-format off
     printf("%sFeatures:\n", Indent(indent + 1));
-    printf("%srobustBufferAccess: %s\n", Indent(indent + 2), info.features.robustBufferAccess ? "YES" : "NO");
-    printf("%sfullDrawIndexUint32: %s\n", Indent(indent + 2), info.features.fullDrawIndexUint32 ? "YES" : "NO");
-    printf("%simageCubeArray: %s\n", Indent(indent + 2), info.features.imageCubeArray ? "YES" : "NO");
-    printf("%sindependentBlend: %s\n", Indent(indent + 2), info.features.independentBlend ? "YES" : "NO");
-    printf("%sgeometryShader: %s\n", Indent(indent + 2), info.features.geometryShader ? "YES" : "NO");
-    printf("%stessellationShader: %s\n", Indent(indent + 2), info.features.tessellationShader ? "YES" : "NO");
-    printf("%ssampleRateShading: %s\n", Indent(indent + 2), info.features.sampleRateShading ? "YES" : "NO");
-    printf("%sdualSrcBlend: %s\n", Indent(indent + 2), info.features.dualSrcBlend ? "YES" : "NO");
-    printf("%slogicOp: %s\n", Indent(indent + 2), info.features.logicOp ? "YES" : "NO");
-    printf("%smultiDrawIndirect: %s\n", Indent(indent + 2), info.features.multiDrawIndirect ? "YES" : "NO");
-    printf("%sdrawIndirectFirstInstance: %s\n", Indent(indent + 2), info.features.drawIndirectFirstInstance ? "YES" : "NO");
-    printf("%sdepthClamp: %s\n", Indent(indent + 2), info.features.depthClamp ? "YES" : "NO");
-    printf("%sdepthBiasClamp: %s\n", Indent(indent + 2), info.features.depthBiasClamp ? "YES" : "NO");
-    printf("%sfillModeNonSolid: %s\n", Indent(indent + 2), info.features.fillModeNonSolid ? "YES" : "NO");
-    printf("%sdepthBounds: %s\n", Indent(indent + 2), info.features.depthBounds ? "YES" : "NO");
-    printf("%swideLines: %s\n", Indent(indent + 2), info.features.wideLines ? "YES" : "NO");
-    printf("%slargePoints: %s\n", Indent(indent + 2), info.features.largePoints ? "YES" : "NO");
-    printf("%salphaToOne: %s\n", Indent(indent + 2), info.features.alphaToOne ? "YES" : "NO");
-    printf("%smultiViewport: %s\n", Indent(indent + 2), info.features.multiViewport ? "YES" : "NO");
-    printf("%ssamplerAnisotropy: %s\n", Indent(indent + 2), info.features.samplerAnisotropy ? "YES" : "NO");
-    printf("%stextureCompressionETC2: %s\n", Indent(indent + 2), info.features.textureCompressionETC2 ? "YES" : "NO");
-    printf("%stextureCompressionASTC_LDR: %s\n", Indent(indent + 2), info.features.textureCompressionASTC_LDR ? "YES" : "NO");
-    printf("%stextureCompressionBC: %s\n", Indent(indent + 2), info.features.textureCompressionBC ? "YES" : "NO");
-    printf("%socclusionQueryPrecise: %s\n", Indent(indent + 2), info.features.occlusionQueryPrecise ? "YES" : "NO");
-    printf("%spipelineStatisticsQuery: %s\n", Indent(indent + 2), info.features.pipelineStatisticsQuery ? "YES" : "NO");
-    printf("%svertexPipelineStoresAndAtomics: %s\n", Indent(indent + 2), info.features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
-    printf("%sfragmentStoresAndAtomics: %s\n", Indent(indent + 2), info.features.fragmentStoresAndAtomics ? "YES" : "NO");
-    printf("%sshaderTessellationAndGeometryPointSize: %s\n", Indent(indent + 2), info.features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
-    printf("%sshaderImageGatherExtended: %s\n", Indent(indent + 2), info.features.shaderImageGatherExtended ? "YES" : "NO");
-    printf("%sshaderStorageImageExtendedFormats: %s\n", Indent(indent + 2), info.features.shaderStorageImageExtendedFormats ? "YES" : "NO");
-    printf("%sshaderStorageImageMultisample: %s\n", Indent(indent + 2), info.features.shaderStorageImageMultisample ? "YES" : "NO");
-    printf("%sshaderStorageImageReadWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
-    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
-    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderClipDistance: %s\n", Indent(indent + 2), info.features.shaderClipDistance ? "YES" : "NO");
-    printf("%sshaderCullDistance: %s\n", Indent(indent + 2), info.features.shaderCullDistance ? "YES" : "NO");
-    printf("%sshaderFloat64: %s\n", Indent(indent + 2), info.features.shaderFloat64 ? "YES" : "NO");
-    printf("%sshaderInt64: %s\n", Indent(indent + 2), info.features.shaderInt64 ? "YES" : "NO");
-    printf("%sshaderInt16: %s\n", Indent(indent + 2), info.features.shaderInt16 ? "YES" : "NO");
-    printf("%sshaderResourceResidency: %s\n", Indent(indent + 2), info.features.shaderResourceResidency ? "YES" : "NO");
-    printf("%sshaderResourceMinLod: %s\n", Indent(indent + 2), info.features.shaderResourceMinLod ? "YES" : "NO");
-    printf("%ssparseBinding: %s\n", Indent(indent + 2), info.features.sparseBinding ? "YES" : "NO");
-    printf("%ssparseResidencyBuffer: %s\n", Indent(indent + 2), info.features.sparseResidencyBuffer ? "YES" : "NO");
-    printf("%ssparseResidencyImage2D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage2D ? "YES" : "NO");
-    printf("%ssparseResidencyImage3D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage3D ? "YES" : "NO");
-    printf("%ssparseResidency2Samples: %s\n", Indent(indent + 2), info.features.sparseResidency2Samples ? "YES" : "NO");
-    printf("%ssparseResidency4Samples: %s\n", Indent(indent + 2), info.features.sparseResidency4Samples ? "YES" : "NO");
-    printf("%ssparseResidency8Samples: %s\n", Indent(indent + 2), info.features.sparseResidency8Samples ? "YES" : "NO");
-    printf("%ssparseResidency16Samples: %s\n", Indent(indent + 2), info.features.sparseResidency16Samples ? "YES" : "NO");
-    printf("%ssparseResidencyAliased: %s\n", Indent(indent + 2), info.features.sparseResidencyAliased ? "YES" : "NO");
-    printf("%svariableMultisampleRate: %s\n", Indent(indent + 2), info.features.variableMultisampleRate ? "YES" : "NO");
-    printf("%sinheritedQueries: %s\n", Indent(indent + 2), info.features.inheritedQueries ? "YES" : "NO");
-    // clang-format on
+    if (options.unsupported_features) {
+        PrintAllFeatures(Indent(indent + 2), info.features);
+    } else {
+        PrintSupportedFeatures(Indent(indent + 2), info.features);
+    }
 
     printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size());
     if (!info.extensions.empty())
@@ -472,27 +579,54 @@
         PrintGpuInfo(gpu, options, indent + 1);
 }
 
+const char kUsageString[] =
+    "usage: vkinfo [options]\n"
+    "  -v                       enable all the following verbose options\n"
+    "    -layer_description     print layer description strings\n"
+    "    -layer_extensions      print extensions supported by each layer\n"
+    "    -unsupported_features  print all physical device features\n"
+    "  -validate                enable validation layers if present\n"
+    "  -debug_pause             pause at start until resumed via debugger\n";
+
 }  // namespace
 
 // ----------------------------------------------------------------------------
 
 int main(int argc, char const* argv[]) {
+    static volatile bool startup_pause = false;
     Options options = {
         .layer_description = false, .layer_extensions = false,
+        .unsupported_features = false,
+        .validate = false,
     };
     for (int argi = 1; argi < argc; argi++) {
+        if (strcmp(argv[argi], "-h") == 0) {
+            fputs(kUsageString, stdout);
+            return 0;
+        }
         if (strcmp(argv[argi], "-v") == 0) {
             options.layer_description = true;
             options.layer_extensions = true;
+            options.unsupported_features = true;
         } else if (strcmp(argv[argi], "-layer_description") == 0) {
             options.layer_description = true;
         } else if (strcmp(argv[argi], "-layer_extensions") == 0) {
             options.layer_extensions = true;
+        } else if (strcmp(argv[argi], "-unsupported_features") == 0) {
+            options.unsupported_features = true;
+        } else if (strcmp(argv[argi], "-validate") == 0) {
+            options.validate = true;
+        } else if (strcmp(argv[argi], "-debug_pause") == 0) {
+            startup_pause = true;
         }
     }
 
+    while (startup_pause) {
+        sleep(0);
+    }
+
     VulkanInfo info;
-    GatherInfo(&info);
+    GatherInfo(&info, options);
     PrintInfo(info, options);
     return 0;
 }