Merge "Plumbing for getting FenceTracker timestamps" into nyc-mr1-dev
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index 19d2d64..312dceb 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -80,8 +80,8 @@
     }
 
     if (s == -1) {
-        printf("Failed to connect to dumpstatez service: %s\n", strerror(errno));
-        return EXIT_FAILURE;
+        printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
+        return EXIT_SUCCESS;
     }
 
     // Set a timeout so that if nothing is read in 10 minutes, we'll stop
@@ -91,7 +91,7 @@
     tv.tv_sec = 10 * 60;
     tv.tv_usec = 0;
     if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
-        printf("WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+        fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno));
     }
 
     while (1) {
@@ -105,8 +105,7 @@
             if (errno == EAGAIN) {
                 errno = ETIMEDOUT;
             }
-            printf("\nBugreport read terminated abnormally (%s).\n",
-                    strerror(errno));
+            printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno));
             break;
         }
 
@@ -117,15 +116,17 @@
                     write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send,
                             bytes_to_send));
             if (bytes_written == -1) {
-                printf(
+                fprintf(stderr,
                         "Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
                         bytes_read, bytes_to_send, strerror(errno));
-                return EXIT_FAILURE;
+                break;
             }
             bytes_to_send -= bytes_written;
         } while (bytes_written != 0 && bytes_to_send > 0);
     }
 
-    close(s);
+    if (close(s) == -1) {
+        fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
+    }
     return EXIT_SUCCESS;
 }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index e293164..884cdf4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -72,6 +72,8 @@
 #define RECOVERY_DIR "/cache/recovery"
 #define RECOVERY_DATA_DIR "/data/misc/recovery"
 #define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
+#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
 #define TOMBSTONE_DIR "/data/tombstones"
 #define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
 /* Can accomodate a tombstone number up to 9999. */
@@ -856,7 +858,7 @@
 #endif
     dump_file("INTERRUPTS (1)", "/proc/interrupts");
 
-    run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "connectivity", "--diag", NULL);
+    run_command("NETWORK DIAGNOSTICS", 10, "dumpsys", "-t", "10", "connectivity", "--diag", NULL);
 
 #ifdef FWDUMP_bcmdhd
     run_command("DUMP WIFI STATUS", 20,
@@ -932,30 +934,30 @@
     printf("== Checkins\n");
     printf("========================================================\n");
 
-    run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "batterystats", "-c", NULL);
-    run_command("CHECKIN MEMINFO", 30, "dumpsys", "meminfo", "--checkin", NULL);
-    run_command("CHECKIN NETSTATS", 30, "dumpsys", "netstats", "--checkin", NULL);
-    run_command("CHECKIN PROCSTATS", 30, "dumpsys", "procstats", "-c", NULL);
-    run_command("CHECKIN USAGESTATS", 30, "dumpsys", "usagestats", "-c", NULL);
-    run_command("CHECKIN PACKAGE", 30, "dumpsys", "package", "--checkin", NULL);
+    run_command("CHECKIN BATTERYSTATS", 30, "dumpsys", "-t", "30", "batterystats", "-c", NULL);
+    run_command("CHECKIN MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "--checkin", NULL);
+    run_command("CHECKIN NETSTATS", 30, "dumpsys", "-t", "30", "netstats", "--checkin", NULL);
+    run_command("CHECKIN PROCSTATS", 30, "dumpsys", "-t", "30", "procstats", "-c", NULL);
+    run_command("CHECKIN USAGESTATS", 30, "dumpsys", "-t", "30", "usagestats", "-c", NULL);
+    run_command("CHECKIN PACKAGE", 30, "dumpsys", "-t", "30", "package", "--checkin", NULL);
 
     printf("========================================================\n");
     printf("== Running Application Activities\n");
     printf("========================================================\n");
 
-    run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
+    run_command("APP ACTIVITIES", 30, "-t", "30", "dumpsys", "activity", "all", NULL);
 
     printf("========================================================\n");
     printf("== Running Application Services\n");
     printf("========================================================\n");
 
-    run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
+    run_command("APP SERVICES", 30, "-t", "30", "dumpsys", "activity", "service", "all", NULL);
 
     printf("========================================================\n");
     printf("== Running Application Providers\n");
     printf("========================================================\n");
 
-    run_command("APP SERVICES", 30, "dumpsys", "activity", "provider", "all", NULL);
+    run_command("APP SERVICES", 30, "-t", "30", "dumpsys", "activity", "provider", "all", NULL);
 
 
     printf("========================================================\n");
@@ -1313,7 +1315,7 @@
     // Invoking the following dumpsys calls before dump_traces() to try and
     // keep the system stats as close to its initial state as possible.
     run_command_as_shell("DUMPSYS MEMINFO", 30, "dumpsys", "-t", "30", "meminfo", "-a", NULL);
-    run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "cpuinfo", "-a", NULL);
+    run_command_as_shell("DUMPSYS CPUINFO", 10, "dumpsys", "-t", "10", "cpuinfo", "-a", NULL);
 
     /* collect stack traces from Dalvik and native processes (needs root) */
     dump_traces_path = dump_traces();
@@ -1323,6 +1325,10 @@
     add_dir(RECOVERY_DIR, true);
     add_dir(RECOVERY_DATA_DIR, true);
     add_dir(LOGPERSIST_DATA_DIR, false);
+    if (!is_user_build()) {
+        add_dir(PROFILE_DATA_DIR_CUR, true);
+        add_dir(PROFILE_DATA_DIR_REF, true);
+    }
     add_mountinfo();
 
     if (!drop_root_user()) {
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 55e9521..8999441 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -28,9 +28,9 @@
 #include <sys/xattr.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/log.h>               // TODO: Move everything to base/logging.
@@ -49,6 +49,7 @@
 #define LOG_TAG "installd"
 #endif
 
+using android::base::EndsWith;
 using android::base::StringPrintf;
 
 namespace android {
@@ -70,6 +71,12 @@
     return strcmp(tmp_property_value, "true") == 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);
+}
+
 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);
@@ -105,6 +112,12 @@
                 PLOG(ERROR) << "Failed to prepare " << profile_path;
                 return -1;
             }
+            std::string profile_file = create_primary_profile(profile_path);
+            // read-write only for the app user.
+            if (fs_prepare_file_strict(profile_file.c_str(), 0600, 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.
@@ -157,12 +170,6 @@
     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 bool clear_profile(const std::string& profile) {
     base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
     if (ufd.get() < 0) {
@@ -1202,50 +1209,50 @@
 
 static void run_profman_dump(const std::vector<fd_t>& profile_fds,
                              fd_t reference_profile_fd,
-                             const std::vector<std::string>& code_locations,
-                             const std::vector<fd_t>& code_location_fds,
+                             const std::vector<std::string>& dex_locations,
+                             const std::vector<fd_t>& apk_fds,
                              fd_t output_fd) {
+    std::vector<std::string> profman_args;
     static const char* PROFMAN_BIN = "/system/bin/profman";
-    const bool has_reference_profile = (reference_profile_fd != -1);
-    // program name
-    // --dump-only
-    // --dump-output-to-fd=<output_fd>
-    // (optionally, --reference-profile-file-fd=<reference_profile_fd>)
-    const size_t fixed_args = (has_reference_profile ? 4 : 3);
-    // Fixed arguments, profiles, code paths, code path fds, and final NULL.
-    const size_t argc = fixed_args + profile_fds.size() + code_locations.size() +
-        code_location_fds.size() + 1;
-    const char **argv = new const char*[argc];
-    int i = 0;
-    argv[i++] = PROFMAN_BIN;
-    argv[i++] = "--dump-only";
-    std::string dump_output = StringPrintf("--dump-output-to-fd=%d", output_fd);
-    argv[i++] = dump_output.c_str();
-    if (has_reference_profile) {
-        std::string reference =
-            StringPrintf("--reference-profile-file-fd=%d", reference_profile_fd);
-        argv[i++] = reference.c_str();
+    profman_args.push_back(PROFMAN_BIN);
+    profman_args.push_back("--dump-only");
+    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
+    if (reference_profile_fd != -1) {
+        profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
+                                            reference_profile_fd));
     }
     for (fd_t profile_fd : profile_fds) {
-        std::string profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd);
-        argv[i++] = strdup(profile_arg.c_str());
+        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
     }
-    for (const std::string& code_location : code_locations) {
-        std::string path_str = StringPrintf("--code-location=%s", code_location.c_str());
-        argv[i++] = strdup(path_str.c_str());
+    for (const std::string& dex_location : dex_locations) {
+        profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
     }
-    for (fd_t code_location_fd : code_location_fds) {
-        std::string fd_str = StringPrintf("--code-location-fd=%d", code_location_fd);
-        argv[i++] = strdup(fd_str.c_str());
+    for (fd_t apk_fd : apk_fds) {
+        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
+    }
+    const char **argv = new const char*[profman_args.size() + 1];
+    size_t i = 0;
+    for (const std::string& profman_arg : profman_args) {
+        argv[i++] = profman_arg.c_str();
     }
     argv[i] = NULL;
-    assert(i == argc - 1);
 
     execv(PROFMAN_BIN, (char * const *)argv);
     ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
     exit(68);   /* only get here on exec failure */
 }
 
+static const char* get_location_from_path(const char* path) {
+    static constexpr char kLocationSeparator = '/';
+    const char *location = strrchr(path, kLocationSeparator);
+    if (location == NULL) {
+        return path;
+    } else {
+        // Skip the separator character.
+        return location + 1;
+    }
+}
+
 // Dumps the contents of a profile file, using pkgname's dex files for pretty
 // printing the result.
 bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) {
@@ -1265,32 +1272,35 @@
         return false;
     }
 
-    fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW);
+    fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
     if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
         ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
         return false;
     }
-    std::vector<std::string> code_locations = base::Split(code_path_string, ";");
-    std::vector<fd_t> code_location_fds;
-    for (const std::string& code_location : code_locations) {
-        fd_t code_location_fd = open(code_location.c_str(), O_RDONLY | O_NOFOLLOW);
-        if (code_location_fd == -1) {
-            ALOGE("installd cannot open '%s'\n", code_location.c_str());
+    std::vector<std::string> code_full_paths = base::Split(code_path_string, ";");
+    std::vector<std::string> dex_locations;
+    std::vector<fd_t> apk_fds;
+    for (const std::string& code_full_path : code_full_paths) {
+        const char* full_path = code_full_path.c_str();
+        fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
+        if (apk_fd == -1) {
+            ALOGE("installd cannot open '%s'\n", full_path);
             return false;
         }
-        code_location_fds.push_back(code_location_fd);
+        dex_locations.push_back(get_location_from_path(full_path));
+        apk_fds.push_back(apk_fd);
     }
 
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
-        run_profman_dump(profile_fds, reference_profile_fd, code_locations,
-                         code_location_fds, output_fd);
+        run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
+                         apk_fds, output_fd);
         exit(68);   /* only get here on exec failure */
     }
     /* parent */
-    close_all_fds(code_location_fds, "code_location_fds");
+    close_all_fds(apk_fds, "apk_fds");
     close_all_fds(profile_fds, "profile_fds");
     if (close(reference_profile_fd) != 0) {
         PLOG(WARNING) << "Failed to close fd for reference profile";
@@ -1304,13 +1314,29 @@
     return true;
 }
 
-static void trim_extension(char* path) {
-  // Trim the extension.
-  int pos = strlen(path);
-  for (; pos >= 0 && path[pos] != '.'; --pos) {}
-  if (pos >= 0) {
-      path[pos] = '\0';  // Trim extension
+// Translate the given oat path to an art (app image) path. An empty string
+// denotes an error.
+static std::string create_image_filename(const std::string& oat_path) {
+  // A standard dalvik-cache entry. Replace ".dex" with ".art."
+  if (EndsWith(oat_path, ".dex")) {
+    std::string art_path = oat_path;
+    art_path.replace(art_path.length() - strlen("dex"), strlen("dex"), "art");
+    CHECK(EndsWith(art_path, ".art"));
+    return art_path;
   }
+
+  // An odex entry. Not that this may not be an extension, e.g., in the OTA
+  // case (where the base name will have an extension for the B artifact).
+  size_t odex_pos = oat_path.rfind(".odex");
+  if (odex_pos != std::string::npos) {
+    std::string art_path = oat_path;
+    art_path.replace(odex_pos, strlen(".odex"), ".art");
+    CHECK_NE(art_path.find(".art"), std::string::npos);
+    return art_path;
+  }
+
+  // Don't know how to handle this.
+  return "";
 }
 
 static bool add_extension_to_file_name(char* file_name, const char* extension) {
@@ -1321,7 +1347,7 @@
     return true;
 }
 
-static int open_output_file(char* file_name, bool recreate, int permissions) {
+static int open_output_file(const char* file_name, bool recreate, int permissions) {
     int flags = O_RDWR | O_CREAT;
     if (recreate) {
         if (unlink(file_name) < 0) {
@@ -1379,19 +1405,110 @@
     return analyse_profiles(uid, pkgname);
 }
 
+static const char* parse_null(const char* arg) {
+    if (strcmp(arg, "!") == 0) {
+        return nullptr;
+    } else {
+        return arg;
+    }
+}
+
+int dexopt(const char* params[DEXOPT_PARAM_COUNT]) {
+    return dexopt(params[0],                    // apk_path
+                  atoi(params[1]),              // uid
+                  params[2],                    // pkgname
+                  params[3],                    // instruction_set
+                  atoi(params[4]),              // dexopt_needed
+                  params[5],                    // oat_dir
+                  atoi(params[6]),              // dexopt_flags
+                  params[7],                    // compiler_filter
+                  parse_null(params[8]),        // volume_uuid
+                  parse_null(params[9]));       // shared_libraries
+    static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
+}
+
+// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
+// on destruction. It will also run the given cleanup (unless told not to) after closing.
+//
+// Usage example:
+//
+//   Dex2oatFileWrapper<std::function<void ()>> file(open(...),
+//                                                   [name]() {
+//                                                       unlink(name.c_str());
+//                                                   });
+//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
+//            wrapper if captured as a reference.
+//
+//   if (file.get() == -1) {
+//       // Error opening...
+//   }
+//
+//   ...
+//   if (error) {
+//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
+//       // and delete the file (after the fd is closed).
+//       return -1;
+//   }
+//
+//   (Success case)
+//   file.SetCleanup(false);
+//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
+//   // (leaving the file around; after the fd is closed).
+//
+template <typename Cleanup>
+class Dex2oatFileWrapper {
+ public:
+    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) {
+    }
+
+    Dex2oatFileWrapper(int value, Cleanup cleanup)
+            : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
+
+    ~Dex2oatFileWrapper() {
+        reset(-1);
+    }
+
+    int get() {
+        return value_;
+    }
+
+    void SetCleanup(bool cleanup) {
+        do_cleanup_ = cleanup;
+    }
+
+    void reset(int new_value) {
+        if (value_ >= 0) {
+            close(value_);
+        }
+        if (do_cleanup_ && cleanup_ != nullptr) {
+            cleanup_();
+        }
+
+        value_ = new_value;
+    }
+
+    void reset(int new_value, Cleanup new_cleanup) {
+        if (value_ >= 0) {
+            close(value_);
+        }
+        if (do_cleanup_ && cleanup_ != nullptr) {
+            cleanup_();
+        }
+
+        value_ = new_value;
+        cleanup_ = new_cleanup;
+    }
+
+ private:
+    int value_;
+    Cleanup cleanup_;
+    bool do_cleanup_;
+};
+
 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* compiler_filter,
            const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries)
 {
-    struct utimbuf ut;
-    struct stat input_stat;
-    char out_path[PKG_PATH_MAX];
-    char swap_file_name[PKG_PATH_MAX];
-    char image_path[PKG_PATH_MAX];
-    const char *input_file;
-    char in_odex_path[PKG_PATH_MAX];
-    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;
@@ -1401,12 +1518,16 @@
     CHECK(pkgname != nullptr);
     CHECK(pkgname[0] != 0);
 
-    fd_t reference_profile_fd = -1;
     // Public apps should not be compiled with profile information ever. Same goes for the special
     // package '*' used for the system server.
+    Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
     if (!is_public && pkgname[0] != '*') {
         // Open reference profile in read only mode as dex2oat does not get write permissions.
-        reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
+        const std::string pkgname_str(pkgname);
+        reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
+                                   [pkgname_str]() {
+                                       clear_reference_profile(pkgname_str.c_str());
+                                   });
         // Note: it's OK to not find a profile here.
     }
 
@@ -1414,10 +1535,13 @@
         LOG_FATAL("dexopt flags contains unknown fields\n");
     }
 
+    char out_path[PKG_PATH_MAX];
     if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
         return false;
     }
 
+    const char *input_file;
+    char in_odex_path[PKG_PATH_MAX];
     switch (dexopt_needed) {
         case DEXOPT_DEX2OAT_NEEDED:
             input_file = apk_path;
@@ -1436,35 +1560,41 @@
 
         default:
             ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            exit(72);
+            return 72;
     }
 
+    struct stat input_stat;
     memset(&input_stat, 0, sizeof(input_stat));
     stat(input_file, &input_stat);
 
-    input_fd = open(input_file, O_RDONLY, 0);
-    if (input_fd < 0) {
+    base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
+    if (input_fd.get() < 0) {
         ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
         return -1;
     }
 
-    out_fd = open_output_file(out_path, /*recreate*/true, /*permissions*/0644);
-    if (out_fd < 0) {
+    const std::string out_path_str(out_path);
+    Dex2oatFileWrapper<std::function<void ()>> out_fd(
+            open_output_file(out_path, /*recreate*/true, /*permissions*/0644),
+            [out_path_str]() { unlink(out_path_str.c_str()); });
+    if (out_fd.get() < 0) {
         ALOGE("installd cannot open '%s' for output during dexopt\n", out_path);
-        goto fail;
+        return -1;
     }
-    if (!set_permissions_and_ownership(out_fd, is_public, uid, out_path)) {
-        goto fail;
+    if (!set_permissions_and_ownership(out_fd.get(), is_public, uid, out_path)) {
+        return -1;
     }
 
     // Create a swap file if necessary.
+    base::unique_fd swap_fd;
     if (ShouldUseSwapFileForDexopt()) {
         // Make sure there really is enough space.
+        char swap_file_name[PKG_PATH_MAX];
         strcpy(swap_file_name, out_path);
         if (add_extension_to_file_name(swap_file_name, ".swap")) {
-            swap_fd = open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600);
+            swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
         }
-        if (swap_fd < 0) {
+        if (swap_fd.get() < 0) {
             // Could not create swap file. Optimistically go on and hope that we can compile
             // without it.
             ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
@@ -1477,116 +1607,108 @@
     }
 
     // Avoid generating an app image for extract only since it will not contain any classes.
-    strcpy(image_path, out_path);
-    trim_extension(image_path);
-    if (add_extension_to_file_name(image_path, ".art")) {
-      char app_image_format[kPropertyValueMax];
-      bool have_app_image_format =
-              get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
-      // Use app images only if it is enabled (by a set image format) and we are compiling
-      // profile-guided (so the app image doesn't conservatively contain all classes).
-      if (profile_guided && have_app_image_format) {
-          // Recreate is true since we do not want to modify a mapped image. If the app is already
-          // running and we modify the image file, it can cause crashes (b/27493510).
-          image_fd = open_output_file(image_path, /*recreate*/true, /*permissions*/0600);
-          if (image_fd < 0) {
-              // Could not create application image file. Go on since we can compile without it.
-              ALOGE("installd could not create '%s' for image file during dexopt\n", image_path);
-          } else if (!set_permissions_and_ownership(image_fd, is_public, uid, image_path)) {
-              image_fd = -1;
-          }
-      }
-      // If we have a valid image file path but no image fd, erase the image file.
-      if (image_fd < 0) {
-          if (unlink(image_path) < 0) {
-              if (errno != ENOENT) {
-                  PLOG(ERROR) << "Couldn't unlink image file " << image_path;
-              }
-          }
-      }
+    Dex2oatFileWrapper<std::function<void ()>> image_fd;
+    const std::string image_path = create_image_filename(out_path);
+    if (!image_path.empty()) {
+        char app_image_format[kPropertyValueMax];
+        bool have_app_image_format =
+                get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+        // Use app images only if it is enabled (by a set image format) and we are compiling
+        // profile-guided (so the app image doesn't conservatively contain all classes).
+        if (profile_guided && have_app_image_format) {
+            // Recreate is true since we do not want to modify a mapped image. If the app is
+            // already running and we modify the image file, it can cause crashes (b/27493510).
+            image_fd.reset(open_output_file(image_path.c_str(),
+                                            true /*recreate*/,
+                                            0600 /*permissions*/),
+                           [image_path]() { unlink(image_path.c_str()); }
+                           );
+            if (image_fd.get() < 0) {
+                // Could not create application image file. Go on since we can compile without
+                // it.
+                LOG(ERROR) << "installd could not create '"
+                        << image_path
+                        << "' for image file during dexopt";
+            } else if (!set_permissions_and_ownership(image_fd.get(),
+                                                      is_public,
+                                                      uid,
+                                                      image_path.c_str())) {
+                image_fd.reset(-1);
+            }
+        }
+        // If we have a valid image file path but no image fd, explicitly erase the image file.
+        if (image_fd.get() < 0) {
+            if (unlink(image_path.c_str()) < 0) {
+                if (errno != ENOENT) {
+                    PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+                }
+            }
+        }
     }
 
     ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
 
-    pid_t pid;
-    pid = fork();
+    pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
         drop_capabilities(uid);
 
         SetDex2OatAndPatchOatScheduling(boot_complete);
-        if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) {
+        if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
             ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno));
-            exit(67);
+            _exit(67);
         }
 
         if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
             || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
-            run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);
+            run_patchoat(input_fd.get(),
+                         out_fd.get(),
+                         input_file,
+                         out_path,
+                         pkgname,
+                         instruction_set);
         } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
             // Pass dex2oat the relative path to the input file.
-            const char *input_file_name = strrchr(input_file, '/');
-            if (input_file_name == NULL) {
-                input_file_name = input_file;
-            } else {
-                input_file_name++;
-            }
-            run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd,
-                        instruction_set, compiler_filter, vm_safe_mode, debuggable, boot_complete,
-                        reference_profile_fd, shared_libraries);
+            const char *input_file_name = get_location_from_path(input_file);
+            run_dex2oat(input_fd.get(),
+                        out_fd.get(),
+                        image_fd.get(),
+                        input_file_name,
+                        out_path,
+                        swap_fd.get(),
+                        instruction_set,
+                        compiler_filter,
+                        vm_safe_mode,
+                        debuggable,
+                        boot_complete,
+                        reference_profile_fd.get(),
+                        shared_libraries);
         } else {
             ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            exit(73);
+            _exit(73);
         }
-        exit(68);   /* only get here on exec failure */
+        _exit(68);   /* only get here on exec failure */
     } else {
-        res = wait_child(pid);
+        int res = wait_child(pid);
         if (res == 0) {
             ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
         } else {
             ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
-            goto fail;
+            return -1;
         }
     }
 
+    struct utimbuf ut;
     ut.actime = input_stat.st_atime;
     ut.modtime = input_stat.st_mtime;
     utime(out_path, &ut);
 
-    close(out_fd);
-    close(input_fd);
-    if (swap_fd >= 0) {
-        close(swap_fd);
-    }
-    if (reference_profile_fd >= 0) {
-        close(reference_profile_fd);
-    }
-    if (image_fd >= 0) {
-        close(image_fd);
-    }
-    return 0;
+    // We've been successful, don't delete output.
+    out_fd.SetCleanup(false);
+    image_fd.SetCleanup(false);
+    reference_profile_fd.SetCleanup(false);
 
-fail:
-    if (out_fd >= 0) {
-        close(out_fd);
-        unlink(out_path);
-    }
-    if (input_fd >= 0) {
-        close(input_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.
-        clear_reference_profile(pkgname);
-    }
-    if (swap_fd >= 0) {
-        close(swap_fd);
-    }
-    if (image_fd >= 0) {
-        close(image_fd);
-    }
-    return -1;
+    return 0;
 }
 
 int mark_boot_complete(const char* instruction_set)
@@ -1922,6 +2044,37 @@
     return true;
 }
 
+// Move/rename a B artifact (from) to an A artifact (to).
+static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
+    // Check whether B exists.
+    {
+        struct stat s;
+        if (stat(b_path.c_str(), &s) != 0) {
+            // Silently ignore for now. The service calling this isn't smart enough to understand
+            // lack of artifacts at the moment.
+            return false;
+        }
+        if (!S_ISREG(s.st_mode)) {
+            LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+            // Try to unlink, but swallow errors.
+            unlink(b_path.c_str());
+            return false;
+        }
+    }
+
+    // Rename B to A.
+    if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
+        // Delete the b_path so we don't try again (or fail earlier).
+        if (unlink(b_path.c_str()) != 0) {
+            PLOG(ERROR) << "Could not unlink " << b_path;
+        }
+
+        return false;
+    }
+
+    return true;
+}
+
 int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
     if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
         LOG(ERROR) << "Cannot move_ab with null input";
@@ -1940,37 +2093,35 @@
     if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
         return -1;
     }
+    const std::string a_image_path = create_image_filename(a_path);
 
     // B path = A path + ".b"
-    std::string b_path = StringPrintf("%s.b", a_path);
+    const std::string b_path = StringPrintf("%s.b", a_path);
+    const std::string b_image_path = StringPrintf("%s.b", a_image_path.c_str());
 
-    // Check whether B exists.
-    {
-        struct stat s;
-        if (stat(b_path.c_str(), &s) != 0) {
-            // Silently ignore for now. The service calling this isn't smart enough to understand
-            // lack of artifacts at the moment.
-            return -1;
+    bool oat_success = move_ab_path(b_path, a_path);
+    bool success;
+
+    if (oat_success) {
+        // Note: we can live without an app image. As such, ignore failure to move the image file.
+        //       If we decide to require the app image, or the app image being moved correctly,
+        //       then change accordingly.
+        constexpr bool kIgnoreAppImageFailure = true;
+
+        bool art_success = true;
+        if (!a_image_path.empty()) {
+            art_success = move_ab_path(b_image_path, a_image_path);
         }
-        if (!S_ISREG(s.st_mode)) {
-            LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
-            // Try to unlink, but swallow errors.
-            unlink(b_path.c_str());
-            return -1;
-        }
+
+        success = art_success || kIgnoreAppImageFailure;
+    } else {
+        // Cleanup: delete B image, ignore errors.
+        unlink(b_image_path.c_str());
+
+        success = false;
     }
 
-    // Rename B to A.
-    if (!unlink_and_rename(b_path.c_str(), a_path)) {
-        // Delete the b_path so we don't try again (or fail earlier).
-        if (unlink(b_path.c_str()) != 0) {
-            PLOG(ERROR) << "Could not unlink " << b_path;
-        }
-
-        return -1;
-    }
-
-    return 0;
+    return success ? 0 : -1;
 }
 
 }  // namespace installd
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index 7a42c5c..c0c39c5 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -56,9 +56,21 @@
 
 bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files);
 
-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* compiler_filter,
-           const char* volume_uuid, const char* shared_libraries);
+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* compiler_filter,
+           const char* volume_uuid,
+           const char* shared_libraries);
+static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
+
+// Helper for the above, converting arguments.
+int dexopt(const char* params[DEXOPT_PARAM_COUNT]);
+
 int mark_boot_complete(const char *instruction_set);
 int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
 int idmap(const char *target_path, const char *overlay_path, uid_t uid);
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 061359e..9d2f71b 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -219,7 +219,8 @@
 // We use otapreopt_chroot to get into the chroot.
 static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
 
-static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
+                         char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
     // Time to fork and run otapreopt.
 
     // Check that the tool exists.
@@ -231,12 +232,14 @@
 
     pid_t pid = fork();
     if (pid == 0) {
-        const char* argv[1 + 9 + 1];
+        const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
         argv[0] = kOtaPreopt;
-        for (size_t i = 1; i <= 9; ++i) {
-            argv[i] = arg[i - 1];
+
+        for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
+            argv[i + 1] = args[i];
         }
-        argv[10] = nullptr;
+
+        argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
 
         execv(argv[0], (char * const *)argv);
         PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
@@ -252,22 +255,30 @@
     }
 }
 
+static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
+                             char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+    return dexopt(args);
+}
+
+using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
+                         char reply[REPLY_MAX]);
+
 static int do_dexopt(char **arg, char reply[REPLY_MAX])
 {
-    int dexopt_flags = atoi(arg[6]);
-    if ((dexopt_flags & DEXOPT_OTA) != 0) {
-      return do_ota_dexopt(arg, reply);
+    const char* args[DEXOPT_PARAM_COUNT];
+    for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
+        CHECK(arg[i] != nullptr);
+        args[i] = arg[i];
     }
-    return dexopt(arg[0],                      // apk_path
-                  atoi(arg[1]),                // uid
-                  arg[2],                      // pkgname
-                  arg[3],                      // instruction_set
-                  atoi(arg[4]),                // dexopt_needed
-                  arg[5],                      // oat_dir
-                  dexopt_flags,
-                  arg[7],                      // compiler_filter
-                  parse_null(arg[8]),          // volume_uuid
-                  parse_null(arg[9]));         // shared_libraries
+
+    int dexopt_flags = atoi(arg[6]);
+    DexoptFn dexopt_fn;
+    if ((dexopt_flags & DEXOPT_OTA) != 0) {
+        dexopt_fn = do_ota_dexopt;
+    } else {
+        dexopt_fn = do_regular_dexopt;
+    }
+    return dexopt_fn(args, reply);
 }
 
 static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 8513695..823b8ee 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -21,6 +21,8 @@
 namespace android {
 namespace installd {
 
+constexpr size_t DEXOPT_PARAM_COUNT = 10U;
+
 /* elements combined with a valid package name to form paths */
 
 constexpr const char* PRIMARY_USER_PREFIX = "data/";
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index ac511ec..e1cfc9d 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -30,6 +30,7 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/fs.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
@@ -39,7 +40,6 @@
 #include <file_parsing.h>
 #include <globals.h>
 #include <installd_deps.h>  // Need to fill in requirements of commands.
-#include <string_helpers.h>
 #include <system_properties.h>
 #include <utils.h>
 
@@ -51,6 +51,10 @@
 #define TOKEN_MAX     16    /* max number of arguments in buffer */
 #define REPLY_MAX     256   /* largest reply allowed */
 
+using android::base::EndsWith;
+using android::base::Join;
+using android::base::Split;
+using android::base::StartsWith;
 using android::base::StringPrintf;
 
 namespace android {
@@ -188,12 +192,14 @@
 
     bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
         size_t index = 0;
-        while (index < ARRAY_SIZE(package_parameters_) &&
+        static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_),
+                      "Unexpected dexopt param count");
+        while (index < DEXOPT_PARAM_COUNT &&
                 argv[index + 1] != nullptr) {
             package_parameters_[index] = argv[index + 1];
             index++;
         }
-        if (index != ARRAY_SIZE(package_parameters_)) {
+        if (index != ARRAY_SIZE(package_parameters_) || argv[index + 1] != nullptr) {
             LOG(ERROR) << "Wrong number of parameters";
             return false;
         }
@@ -295,7 +301,7 @@
         std::vector<std::string> cmd;
         cmd.push_back("/system/bin/dex2oat");
         cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
-        for (const std::string& boot_part : Split(boot_cp, ':')) {
+        for (const std::string& boot_part : Split(boot_cp, ":")) {
             cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
         }
         cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
@@ -324,7 +330,7 @@
         const std::string* extra_opts =
                 system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
         if (extra_opts != nullptr) {
-            std::vector<std::string> extra_vals = Split(*extra_opts, ' ');
+            std::vector<std::string> extra_vals = Split(*extra_opts, " ");
             cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
         }
         // TODO: Should we lower this? It's usually set close to max, because
@@ -357,17 +363,50 @@
     }
 
     int RunPreopt() {
-        int ret = dexopt(package_parameters_[0],          // apk_path
-                atoi(package_parameters_[1]),             // uid
-                package_parameters_[2],                   // pkgname
-                package_parameters_[3],                   // instruction_set
-                atoi(package_parameters_[4]),             // dexopt_needed
-                package_parameters_[5],                   // oat_dir
-                atoi(package_parameters_[6]),             // dexopt_flags
-                package_parameters_[7],                   // compiler_filter
-                ParseNull(package_parameters_[8]),        // volume_uuid
-                ParseNull(package_parameters_[9]));       // shared_libraries
-        return ret;
+        // Run the preopt.
+        //
+        // There's one thing we have to be careful about: we may/will be asked to compile an app
+        // living in the system image. This may be a valid request - if the app wasn't compiled,
+        // e.g., if the system image wasn't large enough to include preopted files. However, the
+        // data we have is from the old system, so the driver (the OTA service) can't actually
+        // know. Thus, we will get requests for apps that have preopted components. To avoid
+        // duplication (we'd generate files that are not used and are *not* cleaned up), do two
+        // simple checks:
+        //
+        // 1) Does the apk_path start with the value of ANDROID_ROOT? (~in the system image)
+        //    (For simplicity, assume the value of ANDROID_ROOT does not contain a symlink.)
+        //
+        // 2) If you replace the name in the apk_path with "oat," does the path exist?
+        //    (=have a subdirectory for preopted files)
+        //
+        // If the answer to both is yes, skip the dexopt.
+        //
+        // Note: while one may think it's OK to call dexopt and it will fail (because APKs should
+        //       be stripped), that's not true for APKs signed outside the build system (so the
+        //       jar content must be exactly the same).
+
+        //       (This is ugly as it's the only thing where we need to understand the contents
+        //        of package_parameters_, but it beats postponing the decision or using the call-
+        //        backs to do weird things.)
+        constexpr size_t kApkPathIndex = 0;
+        CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex);
+        CHECK(package_parameters_[kApkPathIndex] != nullptr);
+        CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr);
+        if (StartsWith(package_parameters_[kApkPathIndex],
+                       system_properties_.GetProperty(kAndroidRootPathPropertyName)->c_str())) {
+            const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/');
+            if (last_slash != nullptr) {
+                std::string path(package_parameters_[kApkPathIndex],
+                                 last_slash - package_parameters_[kApkPathIndex] + 1);
+                CHECK(EndsWith(path, "/"));
+                path = path + "oat";
+                if (access(path.c_str(), F_OK) == 0) {
+                    return 0;
+                }
+            }
+        }
+
+        return dexopt(package_parameters_);
     }
 
     ////////////////////////////////////
@@ -376,7 +415,7 @@
 
     // Wrapper on fork/execv to run a command in a subprocess.
     bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
-        const std::string command_line(Join(arg_vector, ' '));
+        const std::string command_line = Join(arg_vector, ' ');
 
         CHECK_GE(arg_vector.size(), 1U) << command_line;
 
@@ -484,7 +523,7 @@
     // to compile, instead of the A properties we could get from init/get_property.
     SystemProperties system_properties_;
 
-    const char* package_parameters_[10];
+    const char* package_parameters_[DEXOPT_PARAM_COUNT];
 
     // Store environment values we need to set.
     std::vector<std::string> environ_;
@@ -497,7 +536,6 @@
 ////////////////////////
 
 int get_property(const char *key, char *value, const char *default_value) {
-    // TODO: Replace with system-properties map.
     return gOps.GetProperty(key, value, default_value);
 }
 
@@ -505,9 +543,8 @@
 bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir,
                              const char *apk_path,
                              const char *instruction_set) {
-    // TODO: Insert B directory.
-    char *file_name_start;
-    char *file_name_end;
+    const char *file_name_start;
+    const char *file_name_end;
 
     file_name_start = strrchr(apk_path, '/');
     if (file_name_start == nullptr) {
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index f7f69a9..be0ff2e 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -22,6 +22,8 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
+#include <installd_constants.h>
+
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
 #endif
@@ -78,13 +80,13 @@
 
     // Now go on and run otapreopt.
 
-    const char* argv[1 + 9 + 1];
-    CHECK_EQ(argc, 10);
+    const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
+    CHECK_EQ(static_cast<size_t>(argc), DEXOPT_PARAM_COUNT + 1);
     argv[0] = "/system/bin/otapreopt";
-    for (size_t i = 1; i <= 9; ++i) {
+    for (size_t i = 1; i <= DEXOPT_PARAM_COUNT; ++i) {
         argv[i] = arg[i];
     }
-    argv[10] = nullptr;
+    argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
 
     execv(argv[0], (char * const *)argv);
     PLOG(ERROR) << "execv(OTAPREOPT) failed.";
diff --git a/cmds/installd/string_helpers.h b/cmds/installd/string_helpers.h
deleted file mode 100644
index e8fcdef..0000000
--- a/cmds/installd/string_helpers.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_OTAPREOPT_STRING_HELPERS_H_
-#define ART_OTAPREOPT_STRING_HELPERS_H_
-
-#include <sstream>
-#include <string>
-
-#include <android-base/macros.h>
-
-namespace android {
-namespace installd {
-
-static inline bool StringStartsWith(const std::string& target,
-                                    const char* prefix) {
-    return target.compare(0, strlen(prefix), prefix) == 0;
-}
-
-// Split the input according to the separator character. Doesn't honor quotation.
-static inline std::vector<std::string> Split(const std::string& in, const char separator) {
-    if (in.empty()) {
-        return std::vector<std::string>();
-    }
-
-    std::vector<std::string> ret;
-    std::stringstream strstr(in);
-    std::string token;
-
-    while (std::getline(strstr, token, separator)) {
-        ret.push_back(token);
-    }
-
-    return ret;
-}
-
-template <typename StringT>
-static inline std::string Join(const std::vector<StringT>& strings, char separator) {
-    if (strings.empty()) {
-        return "";
-    }
-
-    std::string result(strings[0]);
-    for (size_t i = 1; i < strings.size(); ++i) {
-        result += separator;
-        result += strings[i];
-    }
-    return result;
-}
-
-}  // namespace installd
-}  // namespace android
-
-#endif  // ART_OTAPREOPT_STRING_HELPERS_H_
diff --git a/docs/Doxyfile b/docs/Doxyfile
index 46d6d84..3ea453f 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -677,7 +677,7 @@
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = ../include/android
+INPUT                  = ../include/android ../../av/include/ndk ../../av/include/camera/ndk
 
 # This tag can be used to specify the character encoding of the source files 
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
diff --git a/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png b/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
new file mode 100644
index 0000000..7578b48
--- /dev/null
+++ b/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
new file mode 100644
index 0000000..7b10f6b
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
new file mode 100644
index 0000000..41972cf
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
new file mode 100644
index 0000000..d26600b
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
new file mode 100644
index 0000000..1e7208e
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
new file mode 100644
index 0000000..ecef3ae
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
new file mode 100644
index 0000000..a02fd89
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
new file mode 100644
index 0000000..c309ac5
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
new file mode 100644
index 0000000..414fad4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
new file mode 100644
index 0000000..c147a87
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
new file mode 100644
index 0000000..4ce2125
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
Binary files differ
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 2685bcc..1c355c4 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -705,9 +705,9 @@
     }
 
     setDataPosition(start);
-    val->reset(new std::vector<T>());
+    val->reset(new std::vector<std::unique_ptr<T>>());
 
-    status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable);
+    status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>);
 
     if (status != OK) {
         val->reset();
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index 73f923c..312e02f 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -140,6 +140,8 @@
             const sp<IBinder>& handle, uint64_t frameNumber);
     status_t    setOverrideScalingMode(const sp<IBinder>& id,
             int32_t overrideScalingMode);
+    status_t    setPositionAppliesWithResize(const sp<IBinder>& id);
+
     status_t    destroySurface(const sp<IBinder>& id);
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index bedebb6..fafd194 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -73,6 +73,11 @@
     status_t    setCrop(const Rect& crop);
     status_t    setFinalCrop(const Rect& crop);
 
+    // If the size changes in this transaction, position updates specified
+    // in this transaction will not complete until a buffer of the new size
+    // arrives.
+    status_t    setPositionAppliesWithResize();
+
     // Defers applying any changes made in this transaction until the Layer
     // identified by handle reaches the given frameNumber
     status_t deferTransactionUntil(sp<IBinder> handle, uint64_t frameNumber);
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 92d31d1..4885e05 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -54,7 +54,8 @@
         eCropChanged                = 0x00000100,
         eDeferTransaction           = 0x00000200,
         eFinalCropChanged           = 0x00000400,
-        eOverrideScalingModeChanged = 0x00000800
+        eOverrideScalingModeChanged = 0x00000800,
+        ePositionAppliesWithResize  = 0x00001000,
     };
 
     layer_state_t()
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index ac7ac07..3a0a283 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -444,14 +444,6 @@
 
         mSlots[found].mBufferState.dequeue();
 
-        // If shared buffer mode has just been enabled, cache the slot of the
-        // first buffer that is dequeued and mark it as the shared buffer.
-        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
-                BufferQueueCore::INVALID_BUFFER_SLOT) {
-            mCore->mSharedBufferSlot = found;
-            mSlots[found].mBufferState.mShared = true;
-        }
-
         if ((buffer == NULL) ||
                 buffer->needsReallocation(width, height, format, usage))
         {
@@ -483,9 +475,21 @@
 
         eglDisplay = mSlots[found].mEglDisplay;
         eglFence = mSlots[found].mEglFence;
-        *outFence = mSlots[found].mFence;
+        // Don't return a fence in shared buffer mode, except for the first
+        // frame.
+        *outFence = (mCore->mSharedBufferMode &&
+                mCore->mSharedBufferSlot == found) ?
+                Fence::NO_FENCE : mSlots[found].mFence;
         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
         mSlots[found].mFence = Fence::NO_FENCE;
+
+        // If shared buffer mode has just been enabled, cache the slot of the
+        // first buffer that is dequeued and mark it as the shared buffer.
+        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
+                BufferQueueCore::INVALID_BUFFER_SLOT) {
+            mCore->mSharedBufferSlot = found;
+            mSlots[found].mBufferState.mShared = true;
+        }
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 85c194d..4739ca4 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -195,6 +195,9 @@
     ANativeWindowBuffer* buf;
     int fenceFd = -1;
     int result = c->dequeueBuffer(&buf, &fenceFd);
+    if (result != OK) {
+        return result;
+    }
     sp<Fence> fence(new Fence(fenceFd));
     int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
     if (waitResult != OK) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e33cc37..92ae41e 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -165,6 +165,8 @@
             uint64_t frameNumber);
     status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, int32_t overrideScalingMode);
+    status_t setPositionAppliesWithResize(const sp<SurfaceComposerClient>& client,
+            const sp<IBinder>& id);
 
     void setDisplaySurface(const sp<IBinder>& token,
             const sp<IGraphicBufferProducer>& bufferProducer);
@@ -443,6 +445,18 @@
     return NO_ERROR;
 }
 
+status_t Composer::setPositionAppliesWithResize(
+        const sp<SurfaceComposerClient>& client,
+        const sp<IBinder>& id) {
+    Mutex::Autolock lock(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s) {
+        return BAD_INDEX;
+    }
+    s->what |= layer_state_t::ePositionAppliesWithResize;
+    return NO_ERROR;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) {
@@ -685,6 +699,11 @@
             this, id, overrideScalingMode);
 }
 
+status_t SurfaceComposerClient::setPositionAppliesWithResize(
+        const sp<IBinder>& id) {
+    return getComposer().setPositionAppliesWithResize(this, id);
+}
+
 // ----------------------------------------------------------------------------
 
 void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 314d83a..4671e50 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -112,6 +112,11 @@
     if (err < 0) return err;
     return mClient->setPosition(mHandle, x, y);
 }
+status_t SurfaceControl::setPositionAppliesWithResize() {
+    status_t err = validate();
+    if (err < 0) return err;
+    return mClient->setPositionAppliesWithResize(mHandle);
+}
 status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
     status_t err = validate();
     if (err < 0) return err;
diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h
index d23f435..18ef7d5 100644
--- a/opengl/libagl/context.h
+++ b/opengl/libagl/context.h
@@ -26,7 +26,8 @@
 #endif
 
 #include <private/pixelflinger/ggl_context.h>
-#include <hardware/gralloc.h>
+
+#include <system/window.h>
 
 #include <GLES/gl.h>
 #include <GLES/glext.h>
@@ -615,7 +616,7 @@
     culling_t               cull;
     lighting_t              lighting;
     user_clip_planes_t      clipPlanes;
-    compute_iterators_t     lerp;           __attribute__((aligned(32)));
+    compute_iterators_t     lerp           __attribute__((aligned(32)));
     vertex_t                current;
     vec4_t                  currentColorClamped;
     vec3_t                  currentNormal;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 92139e9..c1efd1c 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -32,6 +32,7 @@
 #include <utils/threads.h>
 #include <ui/ANativeObjectBase.h>
 #include <ui/Fence.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -242,7 +243,6 @@
     ANativeWindow*   nativeWindow;
     ANativeWindowBuffer*   buffer;
     ANativeWindowBuffer*   previousBuffer;
-    gralloc_module_t const*    module;
     int width;
     int height;
     void* bits;
@@ -341,16 +341,12 @@
         EGLConfig config,
         int32_t depthFormat,
         ANativeWindow* window)
-    : egl_surface_t(dpy, config, depthFormat), 
-    nativeWindow(window), buffer(0), previousBuffer(0), module(0),
-    bits(NULL)
+    : egl_surface_t(dpy, config, depthFormat),
+    nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL)
 {
-    hw_module_t const* pModule;
-    hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
-    module = reinterpret_cast<gralloc_module_t const*>(pModule);
 
     pixelFormatTable = gglGetPixelFormatTable();
-    
+
     // keep a reference on the window
     nativeWindow->common.incRef(&nativeWindow->common);
     nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width);
@@ -440,22 +436,16 @@
 status_t egl_window_surface_v2_t::lock(
         ANativeWindowBuffer* buf, int usage, void** vaddr)
 {
-    int err;
-
-    err = module->lock(module, buf->handle,
-            usage, 0, 0, buf->width, buf->height, vaddr);
-
-    return err;
+    auto& mapper = GraphicBufferMapper::get();
+    return mapper.lock(buf->handle, usage,
+            android::Rect(buf->width, buf->height), vaddr);
 }
 
 status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf)
 {
     if (!buf) return BAD_VALUE;
-    int err = NO_ERROR;
-
-    err = module->unlock(module, buf->handle);
-
-    return err;
+    auto& mapper = GraphicBufferMapper::get();
+    return mapper.unlock(buf->handle);
 }
 
 void egl_window_surface_v2_t::copyBlt(
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 479bf7e..e7fe9d7 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -229,7 +229,7 @@
 #endif
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
     }
-    const vec4_t eyeViewer = { 0, 0, 0x10000, 0 };
+    const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}};
 #if OBJECT_SPACE_LIGHTING
     c->transforms.mvui.point3(&c->transforms.mvui,
             &c->lighting.objViewer, &eyeViewer);
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index cdeccb3..034c857 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -253,13 +253,13 @@
 {
     GLfloat const* const m = lhs.m;
     for (int i=0 ; i<4 ; i++) {
-        register const float rhs_i0 = rhs.m[ I(i,0) ];
-        register float ri0 = m[ I(0,0) ] * rhs_i0;
-        register float ri1 = m[ I(0,1) ] * rhs_i0;
-        register float ri2 = m[ I(0,2) ] * rhs_i0;
-        register float ri3 = m[ I(0,3) ] * rhs_i0;
+        const float rhs_i0 = rhs.m[ I(i,0) ];
+        float ri0 = m[ I(0,0) ] * rhs_i0;
+        float ri1 = m[ I(0,1) ] * rhs_i0;
+        float ri2 = m[ I(0,2) ] * rhs_i0;
+        float ri3 = m[ I(0,3) ] * rhs_i0;
         for (int j=1 ; j<4 ; j++) {
-            register const float rhs_ij = rhs.m[ I(i,j) ];
+            const float rhs_ij = rhs.m[ I(i,j) ];
             ri0 += m[ I(j,0) ] * rhs_ij;
             ri1 += m[ I(j,1) ] * rhs_ij;
             ri2 += m[ I(j,2) ] * rhs_ij;
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 9aa1c4f..3fe5ed0 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -25,6 +25,9 @@
 
 #include <ETC1/etc1.h>
 
+#include <ui/GraphicBufferMapper.h>
+#include <ui/Rect.h>
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -128,17 +131,11 @@
             ANativeWindowBuffer* native_buffer = u.texture->buffer;
             if (native_buffer) {
                 c->rasterizer.procs.activeTexture(c, i);
-                hw_module_t const* pModule;
-                if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
-                    continue;
 
-                gralloc_module_t const* module =
-                    reinterpret_cast<gralloc_module_t const*>(pModule);
-
+                auto& mapper = GraphicBufferMapper::get();
                 void* vaddr;
-                int err = module->lock(module, native_buffer->handle,
-                        GRALLOC_USAGE_SW_READ_OFTEN,
-                        0, 0, native_buffer->width, native_buffer->height,
+                mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN,
+                        Rect(native_buffer->width, native_buffer->height),
                         &vaddr);
 
                 u.texture->setImageBits(vaddr);
@@ -156,14 +153,10 @@
             ANativeWindowBuffer* native_buffer = u.texture->buffer;
             if (native_buffer) {
                 c->rasterizer.procs.activeTexture(c, i);
-                hw_module_t const* pModule;
-                if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
-                    continue;
 
-                gralloc_module_t const* module =
-                    reinterpret_cast<gralloc_module_t const*>(pModule);
+                auto& mapper = GraphicBufferMapper::get();
+                mapper.unlock(native_buffer->handle);
 
-                module->unlock(module, native_buffer->handle);
                 u.texture->setImageBits(NULL);
                 c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
             }
@@ -405,7 +398,7 @@
     return 0;
 }
 
-static size_t dataSizePalette4(int numLevels, int width, int height, int format)
+static GLsizei dataSizePalette4(int numLevels, int width, int height, int format)
 {
     int indexBits = 8;
     int entrySize = 0;
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 00bfc24..24394a9 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1794,9 +1794,9 @@
     uint32_t blue_size = 0;
     uint32_t alpha_size = 0;
 
-#define GET_POSITIVE_VALUE(case_name, target) \
+#define GET_NONNEGATIVE_VALUE(case_name, target) \
     case case_name: \
-        if (value > 0) { \
+        if (value >= 0) { \
             target = value; \
         } else { \
             return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); \
@@ -1808,12 +1808,12 @@
             GLint attr = *attrib_list++;
             GLint value = *attrib_list++;
             switch (attr) {
-                GET_POSITIVE_VALUE(EGL_WIDTH, width);
-                GET_POSITIVE_VALUE(EGL_HEIGHT, height);
-                GET_POSITIVE_VALUE(EGL_RED_SIZE, red_size);
-                GET_POSITIVE_VALUE(EGL_GREEN_SIZE, green_size);
-                GET_POSITIVE_VALUE(EGL_BLUE_SIZE, blue_size);
-                GET_POSITIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
+                GET_NONNEGATIVE_VALUE(EGL_WIDTH, width);
+                GET_NONNEGATIVE_VALUE(EGL_HEIGHT, height);
+                GET_NONNEGATIVE_VALUE(EGL_RED_SIZE, red_size);
+                GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
+                GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
+                GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
                 case EGL_NATIVE_BUFFER_USAGE_ANDROID:
                     if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_PROTECTED;
@@ -1836,7 +1836,7 @@
             }
         }
     }
-#undef GET_POSITIVE_VALUE
+#undef GET_NONNEGATIVE_VALUE
 
     // Validate format.
     if (red_size == 8 && green_size == 8 && blue_size == 8) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 15e5615..d732c07 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -95,7 +95,8 @@
         mQueueItems(),
         mLastFrameNumberReceived(0),
         mUpdateTexImageFailed(false),
-        mAutoRefresh(false)
+        mAutoRefresh(false),
+        mFreezePositionUpdates(false)
 {
 #ifdef USE_HWC2
     ALOGV("Creating Layer %s", name.string());
@@ -1416,11 +1417,9 @@
                 c.requested.w, c.requested.h);
     }
 
+    const bool resizePending = (c.requested.w != c.active.w) ||
+            (c.requested.h != c.active.h);
     if (!isFixedSize()) {
-
-        const bool resizePending = (c.requested.w != c.active.w) ||
-                                   (c.requested.h != c.active.h);
-
         if (resizePending && mSidebandStream == NULL) {
             // don't let Layer::doTransaction update the drawing state
             // if we have a pending resize, unless we are in fixed-size mode.
@@ -1443,7 +1442,17 @@
     // this is used by Layer, which special cases resizes.
     if (flags & eDontUpdateGeometryState)  {
     } else {
-        c.active = c.requested;
+        Layer::State& editCurrentState(getCurrentState());
+        if (mFreezePositionUpdates) {
+            float tx = c.active.transform.tx();
+            float ty = c.active.transform.ty();
+            c.active = c.requested;
+            c.active.transform.set(tx, ty);
+            editCurrentState.active = c.active;
+        } else {
+            editCurrentState.active = editCurrentState.requested;
+            c.active = c.requested;
+        }
     }
 
     if (s.active != c.active) {
@@ -1490,7 +1499,7 @@
     return android_atomic_or(flags, &mTransactionFlags);
 }
 
-bool Layer::setPosition(float x, float y) {
+bool Layer::setPosition(float x, float y, bool immediate) {
     if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y)
         return false;
     mCurrentState.sequence++;
@@ -1499,12 +1508,16 @@
     // we want to apply the position portion of the transform matrix immediately,
     // but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
     mCurrentState.requested.transform.set(x, y);
-    mCurrentState.active.transform.set(x, y);
+    if (immediate && !mFreezePositionUpdates) {
+        mCurrentState.active.transform.set(x, y);
+    }
+    mFreezePositionUpdates = mFreezePositionUpdates || !immediate;
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
+
 bool Layer::setLayer(uint32_t z) {
     if (mCurrentState.z == z)
         return false;
@@ -1584,6 +1597,7 @@
     if (scalingMode == mOverrideScalingMode)
         return false;
     mOverrideScalingMode = scalingMode;
+    setTransactionFlags(eTransactionNeeded);
     return true;
 }
 
@@ -2005,6 +2019,7 @@
             if (bufWidth != uint32_t(oldActiveBuffer->width) ||
                 bufHeight != uint32_t(oldActiveBuffer->height)) {
                 recomputeVisibleRegions = true;
+                mFreezePositionUpdates = false;
             }
         }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fef2d98..4257c37 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -145,7 +145,7 @@
     status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
 
     // modify current state
-    bool setPosition(float x, float y);
+    bool setPosition(float x, float y, bool immediate);
     bool setLayer(uint32_t z);
     bool setSize(uint32_t w, uint32_t h);
 #ifdef USE_HWC2
@@ -604,6 +604,7 @@
     bool mUpdateTexImageFailed; // This is only modified from the main thread
 
     bool mAutoRefresh;
+    bool mFreezePositionUpdates;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0dc04eb..9f357df 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2247,9 +2247,12 @@
     sp<Layer> layer(client->getLayerUser(s.surface));
     if (layer != 0) {
         const uint32_t what = s.what;
+        bool positionAppliesWithResize =
+                what & layer_state_t::ePositionAppliesWithResize;
         if (what & layer_state_t::ePositionChanged) {
-            if (layer->setPosition(s.x, s.y))
+            if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
                 flags |= eTraversalNeeded;
+            }
         }
         if (what & layer_state_t::eLayerChanged) {
             // NOTE: index needs to be calculated before we update the state
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index f4061e7..a86c692 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -2263,9 +2263,12 @@
     sp<Layer> layer(client->getLayerUser(s.surface));
     if (layer != 0) {
         const uint32_t what = s.what;
+        bool positionAppliesWithResize =
+                what & layer_state_t::ePositionAppliesWithResize;
         if (what & layer_state_t::ePositionChanged) {
-            if (layer->setPosition(s.x, s.y))
+            if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
                 flags |= eTraversalNeeded;
+            }
         }
         if (what & layer_state_t::eLayerChanged) {
             // NOTE: index needs to be calculated before we update the state
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 320fddb..f8d4d13 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -66,6 +66,8 @@
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         sp<IBinder> display(sf->getBuiltInDisplay(
                 ISurfaceComposer::eDisplayIdMain));
+        SurfaceComposerClient::openGlobalTransaction();
+        SurfaceComposerClient::closeGlobalTransaction(true);
         ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 0, 0,
                 0, INT_MAX, false));
         *sc = new ScreenCapture(cpuConsumer);
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index ff00f32..0a1dda2 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -128,7 +128,6 @@
     INIT_PROC(instance, GetPhysicalDeviceFormatProperties);
     INIT_PROC(instance, GetPhysicalDeviceImageFormatProperties);
     INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceLayerProperties);
     INIT_PROC(instance, EnumerateDeviceExtensionProperties);
     INIT_PROC(instance, GetPhysicalDeviceSparseImageFormatProperties);
     INIT_PROC_EXT(KHR_surface, instance, DestroySurfaceKHR);
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 779b654..7f8d274 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -38,7 +38,6 @@
     PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
     PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
     PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
     PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 307f0e4..f9a4670 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -434,7 +434,10 @@
   {{AssertType $ "Function"}}
 
   {{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}}
-    true
+    {{/* deprecated and unused internally */}}
+    {{if not (eq $.Name "vkEnumerateDeviceLayerProperties")}}
+      true
+    {{end}}
   {{end}}
 {{end}}
 
@@ -938,8 +941,6 @@
     {{else if eq $.Name "vkDestroyInstance"}}true
     {{else if eq $.Name "vkDestroyDevice"}}true
 
-    {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
-
     {{/* Enumeration of extensions */}}
     {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index eabbf1f..2555272 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -124,7 +124,7 @@
 Hal Hal::hal_;
 
 bool Hal::Open() {
-    ALOG_ASSERT(!dev_, "OpenHAL called more than once");
+    ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
 
     // Use a stub device unless we successfully open a real HAL device.
     hal_.dev_ = &stubhal::kDevice;
@@ -797,6 +797,7 @@
 
         return VK_ERROR_INCOMPATIBLE_DRIVER;
     }
+    data->driver_device = dev;
 
     *pDevice = dev;
 
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 210c3c7..a02ebd7 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,6 +98,7 @@
 
     std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
 
+    VkDevice driver_device;
     DeviceDriverTable driver;
 };
 
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 29351a1..d979a34 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -328,7 +328,6 @@
     INIT_PROC(instance, EnumeratePhysicalDevices);
     INIT_PROC(instance, GetInstanceProcAddr);
     INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceLayerProperties);
     INIT_PROC(instance, EnumerateDeviceExtensionProperties);
     INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
     INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index ca17d57..a60b2fe 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -58,7 +58,6 @@
     PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
     PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
     PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
     PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c3d71d5..adc7d5c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -109,6 +109,7 @@
 
 struct Surface {
     android::sp<ANativeWindow> window;
+    VkSwapchainKHR swapchain_handle;
 };
 
 VkSurfaceKHR HandleFromSurface(Surface* surface) {
@@ -147,6 +148,65 @@
     return reinterpret_cast<Swapchain*>(handle);
 }
 
+void ReleaseSwapchainImage(VkDevice device,
+                           ANativeWindow* window,
+                           int release_fence,
+                           Swapchain::Image& image) {
+    ALOG_ASSERT(release_fence == -1 || image.dequeued,
+                "ReleaseSwapchainImage: can't provide a release fence for "
+                "non-dequeued images");
+
+    if (image.dequeued) {
+        if (release_fence >= 0) {
+            // We get here from vkQueuePresentKHR. The application is
+            // responsible for creating an execution dependency chain from
+            // vkAcquireNextImage (dequeue_fence) to vkQueuePresentKHR
+            // (release_fence), so we can drop the dequeue_fence here.
+            if (image.dequeue_fence >= 0)
+                close(image.dequeue_fence);
+        } else {
+            // We get here during swapchain destruction, or various serious
+            // error cases e.g. when we can't create the release_fence during
+            // vkQueuePresentKHR. In non-error cases, the dequeue_fence should
+            // have already signalled, since the swapchain images are supposed
+            // to be idle before the swapchain is destroyed. In error cases,
+            // there may be rendering in flight to the image, but since we
+            // weren't able to create a release_fence, waiting for the
+            // dequeue_fence is about the best we can do.
+            release_fence = image.dequeue_fence;
+        }
+        image.dequeue_fence = -1;
+
+        if (window) {
+            window->cancelBuffer(window, image.buffer.get(), release_fence);
+        } else {
+            if (release_fence >= 0) {
+                sync_wait(release_fence, -1 /* forever */);
+                close(release_fence);
+            }
+        }
+
+        image.dequeued = false;
+    }
+
+    if (image.image) {
+        GetData(device).driver.DestroyImage(device, image.image, nullptr);
+        image.image = VK_NULL_HANDLE;
+    }
+
+    image.buffer.clear();
+}
+
+void OrphanSwapchain(VkDevice device, Swapchain* swapchain) {
+    if (swapchain->surface.swapchain_handle != HandleFromSwapchain(swapchain))
+        return;
+    for (uint32_t i = 0; i < swapchain->num_images; i++) {
+        if (!swapchain->images[i].dequeued)
+            ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+    }
+    swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+}
+
 }  // anonymous namespace
 
 VKAPI_ATTR
@@ -165,6 +225,7 @@
     Surface* surface = new (mem) Surface;
 
     surface->window = pCreateInfo->window;
+    surface->swapchain_handle = VK_NULL_HANDLE;
 
     // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
     int err =
@@ -191,6 +252,11 @@
     if (!surface)
         return;
     native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
+    ALOGV_IF(surface->swapchain_handle != VK_NULL_HANDLE,
+             "destroyed VkSurfaceKHR 0x%" PRIx64
+             " has active VkSwapchainKHR 0x%" PRIx64,
+             reinterpret_cast<uint64_t>(surface_handle),
+             reinterpret_cast<uint64_t>(surface->swapchain_handle));
     surface->~Surface();
     if (!allocator)
         allocator = &GetData(instance).allocator;
@@ -344,29 +410,53 @@
         allocator = &GetData(device).allocator;
 
     ALOGV_IF(create_info->imageArrayLayers != 1,
-             "Swapchain imageArrayLayers (%u) != 1 not supported",
+             "swapchain imageArrayLayers=%u not supported",
              create_info->imageArrayLayers);
-
-    ALOGE_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
-             "color spaces other than SRGB_NONLINEAR not yet implemented");
-    ALOGE_IF(create_info->oldSwapchain,
-             "swapchain re-creation not yet implemented");
-    ALOGE_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
-             "swapchain preTransform %d not supported",
+    ALOGV_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
+             "swapchain imageColorSpace=%u not supported",
+             create_info->imageColorSpace);
+    ALOGV_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
+             "swapchain preTransform=%#x not supported",
              create_info->preTransform);
-    ALOGW_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
+    ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
                create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
-             "swapchain present mode %d not supported",
+             "swapchain presentMode=%u not supported",
              create_info->presentMode);
 
     Surface& surface = *SurfaceFromHandle(create_info->surface);
 
+    if (surface.swapchain_handle != create_info->oldSwapchain) {
+        ALOGV("Can't create a swapchain for VkSurfaceKHR 0x%" PRIx64
+              " because it already has active swapchain 0x%" PRIx64
+              " but VkSwapchainCreateInfo::oldSwapchain=0x%" PRIx64,
+              reinterpret_cast<uint64_t>(create_info->surface),
+              reinterpret_cast<uint64_t>(surface.swapchain_handle),
+              reinterpret_cast<uint64_t>(create_info->oldSwapchain));
+        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
+    }
+    if (create_info->oldSwapchain != VK_NULL_HANDLE)
+        OrphanSwapchain(device, SwapchainFromHandle(create_info->oldSwapchain));
+
     // -- Reset the native window --
     // The native window might have been used previously, and had its properties
     // changed from defaults. That will affect the answer we get for queries
     // like MIN_UNDEQUED_BUFFERS. Reset to a known/default state before we
     // attempt such queries.
 
+    // The native window only allows dequeueing all buffers before any have
+    // been queued, since after that point at least one is assumed to be in
+    // non-FREE state at any given time. Disconnecting and re-connecting
+    // orphans the previous buffers, getting us back to the state where we can
+    // dequeue all buffers.
+    err = native_window_api_disconnect(surface.window.get(),
+                                       NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+             strerror(-err), err);
+    err =
+        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+             strerror(-err), err);
+
     err = native_window_set_buffer_count(surface.window.get(), 0);
     if (err != 0) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
@@ -397,7 +487,7 @@
             native_format = HAL_PIXEL_FORMAT_RGB_565;
             break;
         default:
-            ALOGE("unsupported swapchain format %d", create_info->imageFormat);
+            ALOGV("unsupported swapchain format %d", create_info->imageFormat);
             break;
     }
     err = native_window_set_buffers_format(surface.window.get(), native_format);
@@ -618,7 +708,8 @@
         return result;
     }
 
-    *swapchain_handle = HandleFromSwapchain(swapchain);
+    surface.swapchain_handle = HandleFromSwapchain(swapchain);
+    *swapchain_handle = surface.swapchain_handle;
     return VK_SUCCESS;
 }
 
@@ -628,21 +719,13 @@
                          const VkAllocationCallbacks* allocator) {
     const auto& dispatch = GetData(device).driver;
     Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
-    const android::sp<ANativeWindow>& window = swapchain->surface.window;
+    bool active = swapchain->surface.swapchain_handle == swapchain_handle;
+    ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
 
-    for (uint32_t i = 0; i < swapchain->num_images; i++) {
-        Swapchain::Image& img = swapchain->images[i];
-        if (img.dequeued) {
-            window->cancelBuffer(window.get(), img.buffer.get(),
-                                 img.dequeue_fence);
-            img.dequeue_fence = -1;
-            img.dequeued = false;
-        }
-        if (img.image) {
-            dispatch.DestroyImage(device, img.image, nullptr);
-        }
-    }
-
+    for (uint32_t i = 0; i < swapchain->num_images; i++)
+        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+    if (active)
+        swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
     if (!allocator)
         allocator = &GetData(device).allocator;
     swapchain->~Swapchain();
@@ -655,6 +738,10 @@
                                uint32_t* count,
                                VkImage* images) {
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
+             "getting images for non-active swapchain 0x%" PRIx64
+             "; only dequeued image handles are valid",
+             reinterpret_cast<uint64_t>(swapchain_handle));
     VkResult result = VK_SUCCESS;
     if (images) {
         uint32_t n = swapchain.num_images;
@@ -681,6 +768,9 @@
     VkResult result;
     int err;
 
+    if (swapchain.surface.swapchain_handle != swapchain_handle)
+        return VK_ERROR_OUT_OF_DATE_KHR;
+
     ALOGW_IF(
         timeout != UINT64_MAX,
         "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
@@ -739,6 +829,26 @@
     return VK_SUCCESS;
 }
 
+static VkResult WorstPresentResult(VkResult a, VkResult b) {
+    // See the error ranking for vkQueuePresentKHR at the end of section 29.6
+    // (in spec version 1.0.14).
+    static const VkResult kWorstToBest[] = {
+        VK_ERROR_DEVICE_LOST,
+        VK_ERROR_SURFACE_LOST_KHR,
+        VK_ERROR_OUT_OF_DATE_KHR,
+        VK_ERROR_OUT_OF_DEVICE_MEMORY,
+        VK_ERROR_OUT_OF_HOST_MEMORY,
+        VK_SUBOPTIMAL_KHR,
+    };
+    for (auto result : kWorstToBest) {
+        if (a == result || b == result)
+            return result;
+    }
+    ALOG_ASSERT(a == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", a);
+    ALOG_ASSERT(b == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", b);
+    return a != VK_SUCCESS ? a : b;
+}
+
 VKAPI_ATTR
 VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
     ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@@ -746,14 +856,16 @@
              present_info->sType);
     ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
 
+    VkDevice device = GetData(queue).driver_device;
     const auto& dispatch = GetData(queue).driver;
     VkResult final_result = VK_SUCCESS;
+
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
             *SwapchainFromHandle(present_info->pSwapchains[sc]);
-        ANativeWindow* window = swapchain.surface.window.get();
         uint32_t image_idx = present_info->pImageIndices[sc];
         Swapchain::Image& img = swapchain.images[image_idx];
+        VkResult swapchain_result = VK_SUCCESS;
         VkResult result;
         int err;
 
@@ -763,37 +875,42 @@
             present_info->pWaitSemaphores, img.image, &fence);
         if (result != VK_SUCCESS) {
             ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
-            if (present_info->pResults)
-                present_info->pResults[sc] = result;
-            if (final_result == VK_SUCCESS)
-                final_result = result;
-            // TODO(jessehall): What happens to the buffer here? Does the app
-            // still own it or not, i.e. should we cancel the buffer? Hard to
-            // do correctly without synchronizing, though I guess we could wait
-            // for the queue to idle.
-            continue;
+            swapchain_result = result;
         }
 
-        err = window->queueBuffer(window, img.buffer.get(), fence);
-        if (err != 0) {
-            // TODO(jessehall): What now? We should probably cancel the buffer,
-            // I guess?
-            ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
-            if (present_info->pResults)
-                present_info->pResults[sc] = result;
-            if (final_result == VK_SUCCESS)
-                final_result = VK_ERROR_INITIALIZATION_FAILED;
-            continue;
+        if (swapchain.surface.swapchain_handle ==
+            present_info->pSwapchains[sc]) {
+            ANativeWindow* window = swapchain.surface.window.get();
+            if (swapchain_result == VK_SUCCESS) {
+                err = window->queueBuffer(window, img.buffer.get(), fence);
+                // queueBuffer always closes fence, even on error
+                if (err != 0) {
+                    // TODO(jessehall): What now? We should probably cancel the
+                    // buffer, I guess?
+                    ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+                    swapchain_result = WorstPresentResult(
+                        swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
+                }
+                if (img.dequeue_fence >= 0) {
+                    close(img.dequeue_fence);
+                    img.dequeue_fence = -1;
+                }
+                img.dequeued = false;
+            }
+            if (swapchain_result != VK_SUCCESS) {
+                ReleaseSwapchainImage(device, window, fence, img);
+                OrphanSwapchain(device, &swapchain);
+            }
+        } else {
+            ReleaseSwapchainImage(device, nullptr, fence, img);
+            swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
         }
 
-        if (img.dequeue_fence != -1) {
-            close(img.dequeue_fence);
-            img.dequeue_fence = -1;
-        }
-        img.dequeued = false;
-
         if (present_info->pResults)
-            present_info->pResults[sc] = VK_SUCCESS;
+            present_info->pResults[sc] = swapchain_result;
+
+        if (swapchain_result != final_result)
+            final_result = WorstPresentResult(final_result, swapchain_result);
     }
 
     return final_result;