Merge "Avoid unlinking app image file"
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
new file mode 100644
index 0000000..fc43250
--- /dev/null
+++ b/cmds/dumpstate/bugreport-format.md
@@ -0,0 +1,87 @@
+# Bugreport file format
+
+This document specifies the format of the bugreport files generated by the
+bugreport services (like `bugreport` and `bugreportplus`) and delivered to the
+end user (i.e., it doesn’t include other tools like `adb bugreport`).
+
+A _bugreport_ is initially generated by dumpstate, then processed by **Shell**,
+which in turn delivers it to the end user through a `ACTION_SEND_MULTIPLE`
+intent; the end user then select which app (like an email client) handles such
+intent.
+
+## Text file (Pre-M)
+Prior to _Android M (Marshmallow)_, `dumpstate` generates a flat .txt file named
+_bugreport-DATE.txt_ (where _DATE_ is date the bugreport was generated, in the
+format _YYYY-MM-DD-HH-MM-SS_), and Shell simply propagates it as an attachment
+in the `ACTION_SEND_MULTIPLE` intent.
+
+## Version v0 (Android M)
+On _Android M (Marshmallow)_, dumpstate still generates a flat
+_bugreport-DATE.txt_ file, but then **Shell** creates a zip file called
+_bugreport-DATE.zip_ containing a _bugreport-DATE.txt_ entry and sends that
+file as the `ACTION_SEND_MULTIPLE` attachment.
+
+## Version v1 (Android N)
+On _Android N (TBD)_, `dumpstate` generates a zip file directly (unless there
+is a failure, in which case it reverts to the flat file that is zipped by
+**Shell** and hence the end result is the _v0_ format).
+
+The zip file is by default called _bugreport-DATE.zip_ and it contains a
+_bugreport-DATE.txt_ entry, although the end user can change the name (through
+**Shell**), in which case they would be called _bugreport-NEW_NAME.zip_ and
+_bugreport-NEW_NAME.txt_ respectively.
+
+The zip file also contains 2 metadata entries generated by `dumpstate`:
+
+- `version.txt`:  whose value is **v1**.
+- `main-entry.txt`: whose value is the name of the flat text entry (i.e.,
+  _bugreport-DATE.txt_ or _bugreport-NEW_NAME.txt_).
+
+`dumpstate` can also copy files from the device’s filesystem into the zip file
+under the `FS` folder. For example, a `/dirA/dirB/fileC` file in the device
+would generate a `FS/dirA/dirB/fileC` entry in the zip file.
+
+The flat file also has some minor changes:
+
+- Tombstone files were removed and added to the zip file.
+- The duration of each section is printed in the report.
+- Some dumpsys sections (memory and cpuinfo) are reported earlier in the file.
+
+Besides the files generated by `dumpstate`, **Shell** can also add 2 other
+files upon the end user’s request:
+
+- `title.txt`: whose value is a single-line summary of the problem.
+- `description.txt`: whose value is a multi-line, detailed description of the problem.
+
+## Intermediate versions
+During development, the versions will be suffixed with _-devX_ or
+_-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
+changes become stable.
+
+For example, the initial version during _Android N_ development was
+**v1-dev1**. When `dumpsys` was split in 2 sections but not all tools were
+ready to parse that format, the version was named **v1-dev1-dumpsys-split**,
+which had to be passed do `dumpsys` explicitly (i.e., trhough a
+`-V v1-dev1-dumpsys-split` argument). Once that format became stable and tools
+knew how to parse it, the default version became **v1-dev2**.
+
+Similarly, if changes in the file format are made after the initial release of
+Android defining that format, then a new _sub-version_ will be used.
+For example, if after _Android N_ launches changes are made for the next _N_
+release, the version will be called **v1.1** or something like that.
+
+Determining version and main entry
+-----------------------------------------------
+
+Tools parsing the zipped bugreport file can use the following algorithm to
+determine the bugreport format version and its main entry:
+
+```
+If [entries contain "version.txt"]
+   version = read("version.txt")
+   main_entry = read("main_entry.txt")
+else
+   version = v0
+   main_entry = entries[0]
+fi
+```
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2f124ac..22fd2c3 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -83,6 +83,16 @@
 // Root dir for all files copied as-is into the bugreport
 const std::string& ZIP_ROOT_DIR = "FS";
 
+/*
+ * List of supported zip format versions.
+ *
+ * See bugreport-format.txt for more info.
+ */
+// TODO: change to "v1" before final N build
+static std::string VERSION_DEFAULT = "v1-dev1";
+// TODO: remove before final N build
+static std::string VERSION_DUMPSYS_SPLIT = "v1-dev1-dumpsys-split";
+
 /* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
  * otherwise gets just those modified in the last half an hour. */
 static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
@@ -132,9 +142,9 @@
     if (!zip_writer) return;
     const char *title = "MOUNT INFO";
     mount_points.clear();
-    DurationReporter duration_reporter(title);
+    DurationReporter duration_reporter(title, NULL);
     for_each_pid(do_mountinfo, NULL);
-    printf("%s: %d entries added to zip file\n", title, mount_points.size());
+    ALOGD("%s: %lu entries added to zip file\n", title, mount_points.size());
 }
 
 static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
@@ -325,7 +335,7 @@
 /* End copy from system/core/logd/LogBuffer.cpp */
 
 /* dumps the current system state to stdout */
-static void print_header() {
+static void print_header(std::string version) {
     char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
     char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
     char network[PROPERTY_VALUE_MAX], date[80];
@@ -352,17 +362,22 @@
     printf("Kernel: ");
     dump_file(NULL, "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+    printf("Bugreport format version: %s\n", version.c_str());
     printf("\n");
 }
 
 /* adds a new entry to the existing zip file. */
 static bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
-    DurationReporter duration_reporter(("ADD ZIP ENTRY " + entry_name).c_str());
+    if (!zip_writer) {
+        ALOGD("Not adding zip entry %s from fd because zip_writer is not set", entry_name.c_str());
+        return false;
+    }
     ALOGD("Adding zip entry %s", entry_name.c_str());
     int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(),
             ZipWriter::kCompress, get_mtime(fd, now));
     if (err) {
-        ALOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(), ZipWriter::ErrorCodeString(err));
+        ALOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+                ZipWriter::ErrorCodeString(err));
         return false;
     }
 
@@ -409,11 +424,44 @@
 
 /* adds all files from a directory to the zipped bugreport file */
 void add_dir(const char *dir, bool recursive) {
-    if (!zip_writer) return;
-    DurationReporter duration_reporter(dir);
+    if (!zip_writer) {
+        ALOGD("Not adding dir %s because zip_writer is not set", dir);
+        return;
+    }
+    DurationReporter duration_reporter(dir, NULL);
     dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
 }
 
+/* adds a text entry entry to the existing zip file. */
+static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
+    if (!zip_writer) {
+        ALOGD("Not adding text zip entry %s because zip_writer is not set", entry_name.c_str());
+        return false;
+    }
+    ALOGD("Adding zip text entry %s", entry_name.c_str());
+    int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now);
+    if (err) {
+        ALOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+                ZipWriter::ErrorCodeString(err));
+        return false;
+    }
+
+    err = zip_writer->WriteBytes(content.c_str(), content.length());
+    if (err) {
+        ALOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
+                ZipWriter::ErrorCodeString(err));
+        return false;
+    }
+
+    err = zip_writer->FinishEntry();
+    if (err) {
+        ALOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+        return false;
+    }
+
+    return true;
+}
+
 static void dumpstate(const std::string& screenshot_path) {
     DurationReporter duration_reporter("DUMPSTATE");
     unsigned long timeout;
@@ -717,24 +765,28 @@
 
 
     printf("========================================================\n");
+    printf("== Final progress (pid %d): %d/%d (originally %d)\n",
+            getpid(), progress, weight_total, WEIGHT_TOTAL);
+    printf("========================================================\n");
     printf("== dumpstate: done\n");
     printf("========================================================\n");
 }
 
 static void usage() {
-    fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s] [-q]\n"
-            "  -o: write to file (instead of stdout)\n"
-            "  -d: append date to filename (requires -o)\n"
-            "  -z: generates zipped file (requires -o)\n"
-            "  -p: capture screenshot to filename.png (requires -o)\n"
-            "  -s: write output to control socket (for init)\n"
+    fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s] [-q] [-B] [-P] [-R] [-V version]\n"
             "  -b: play sound file instead of vibrate, at beginning of job\n"
             "  -e: play sound file instead of vibrate, at end of job\n"
+            "  -o: write to file (instead of stdout)\n"
+            "  -d: append date to filename (requires -o)\n"
+            "  -p: capture screenshot to filename.png (requires -o)\n"
+            "  -z: generates zipped file (requires -o)\n"
+            "  -s: write output to control socket (for init)\n"
             "  -q: disable vibrate\n"
             "  -B: send broadcast when finished (requires -o)\n"
-            "  -P: send broadacast when started and update system properties on progress (requires -o and -B)\n"
+            "  -P: send broadcast when started and update system properties on progress (requires -o and -B)\n"
             "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, shouldn't be used with -P)\n"
-                );
+            "  -V: sets the bugreport format version (%s or %s)\n",
+            VERSION_DEFAULT.c_str(), VERSION_DUMPSYS_SPLIT.c_str());
 }
 
 static void sigpipe_handler(int n) {
@@ -751,6 +803,10 @@
         ALOGE("Failed to add text entry to .zip file\n");
         return false;
     }
+    if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+        ALOGE("Failed to add main_entry.txt to .zip file\n");
+        return false;
+    }
 
     int32_t err = zip_writer->Finish();
     if (err) {
@@ -799,6 +855,48 @@
     return std::string(hash_buffer);
 }
 
+/* switch to non-root user and group */
+bool drop_root() {
+    /* ensure we will keep capabilities when we drop root */
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+        ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
+            AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC };
+    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+        ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+        return false;
+    }
+    if (setgid(AID_SHELL) != 0) {
+        ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+        return false;
+    }
+    if (setuid(AID_SHELL) != 0) {
+        ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+        return false;
+    }
+
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    capheader.pid = 0;
+
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[0].inheritable = 0;
+    capdata[1].inheritable = 0;
+
+    if (capset(&capheader, &capdata[0]) < 0) {
+        ALOGE("capset failed: %s\n", strerror(errno));
+        return false;
+    }
+
+    return true;
+}
 
 int main(int argc, char *argv[]) {
     struct sigaction sigact;
@@ -811,6 +909,7 @@
     int do_broadcast = 0;
     int do_early_screenshot = 0;
     int is_remote_mode = 0;
+    std::string version = VERSION_DEFAULT;
 
     now = time(NULL);
 
@@ -840,7 +939,7 @@
 
     /* parse arguments */
     int c;
-    while ((c = getopt(argc, argv, "dho:svqzpPBR")) != -1) {
+    while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
         switch (c) {
             case 'd': do_add_date = 1;          break;
             case 'z': do_zip_file = 1;          break;
@@ -852,6 +951,7 @@
             case 'P': do_update_progress = 1;   break;
             case 'R': is_remote_mode = 1;       break;
             case 'B': do_broadcast = 1;         break;
+            case 'V': version = optarg;         break;
             case '?': printf("\n");
             case 'h':
                 usage();
@@ -874,6 +974,13 @@
         exit(1);
     }
 
+    if (version != VERSION_DEFAULT && version != VERSION_DUMPSYS_SPLIT) {
+        usage();
+        exit(1);
+    }
+
+    ALOGI("bugreport format version: %s\n", version.c_str());
+
     do_early_screenshot = do_update_progress;
 
     // If we are going to use a socket, do it as early as possible
@@ -932,6 +1039,7 @@
         if (do_zip_file) {
             ALOGD("Creating initial .zip file");
             path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
+            create_parent_dirs(path.c_str());
             zip_file.reset(fopen(path.c_str(), "wb"));
             if (!zip_file) {
                 ALOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
@@ -939,6 +1047,7 @@
             } else {
                 zip_writer.reset(new ZipWriter(zip_file.get()));
             }
+            add_text_zip_entry("version.txt", version);
         }
 
         if (do_update_progress) {
@@ -959,8 +1068,6 @@
         fclose(cmdline);
     }
 
-    print_header();
-
     /* open the vibrator before dropping root */
     std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
     if (do_vibrate) {
@@ -999,42 +1106,7 @@
     add_dir(RECOVERY_DIR, true);
     add_mountinfo();
 
-    /* ensure we will keep capabilities when we drop root */
-    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
-        ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
-        return -1;
-    }
-
-    /* switch to non-root user and group */
-    gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
-            AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC };
-    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
-        ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
-        return -1;
-    }
-    if (setgid(AID_SHELL) != 0) {
-        ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
-        return -1;
-    }
-    if (setuid(AID_SHELL) != 0) {
-        ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
-        return -1;
-    }
-
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[0].inheritable = 0;
-    capdata[1].inheritable = 0;
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        ALOGE("capset failed: %s\n", strerror(errno));
+    if (!drop_root()) {
         return -1;
     }
 
@@ -1044,6 +1116,10 @@
            directly, but the libziparchive doesn't support that option yet. */
         redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
     }
+    // NOTE: there should be no stdout output until now, otherwise it would break the header.
+    // In particular, DurationReport objects should be created passing 'title, NULL', so their
+    // duration is logged into ALOG instead.
+    print_header(version);
 
     dumpstate(do_early_screenshot ? "": screenshot_path);
 
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0a9f9e2..a6afbf4 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -112,6 +112,9 @@
 /* redirect output to a file */
 void redirect_to_file(FILE *redirect, char *path);
 
+/* create leading directories, if necessary */
+void create_parent_dirs(const char *path);
+
 /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
 const char *dump_traces();
 
@@ -165,6 +168,7 @@
 class DurationReporter {
 public:
     DurationReporter(const char *title);
+    DurationReporter(const char *title, FILE* out);
 
     ~DurationReporter();
 
@@ -172,6 +176,7 @@
 
 private:
     const char* title_;
+    FILE* out_;
     uint64_t started_;
 };
 
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 3f2d126..0c35430 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -60,19 +60,26 @@
         NULL,
 };
 
-DurationReporter::DurationReporter(const char *title) {
+DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+
+DurationReporter::DurationReporter(const char *title, FILE *out) {
     title_ = title;
     if (title) {
         started_ = DurationReporter::nanotime();
     }
+    out_ = out;
 }
 
 DurationReporter::~DurationReporter() {
     if (title_) {
         uint64_t elapsed = DurationReporter::nanotime() - started_;
         // Use "Yoda grammar" to make it easier to grep|sort sections.
-        printf("------ %.3fs was the duration of '%s' ------\n",
-               (float) elapsed / NANOS_PER_SEC, title_);
+        if (out_) {
+            fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
+                   (float) elapsed / NANOS_PER_SEC, title_);
+        } else {
+            ALOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+        }
     }
 }
 
@@ -650,25 +657,37 @@
     close(fd);
 }
 
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
-    char *chp = path;
+void create_parent_dirs(const char *path) {
+    char *chp = (char*) path;
 
     /* skip initial slash */
     if (chp[0] == '/')
         chp++;
 
     /* create leading directories, if necessary */
+    struct stat dir_stat;
     while (chp && chp[0]) {
         chp = strchr(chp, '/');
         if (chp) {
             *chp = 0;
-            mkdir(path, 0770);  /* drwxrwx--- */
+            if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) {
+                ALOGI("Creating directory %s\n", path);
+                if (mkdir(path, 0770)) { /* drwxrwx--- */
+                    ALOGE("Unable to create directory %s: %s\n", path, strerror(errno));
+                } else if (chown(path, AID_SHELL, AID_SHELL)) {
+                    ALOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno));
+                }
+            }
             *chp++ = '/';
         }
     }
+}
 
-    int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+/* redirect output to a file */
+void redirect_to_file(FILE *redirect, char *path) {
+    create_parent_dirs(path);
+
+    int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
                                      S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     if (fd < 0) {
         fprintf(stderr, "%s: %s\n", path, strerror(errno));
@@ -690,7 +709,7 @@
 
 /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
 const char *dump_traces() {
-    DurationReporter duration_reporter("DUMP TRACES");
+    DurationReporter duration_reporter("DUMP TRACES", NULL);
     ON_DRY_RUN_RETURN(NULL);
     const char* result = NULL;
 
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 209632e..65bcf39 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -43,6 +43,43 @@
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
+#
+# OTA Executable
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := otapreopt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_cflags)
+
+# Base & ASLR boundaries for boot image creation.
+ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
+  LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := -0x1000000
+else
+  LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+endif
+ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA
+  LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := 0x1000000
+else
+  LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+endif
+LOCAL_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
+LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+
+LOCAL_SRC_FILES := otapreopt.cpp $(common_src_files)
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    liblog \
+    liblogwrap \
+    libselinux \
+
+LOCAL_STATIC_LIBRARIES := libdiskusage
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+include $(BUILD_EXECUTABLE)
+
 # Tests.
 
 include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 02e9601..c0ae5b7 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -211,12 +211,41 @@
     return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
 }
 
-static int do_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+  // Time to fork and run otapreopt.
+  pid_t pid = fork();
+  if (pid == 0) {
+    const char* argv[1 + 9 + 1];
+    argv[0] = "/system/bin/otapreopt";
+    for (size_t i = 1; i <= 9; ++i) {
+      argv[i] = arg[i - 1];
+    }
+    argv[10] = nullptr;
+
+    execv(argv[0], (char * const *)argv);
+    ALOGE("execv(OTAPREOPT) failed: %s\n", strerror(errno));
+    exit(99);
+  } else {
+    int res = wait_child(pid);
+    if (res == 0) {
+      ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
+    } else {
+      ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+    }
+    return res;
+  }
+}
+
+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);
+    }
     /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags, volume_uuid,
             use_profiles */
     return dexopt(arg[0], atoi(arg[1]), arg[2], arg[3], atoi(arg[4]),
-                  arg[5], atoi(arg[6]), parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true));
+                  arg[5], dexopt_flags, parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true));
 }
 
 static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 18f998d..0d21519 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -48,6 +48,7 @@
 // This is used as a string literal, can't be constants. TODO: std::string...
 #define DALVIK_CACHE "dalvik-cache"
 constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex";
+constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex";
 
 constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
 constexpr const char* IDMAP_SUFFIX = "@idmap";
@@ -74,6 +75,7 @@
 constexpr int DEXOPT_DEBUGGABLE   = 1 << 3;
 constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4;
 constexpr int DEXOPT_EXTRACTONLY  = 1 << 5;
+constexpr int DEXOPT_OTA          = 1 << 6;
 
 /* all known values for dexopt flags */
 constexpr int DEXOPT_MASK =
@@ -81,7 +83,8 @@
     | DEXOPT_SAFEMODE
     | DEXOPT_DEBUGGABLE
     | DEXOPT_BOOTCOMPLETE
-    | DEXOPT_EXTRACTONLY;
+    | DEXOPT_EXTRACTONLY
+    | DEXOPT_OTA;
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
new file mode 100644
index 0000000..27f7939
--- /dev/null
+++ b/cmds/installd/otapreopt.cpp
@@ -0,0 +1,641 @@
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <algorithm>
+#include <inttypes.h>
+#include <random>
+#include <selinux/android.h>
+#include <selinux/avc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+#include <commands.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>
+
+#ifndef LOG_TAG
+#define LOG_TAG "otapreopt"
+#endif
+
+#define BUFFER_MAX    1024  /* input buffer for commands */
+#define TOKEN_MAX     16    /* max number of arguments in buffer */
+#define REPLY_MAX     256   /* largest reply allowed */
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+static constexpr const char* kBootClassPathPropertyName = "env.BOOTCLASSPATH";
+static constexpr const char* kAndroidRootPathPropertyName = "env.ANDROID_ROOT";
+static constexpr const char* kOTARootDirectory = "/system-b";
+static constexpr size_t kISAIndex = 3;
+
+template<typename T>
+static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
+    return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
+}
+
+template<typename T>
+static constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) {
+    return RoundDown(x + n - 1, n);
+}
+
+class OTAPreoptService {
+ public:
+    static constexpr const char* kOTADataDirectory = "/data/ota";
+
+    // Main driver. Performs the following steps.
+    //
+    // 1) Parse options (read system properties etc from B partition).
+    //
+    // 2) Read in package data.
+    //
+    // 3) Prepare environment variables.
+    //
+    // 4) Prepare(compile) boot image, if necessary.
+    //
+    // 5) Run update.
+    int Main(int argc, char** argv) {
+        if (!ReadSystemProperties()) {
+            LOG(ERROR)<< "Failed reading system properties.";
+            return 1;
+        }
+
+        if (!ReadEnvironment()) {
+            LOG(ERROR) << "Failed reading environment properties.";
+            return 2;
+        }
+
+        if (!ReadPackage(argc, argv)) {
+            LOG(ERROR) << "Failed reading command line file.";
+            return 3;
+        }
+
+        PrepareEnvironment();
+
+        if (!PrepareBootImage()) {
+            LOG(ERROR) << "Failed preparing boot image.";
+            return 4;
+        }
+
+        int dexopt_retcode = RunPreopt();
+
+        return dexopt_retcode;
+    }
+
+    int GetProperty(const char* key, char* value, const char* default_value) {
+        const std::string* prop_value = system_properties_.GetProperty(key);
+        if (prop_value == nullptr) {
+            if (default_value == nullptr) {
+                return 0;
+            }
+            // Copy in the default value.
+            strncpy(value, default_value, kPropertyValueMax - 1);
+            value[kPropertyValueMax - 1] = 0;
+            return strlen(default_value);// TODO: Need to truncate?
+        }
+        size_t size = std::min(kPropertyValueMax - 1, prop_value->length());
+        strncpy(value, prop_value->data(), size);
+        value[size] = 0;
+        return static_cast<int>(size);
+    }
+
+private:
+    bool ReadSystemProperties() {
+        // TODO(agampe): What to do about the things in default.prop? It's only heap sizes, so it's easy
+        //               to emulate for now, but has issues (e.g., vendors modifying the boot classpath
+        //               may require larger values here - revisit). That's why this goes first, so that
+        //               if those dummy values are overridden in build.prop, that's what we'll get.
+        //
+        // Note: It seems we'll get access to the B root partition, so we should read the default.prop
+        //       file.
+        // if (!system_properties_.Load(b_mount_path_ + "/default.prop") {
+        //   return false;
+        // }
+        system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xms", "64m");
+        system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xmx", "64m");
+        system_properties_.SetProperty("dalvik.vm.dex2oat-Xms", "64m");
+        system_properties_.SetProperty("dalvik.vm.dex2oat-Xmx", "512m");
+
+        // TODO(agampe): Do this properly/test.
+        return system_properties_.Load(b_mount_path_ + "/system/build.prop");
+    }
+
+    bool ReadEnvironment() {
+        // Read important environment variables. For simplicity, store them as
+        // system properties.
+        // TODO(agampe): We'll have to parse init.environ.rc for BOOTCLASSPATH.
+        //               For now, just the A version.
+        const char* boot_classpath = getenv("BOOTCLASSPATH");
+        if (boot_classpath == nullptr) {
+            return false;
+        }
+        system_properties_.SetProperty(kBootClassPathPropertyName, boot_classpath);
+
+        const char* root_path = getenv("ANDROID_ROOT");
+        if (root_path == nullptr) {
+            return false;
+        }
+        system_properties_.SetProperty(kAndroidRootPathPropertyName, b_mount_path_ + root_path);
+
+        return true;
+    }
+
+    bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
+        size_t index = 0;
+        while (index < ARRAY_SIZE(package_parameters_) &&
+                argv[index + 1] != nullptr) {
+            package_parameters_[index] = argv[index + 1];
+            index++;
+        }
+        if (index != ARRAY_SIZE(package_parameters_)) {
+            LOG(ERROR) << "Wrong number of parameters";
+            return false;
+        }
+
+        return true;
+    }
+
+    void PrepareEnvironment() {
+        CHECK(system_properties_.GetProperty(kBootClassPathPropertyName) != nullptr);
+        const std::string& boot_cp =
+                *system_properties_.GetProperty(kBootClassPathPropertyName);
+        environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_cp.c_str()));
+        environ_.push_back(StringPrintf("ANDROID_DATA=%s", kOTADataDirectory));
+        CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr);
+        const std::string& android_root =
+                *system_properties_.GetProperty(kAndroidRootPathPropertyName);
+        environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root.c_str()));
+
+        for (const std::string& e : environ_) {
+            putenv(const_cast<char*>(e.c_str()));
+        }
+    }
+
+    // Ensure that we have the right boot image. The first time any app is
+    // compiled, we'll try to generate it.
+    bool PrepareBootImage() {
+        if (package_parameters_[kISAIndex] == nullptr) {
+            LOG(ERROR) << "Instruction set missing.";
+            return false;
+        }
+        const char* isa = package_parameters_[kISAIndex];
+
+        // Check whether the file exists where expected.
+        std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE;
+        std::string isa_path = dalvik_cache + "/" + isa;
+        std::string art_path = isa_path + "/system@framework@boot.art";
+        std::string oat_path = isa_path + "/system@framework@boot.oat";
+        if (access(art_path.c_str(), F_OK) == 0 &&
+                access(oat_path.c_str(), F_OK) == 0) {
+            // Files exist, assume everything is alright.
+            return true;
+        }
+
+        // Create the directories, if necessary.
+        if (access(dalvik_cache.c_str(), F_OK) != 0) {
+            if (mkdir(dalvik_cache.c_str(), 0711) != 0) {
+                PLOG(ERROR) << "Could not create dalvik-cache dir";
+                return false;
+            }
+        }
+        if (access(isa_path.c_str(), F_OK) != 0) {
+            if (mkdir(isa_path.c_str(), 0711) != 0) {
+                PLOG(ERROR) << "Could not create dalvik-cache isa dir";
+                return false;
+            }
+        }
+
+        // Prepare and run dex2oat.
+        // TODO: Delete files, just for a blank slate.
+        const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName);
+
+        // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
+        std::vector<std::string> cmd;
+        cmd.push_back(b_mount_path_ + "/system/bin/dex2oat");
+        cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
+        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()));
+
+        int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
+                ART_BASE_ADDRESS_MAX_DELTA);
+        cmd.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset));
+
+        cmd.push_back(StringPrintf("--instruction-set=%s", isa));
+
+        // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
+        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
+                "-Xms",
+                true,
+                cmd);
+        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
+                "-Xmx",
+                true,
+                cmd);
+        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
+                "--compiler-filter=",
+                false,
+                cmd);
+        cmd.push_back(StringPrintf("--image-classes=%s/system/etc/preloaded-classes",
+                                   b_mount_path_.c_str()));
+        // TODO: Compiled-classes.
+        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, ' ');
+            cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
+        }
+        // TODO: Should we lower this? It's usually set close to max, because
+        //       normally there's not much else going on at boot.
+        AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
+                "-j",
+                false,
+                cmd);
+        AddCompilerOptionFromSystemProperty(
+                StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
+                "--instruction-set-variant=",
+                false,
+                cmd);
+        AddCompilerOptionFromSystemProperty(
+                StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
+                "--instruction-set-features=",
+                false,
+                cmd);
+
+        std::string error_msg;
+        bool result = Exec(cmd, &error_msg);
+        if (!result) {
+            LOG(ERROR) << "Could not generate boot image: " << error_msg;
+        }
+        return result;
+    }
+
+    static const char* ParseNull(const char* arg) {
+        return (strcmp(arg, "!") == 0) ? nullptr : arg;
+    }
+
+    int RunPreopt() {
+        /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags,
+           volume_uuid, use_profiles */
+        int ret = dexopt(package_parameters_[0],
+                atoi(package_parameters_[1]),
+                package_parameters_[2],
+                package_parameters_[3],
+                atoi(package_parameters_[4]),
+                package_parameters_[5],
+                atoi(package_parameters_[6]),
+                ParseNull(package_parameters_[7]),
+                (atoi(package_parameters_[8]) == 0 ? false : true));
+        return ret;
+    }
+
+    ////////////////////////////////////
+    // Helpers, mostly taken from ART //
+    ////////////////////////////////////
+
+    // 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, ' '));
+
+        CHECK_GE(arg_vector.size(), 1U) << command_line;
+
+        // Convert the args to char pointers.
+        const char* program = arg_vector[0].c_str();
+        std::vector<char*> args;
+        for (size_t i = 0; i < arg_vector.size(); ++i) {
+            const std::string& arg = arg_vector[i];
+            char* arg_str = const_cast<char*>(arg.c_str());
+            CHECK(arg_str != nullptr) << i;
+            args.push_back(arg_str);
+        }
+        args.push_back(nullptr);
+
+        // Fork and exec.
+        pid_t pid = fork();
+        if (pid == 0) {
+            // No allocation allowed between fork and exec.
+
+            // Change process groups, so we don't get reaped by ProcessManager.
+            setpgid(0, 0);
+
+            execv(program, &args[0]);
+
+            PLOG(ERROR) << "Failed to execv(" << command_line << ")";
+            // _exit to avoid atexit handlers in child.
+            _exit(1);
+        } else {
+            if (pid == -1) {
+                *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+                        command_line.c_str(), strerror(errno));
+                return false;
+            }
+
+            // wait for subprocess to finish
+            int status;
+            pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+            if (got_pid != pid) {
+                *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+                        "wanted %d, got %d: %s",
+                        command_line.c_str(), pid, got_pid, strerror(errno));
+                return false;
+            }
+            if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+                *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+                        command_line.c_str());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
+    static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
+        constexpr size_t kPageSize = PAGE_SIZE;
+        CHECK_EQ(min_delta % kPageSize, 0u);
+        CHECK_EQ(max_delta % kPageSize, 0u);
+        CHECK_LT(min_delta, max_delta);
+
+        std::default_random_engine generator;
+        generator.seed(GetSeed());
+        std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
+        int32_t r = distribution(generator);
+        if (r % 2 == 0) {
+            r = RoundUp(r, kPageSize);
+        } else {
+            r = RoundDown(r, kPageSize);
+        }
+        CHECK_LE(min_delta, r);
+        CHECK_GE(max_delta, r);
+        CHECK_EQ(r % kPageSize, 0u);
+        return r;
+    }
+
+    static uint64_t GetSeed() {
+#ifdef __BIONIC__
+        // Bionic exposes arc4random, use it.
+        uint64_t random_data;
+        arc4random_buf(&random_data, sizeof(random_data));
+        return random_data;
+#else
+#error "This is only supposed to run with bionic. Otherwise, implement..."
+#endif
+    }
+
+    void AddCompilerOptionFromSystemProperty(const char* system_property,
+            const char* prefix,
+            bool runtime,
+            std::vector<std::string>& out) {
+        const std::string* value =
+        system_properties_.GetProperty(system_property);
+        if (value != nullptr) {
+            if (runtime) {
+                out.push_back("--runtime-arg");
+            }
+            if (prefix != nullptr) {
+                out.push_back(StringPrintf("%s%s", prefix, value->c_str()));
+            } else {
+                out.push_back(*value);
+            }
+        }
+    }
+
+    // The path where the B partitions are mounted.
+    // TODO(agampe): If we're running this *inside* the change-root, we wouldn't need this.
+    std::string b_mount_path_;
+
+    // Stores the system properties read out of the B partition. We need to use these properties
+    // to compile, instead of the A properties we could get from init/get_property.
+    SystemProperties system_properties_;
+
+    const char* package_parameters_[9];
+
+    // Store environment values we need to set.
+    std::vector<std::string> environ_;
+};
+
+OTAPreoptService gOps;
+
+////////////////////////
+// Plug-in functions. //
+////////////////////////
+
+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);
+}
+
+// Compute the output path of
+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;
+
+    file_name_start = strrchr(apk_path, '/');
+    if (file_name_start == nullptr) {
+        ALOGE("apk_path '%s' has no '/'s in it\n", apk_path);
+        return false;
+    }
+    file_name_end = strrchr(file_name_start, '.');
+    if (file_name_end == nullptr) {
+        ALOGE("apk_path '%s' has no extension\n", apk_path);
+        return false;
+    }
+
+    // Calculate file_name
+    file_name_start++;  // Move past '/', is valid as file_name_end is valid.
+    size_t file_name_len = file_name_end - file_name_start;
+    std::string file_name(file_name_start, file_name_len);
+
+    // <apk_parent_dir>/oat/<isa>/<file_name>.odex.b
+    snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.b", oat_dir, instruction_set,
+             file_name.c_str());
+    return true;
+}
+
+/*
+ * Computes the odex file for the given apk_path and instruction_set.
+ * /system/framework/whatever.jar -> /system/framework/oat/<isa>/whatever.odex
+ *
+ * Returns false if it failed to determine the odex file path.
+ */
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
+                              const char *instruction_set) {
+    if (StringPrintf("%soat/%s/odex.b", apk_path, instruction_set).length() + 1 > PKG_PATH_MAX) {
+        ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
+        return false;
+    }
+
+    const char *path_end = strrchr(apk_path, '/');
+    if (path_end == nullptr) {
+        ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
+        return false;
+    }
+    std::string path_component(apk_path, path_end - apk_path);
+
+    const char *name_begin = path_end + 1;
+    const char *extension_start = strrchr(name_begin, '.');
+    if (extension_start == nullptr) {
+        ALOGE("apk_path '%s' has no extension.\n", apk_path);
+        return false;
+    }
+    std::string name_component(name_begin, extension_start - name_begin);
+
+    std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b",
+                                        path_component.c_str(),
+                                        instruction_set,
+                                        name_component.c_str());
+    CHECK_LT(new_path.length(), PKG_PATH_MAX);
+    strcpy(path, new_path.c_str());
+    return true;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX],
+                       const char *src,
+                       const char *instruction_set) {
+    size_t srclen = strlen(src);
+
+        /* demand that we are an absolute path */
+    if ((src == 0) || (src[0] != '/') || strstr(src,"..")) {
+        return false;
+    }
+
+    if (srclen > PKG_PATH_MAX) {        // XXX: PKG_NAME_MAX?
+        return false;
+    }
+
+    std::string from_src = std::string(src + 1);
+    std::replace(from_src.begin(), from_src.end(), '/', '@');
+
+    std::string assembled_path = StringPrintf("%s/%s/%s/%s%s",
+                                              OTAPreoptService::kOTADataDirectory,
+                                              DALVIK_CACHE,
+                                              instruction_set,
+                                              from_src.c_str(),
+                                              DALVIK_CACHE_POSTFIX2);
+
+    if (assembled_path.length() + 1 > PKG_PATH_MAX) {
+        return false;
+    }
+    strcpy(path, assembled_path.c_str());
+
+    return true;
+}
+
+bool initialize_globals() {
+    const char* data_path = getenv("ANDROID_DATA");
+    if (data_path == nullptr) {
+        ALOGE("Could not find ANDROID_DATA");
+        return false;
+    }
+    return init_globals_from_data_and_root(data_path, kOTARootDirectory);
+}
+
+static bool initialize_directories() {
+    // This is different from the normal installd. We only do the base
+    // directory, the rest will be created on demand when each app is compiled.
+    mode_t old_umask = umask(0);
+    LOG(INFO) << "Old umask: " << old_umask;
+    if (access(OTAPreoptService::kOTADataDirectory, R_OK) < 0) {
+        ALOGE("Could not access %s\n", OTAPreoptService::kOTADataDirectory);
+        return false;
+    }
+    return true;
+}
+
+static int log_callback(int type, const char *fmt, ...) {
+    va_list ap;
+    int priority;
+
+    switch (type) {
+        case SELINUX_WARNING:
+            priority = ANDROID_LOG_WARN;
+            break;
+        case SELINUX_INFO:
+            priority = ANDROID_LOG_INFO;
+            break;
+        default:
+            priority = ANDROID_LOG_ERROR;
+            break;
+    }
+    va_start(ap, fmt);
+    LOG_PRI_VA(priority, "SELinux", fmt, ap);
+    va_end(ap);
+    return 0;
+}
+
+static int otapreopt_main(const int argc, char *argv[]) {
+    int selinux_enabled = (is_selinux_enabled() > 0);
+
+    setenv("ANDROID_LOG_TAGS", "*:v", 1);
+    android::base::InitLogging(argv);
+
+    ALOGI("otapreopt firing up\n");
+
+    if (argc < 2) {
+        ALOGE("Expecting parameters");
+        exit(1);
+    }
+
+    union selinux_callback cb;
+    cb.func_log = log_callback;
+    selinux_set_callback(SELINUX_CB_LOG, cb);
+
+    if (!initialize_globals()) {
+        ALOGE("Could not initialize globals; exiting.\n");
+        exit(1);
+    }
+
+    if (!initialize_directories()) {
+        ALOGE("Could not create directories; exiting.\n");
+        exit(1);
+    }
+
+    if (selinux_enabled && selinux_status_open(true) < 0) {
+        ALOGE("Could not open selinux status; exiting.\n");
+        exit(1);
+    }
+
+    int ret = android::installd::gOps.Main(argc, argv);
+
+    return ret;
+}
+
+}  // namespace installd
+}  // namespace android
+
+int main(const int argc, char *argv[]) {
+    return android::installd::otapreopt_main(argc, argv);
+}
diff --git a/cmds/installd/string_helpers.h b/cmds/installd/string_helpers.h
new file mode 100644
index 0000000..e8fcdef
--- /dev/null
+++ b/cmds/installd/string_helpers.h
@@ -0,0 +1,67 @@
+/*
+ * 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/cmds/installd/system_properties.h b/cmds/installd/system_properties.h
new file mode 100644
index 0000000..1b5fb3a
--- /dev/null
+++ b/cmds/installd/system_properties.h
@@ -0,0 +1,89 @@
+/*
+ * 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 OTAPREOPT_SYSTEM_PROPERTIES_H_
+#define OTAPREOPT_SYSTEM_PROPERTIES_H_
+
+#include <fstream>
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace installd {
+
+// Helper class to read system properties into and manage as a string->string map.
+class SystemProperties {
+ public:
+    bool Load(const std::string& strFile) {
+        std::ifstream input_stream(strFile);
+
+        if (!input_stream.is_open()) {
+            return false;
+        }
+
+        while (!input_stream.eof()) {
+            // Read the next line.
+            std::string line;
+            getline(input_stream, line);
+
+            // Is the line empty? Simplifies the next check.
+            if (line.empty()) {
+                continue;
+            }
+
+            // Is this a comment (starts with pound)?
+            if (line[0] == '#') {
+                continue;
+            }
+
+            size_t equals_pos = line.find('=');
+            if (equals_pos == std::string::npos || equals_pos == 0) {
+                // Did not find equals sign, or it's the first character - isn't a valid line.
+                continue;
+            }
+
+            std::string key = line.substr(0, equals_pos);
+            std::string value = line.substr(equals_pos + 1,
+                                            line.length() - equals_pos + 1);
+
+            properties_.insert(std::make_pair(key, value));
+        }
+
+        return true;
+    }
+
+    // Look up the key in the map. Returns null if the key isn't mapped.
+    const std::string* GetProperty(const std::string& key) const {
+        auto it = properties_.find(key);
+        if (it != properties_.end()) {
+            return &it->second;
+        }
+        return nullptr;
+    }
+
+    void SetProperty(const std::string& key, const std::string& value) {
+        properties_.insert(std::make_pair(key, value));
+    }
+
+ private:
+    // The actual map.
+    std::unordered_map<std::string, std::string> properties_;
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // OTAPREOPT_SYSTEM_PROPERTIES_H_
diff --git a/data/etc/android.hardware.vr.high_performance.xml b/data/etc/android.hardware.vr.high_performance.xml
new file mode 100644
index 0000000..776f4f7
--- /dev/null
+++ b/data/etc/android.hardware.vr.high_performance.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the set of features required for a VR-compatible device -->
+<permissions>
+    <feature name="android.software.vr.mode" />
+    <feature name="android.hardware.vr.high_performance" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 5edf0e8..9cb4d6d 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -52,6 +52,9 @@
 
     <!-- Feature to specify if the device supports a VR mode. -->
     <feature name="android.software.vr.mode" />
+    <!-- Devices with all optimizations required to be a "VR Ready" device that
+         pass all CTS tests for this feature must include feature
+         android.hardware.vr.high_performance -->
 
     <!-- devices with GPS must include android.hardware.location.gps.xml -->
     <!-- devices with an autofocus camera and/or flash must include either
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 0abf8f3..5956e13 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_PARCEL_H
 #define ANDROID_PARCEL_H
 
+#include <string>
 #include <vector>
 
 #include <cutils/native_handle.h>
@@ -119,6 +120,10 @@
     status_t            writeChar(char16_t val);
     status_t            writeByte(int8_t val);
 
+    // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
+    status_t            writeUtf8AsUtf16(const std::string& str);
+    status_t            writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+
     status_t            writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
     status_t            writeByteVector(const std::vector<int8_t>& val);
     status_t            writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
@@ -136,6 +141,9 @@
     status_t            writeString16Vector(
                             const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
     status_t            writeString16Vector(const std::vector<String16>& val);
+    status_t            writeUtf8VectorAsUtf16Vector(
+                            const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
+    status_t            writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
 
     status_t            writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
     status_t            writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
@@ -230,6 +238,10 @@
     int8_t              readByte() const;
     status_t            readByte(int8_t *pArg) const;
 
+    // Read a UTF16 encoded string, convert to UTF8
+    status_t            readUtf8FromUtf16(std::string* str) const;
+    status_t            readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
+
     const char*         readCString() const;
     String8             readString8() const;
     String16            readString16() const;
@@ -274,6 +286,9 @@
     status_t            readString16Vector(
                             std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
     status_t            readString16Vector(std::vector<String16>* val) const;
+    status_t            readUtf8VectorFromUtf16Vector(
+                            std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
+    status_t            readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
 
     template<typename T>
     status_t            read(Flattenable<T>& val) const;
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 370f5d5..a515f39 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -125,6 +125,10 @@
     // Indicates that this buffer was queued by the producer. When in single
     // buffer mode acquire() can return a BufferItem that wasn't in the queue.
     bool mQueuedBuffer;
+
+    // Indicates that this BufferItem contains a stale buffer which has already
+    // been released by the BufferQueue.
+    bool mIsStale;
 };
 
 } // namespace android
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index fbd5114..e2e73a0 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -105,24 +105,32 @@
     // connected, mDequeueCondition must be broadcast.
     int getMaxBufferCountLocked() const;
 
-    // freeBufferLocked frees the GraphicBuffer and sync resources for the
+    // This performs the same computation but uses the given arguments instead
+    // of the member variables for mMaxBufferCount, mAsyncMode, and
+    // mDequeueBufferCannotBlock.
+    int getMaxBufferCountLocked(bool asyncMode,
+            bool dequeueBufferCannotBlock, int maxBufferCount) const;
+
+    // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
     // given slot.
-    void freeBufferLocked(int slot, bool validate = true);
+    void clearBufferSlotLocked(int slot);
 
     // freeAllBuffersLocked frees the GraphicBuffer and sync resources for
     // all slots, even if they're currently dequeued, queued, or acquired.
     void freeAllBuffersLocked();
 
-    // stillTracking returns true iff the buffer item is still being tracked
-    // in one of the slots.
-    bool stillTracking(const BufferItem* item) const;
+    // If delta is positive, makes more slots available. If negative, takes
+    // away slots. Returns false if the request can't be met.
+    bool adjustAvailableSlotsLocked(int delta);
 
     // waitWhileAllocatingLocked blocks until mIsAllocating is false.
     void waitWhileAllocatingLocked() const;
 
+#if DEBUG_ONLY_CODE
     // validateConsistencyLocked ensures that the free lists are in sync with
     // the information stored in mSlots
     void validateConsistencyLocked() const;
+#endif
 
     // mAllocator is the connection to SurfaceFlinger that is used to allocate
     // new GraphicBuffer objects.
@@ -179,13 +187,20 @@
     Fifo mQueue;
 
     // mFreeSlots contains all of the slots which are FREE and do not currently
-    // have a buffer attached
+    // have a buffer attached.
     std::set<int> mFreeSlots;
 
     // mFreeBuffers contains all of the slots which are FREE and currently have
-    // a buffer attached
+    // a buffer attached.
     std::list<int> mFreeBuffers;
 
+    // mUnusedSlots contains all slots that are currently unused. They should be
+    // free and not have a buffer attached.
+    std::list<int> mUnusedSlots;
+
+    // mActiveBuffers contains all slots which have a non-FREE buffer attached.
+    std::set<int> mActiveBuffers;
+
     // mDequeueCondition is a condition variable used for dequeueBuffer in
     // synchronous mode.
     mutable Condition mDequeueCondition;
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 645a07b..dc05e98 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -187,9 +187,9 @@
     // BufferQueueCore::INVALID_BUFFER_SLOT otherwise
     int getFreeBufferLocked() const;
 
-    // Returns the next free slot if one less than or equal to maxBufferCount
-    // is available or BufferQueueCore::INVALID_BUFFER_SLOT otherwise
-    int getFreeSlotLocked(int maxBufferCount) const;
+    // Returns the next free slot if one is available or
+    // BufferQueueCore::INVALID_BUFFER_SLOT otherwise
+    int getFreeSlotLocked() const;
 
     // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
     // block if there are no available slots and we are not in non-blocking
@@ -200,8 +200,7 @@
         Dequeue,
         Attach,
     };
-    status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found,
-            status_t* returnFlags) const;
+    status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const;
 
     sp<BufferQueueCore> mCore;
 
diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h
index 17a654a..943fa82 100644
--- a/include/gui/BufferSlot.h
+++ b/include/gui/BufferSlot.h
@@ -174,14 +174,15 @@
 struct BufferSlot {
 
     BufferSlot()
-    : mEglDisplay(EGL_NO_DISPLAY),
+    : mGraphicBuffer(nullptr),
+      mEglDisplay(EGL_NO_DISPLAY),
       mBufferState(),
       mRequestBufferCalled(false),
       mFrameNumber(0),
       mEglFence(EGL_NO_SYNC_KHR),
+      mFence(Fence::NO_FENCE),
       mAcquireCalled(false),
-      mNeedsCleanupOnRelease(false),
-      mAttachedByConsumer(false) {
+      mNeedsReallocation(false) {
     }
 
     // mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -191,8 +192,6 @@
     // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
     EGLDisplay mEglDisplay;
 
-    static const char* bufferStateName(BufferState state);
-
     // mBufferState is the current state of this buffer slot.
     BufferState mBufferState;
 
@@ -227,15 +226,10 @@
     // Indicates whether this buffer has been seen by a consumer yet
     bool mAcquireCalled;
 
-    // Indicates whether this buffer needs to be cleaned up by the
-    // consumer.  This is set when a buffer in ACQUIRED state is freed.
-    // It causes releaseBuffer to return STALE_BUFFER_SLOT.
-    bool mNeedsCleanupOnRelease;
-
-    // Indicates whether the buffer was attached on the consumer side.
-    // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
-    // to prevent the producer from using a stale cached buffer.
-    bool mAttachedByConsumer;
+    // Indicates whether the buffer was re-allocated without notifying the
+    // producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when
+    // dequeued to prevent the producer from using a stale cached buffer.
+    bool mNeedsReallocation;
 };
 
 } // namespace android
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index d4c9ee5..ebce7fb 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -199,7 +199,9 @@
     // cannot be less than maxAcquiredBufferCount.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * BAD_VALUE - bufferCount was out of range (see above).
+    // * BAD_VALUE - one of the below conditions occurred:
+    //             * bufferCount was out of range (see above).
+    //             * failure to adjust the number of available slots.
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxBufferCount(int bufferCount) = 0;
 
@@ -212,7 +214,9 @@
     // to be exceeded.
     //
     // Return of a value other than NO_ERROR means an error has occurred:
-    // * BAD_VALUE - maxAcquiredBuffers was out of range (see above).
+    // * BAD_VALUE - one of the below conditions occurred:
+    //             * maxAcquiredBuffers was out of range (see above).
+    //             * failure to adjust the number of available slots.
     // * INVALID_OPERATION - attempting to call this after a producer connected.
     virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
 
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 8646981..1f4c8ac 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -101,8 +101,9 @@
     // * NO_INIT - the buffer queue has been abandoned.
     // * BAD_VALUE - one of the below conditions occurred:
     //     * bufferCount was out of range (see above)
-    //     * client has one or more buffers dequeued
+    //     * client has too many buffers dequeued
     //     * this call would cause the maxBufferCount value to be exceeded
+    //     * failure to adjust the number of available slots.
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) = 0;
 
     // Set the async flag if the producer intends to asynchronously queue
@@ -115,8 +116,10 @@
     //
     // Return of a value other than NO_ERROR means an error has occurred:
     // * NO_INIT - the buffer queue has been abandoned.
-    // * BAD_VALUE - this call would cause the maxBufferCount value to be
+    // * BAD_VALUE - one of the following has occurred:
+    //             * this call would cause the maxBufferCount value to be
     //               exceeded
+    //             * failure to adjust the number of available slots.
     virtual status_t setAsyncMode(bool async) = 0;
 
     // dequeueBuffer requests a new buffer slot for the client to use. Ownership
@@ -436,6 +439,9 @@
     //             * the producer is already connected
     //             * api was out of range (see above).
     //             * output was NULL.
+    //             * Failure to adjust the number of available slots. This can
+    //               happen because of trying to allocate/deallocate the async
+    //               buffer in response to the value of producerControlledByApp.
     // * DEAD_OBJECT - the token is hosted by an already-dead process
     //
     // Additional negative errors may be returned by the internals, they
@@ -534,6 +540,11 @@
     // timeout of -1. If set (to a value other than -1), this will disable
     // non-blocking mode and its corresponding spare buffer (which is used to
     // ensure a buffer is always available).
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * BAD_VALUE - Failure to adjust the number of available slots. This can
+    //               happen because of trying to allocate/deallocate the async
+    //               buffer.
     virtual status_t setDequeueTimeout(nsecs_t timeout) = 0;
 };
 
diff --git a/include/media/openmax/OMX_AudioExt.h b/include/media/openmax/OMX_AudioExt.h
index 2a1c3f2..05c2232 100644
--- a/include/media/openmax/OMX_AudioExt.h
+++ b/include/media/openmax/OMX_AudioExt.h
@@ -94,6 +94,15 @@
     OMX_S32 nPCMLimiterEnable;     /**< Signal level limiting, 0 for disable, 1 for enable, -1 if unspecified */
 } OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE;
 
+typedef struct OMX_AUDIO_PARAM_ANDROID_PROFILETYPE {
+   OMX_U32 nSize;
+   OMX_VERSIONTYPE nVersion;
+   OMX_U32 nPortIndex;
+   OMX_U32 eProfile;      /**< type is OMX_AUDIO_AACPROFILETYPE or OMX_AUDIO_WMAPROFILETYPE
+                                 depending on context */
+   OMX_U32 nProfileIndex; /**< Used to query for individual profile support information */
+} OMX_AUDIO_PARAM_ANDROID_PROFILETYPE;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index 25bea1f..8bfc49d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -61,6 +61,7 @@
     OMX_IndexParamAudioAndroidOpus,                 /**< reference: OMX_AUDIO_PARAM_ANDROID_OPUSTYPE */
     OMX_IndexParamAudioAndroidAacPresentation,      /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */
     OMX_IndexParamAudioAndroidEac3,                 /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
+    OMX_IndexParamAudioProfileQuerySupported,       /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
 
     /* Image parameters and configurations */
     OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a237684..1f6bda2 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -287,12 +287,18 @@
         return new IPCThreadState;
     }
     
-    if (gShutdown) return NULL;
+    if (gShutdown) {
+        ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
+        return NULL;
+    }
     
     pthread_mutex_lock(&gTLSMutex);
     if (!gHaveTLS) {
-        if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+        int key_create_value = pthread_key_create(&gTLS, threadDestructor);
+        if (key_create_value != 0) {
             pthread_mutex_unlock(&gTLSMutex);
+            ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
+                    strerror(key_create_value));
             return NULL;
         }
         gHaveTLS = true;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 6893e39..d3fe158 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -749,6 +749,37 @@
     return NULL;
 }
 
+status_t Parcel::writeUtf8AsUtf16(const std::string& str) {
+    const uint8_t* strData = (uint8_t*)str.data();
+    const size_t strLen= str.length();
+    const ssize_t utf16Len = utf8_to_utf16_length(strData, strLen);
+    if (utf16Len < 0 || utf16Len> std::numeric_limits<int32_t>::max()) {
+        return BAD_VALUE;
+    }
+
+    status_t err = writeInt32(utf16Len);
+    if (err) {
+        return err;
+    }
+
+    // Allocate enough bytes to hold our converted string and its terminating NULL.
+    void* dst = writeInplace((utf16Len + 1) * sizeof(char16_t));
+    if (!dst) {
+        return NO_MEMORY;
+    }
+
+    utf8_to_utf16(strData, strLen, (char16_t*)dst);
+
+    return NO_ERROR;
+}
+
+status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) {
+  if (!str) {
+    return writeInt32(-1);
+  }
+  return writeUtf8AsUtf16(*str);
+}
+
 status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
 {
     if (!val) {
@@ -852,6 +883,15 @@
     return writeNullableTypedVector(val, &Parcel::writeString16);
 }
 
+status_t Parcel::writeUtf8VectorAsUtf16Vector(
+                        const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) {
+    return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
+status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) {
+    return writeTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
 status_t Parcel::writeInt32(int32_t val)
 {
     return writeAligned(val);
@@ -1491,6 +1531,14 @@
     return readTypedVector(val, &Parcel::readString16);
 }
 
+status_t Parcel::readUtf8VectorFromUtf16Vector(
+        std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const {
+    return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
+
+status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const {
+    return readTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
 
 status_t Parcel::readInt32(int32_t *pArg) const
 {
@@ -1649,6 +1697,46 @@
     return int8_t(readInt32());
 }
 
+status_t Parcel::readUtf8FromUtf16(std::string* str) const {
+    size_t utf16Size = 0;
+    const char16_t* src = readString16Inplace(&utf16Size);
+    if (!src) {
+        return UNEXPECTED_NULL;
+    }
+
+    // Save ourselves the trouble, we're done.
+    if (utf16Size == 0u) {
+        str->clear();
+       return NO_ERROR;
+    }
+
+    ssize_t utf8Size = utf16_to_utf8_length(src, utf16Size);
+    if (utf8Size < 0) {
+        return BAD_VALUE;
+    }
+    // Note that while it is probably safe to assume string::resize keeps a
+    // spare byte around for the trailing null, we're going to be explicit.
+    str->resize(utf8Size + 1);
+    utf16_to_utf8(src, utf16Size, &((*str)[0]));
+    str->resize(utf8Size);
+    return NO_ERROR;
+}
+
+status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const {
+    const int32_t start = dataPosition();
+    int32_t size;
+    status_t status = readInt32(&size);
+    str->reset();
+
+    if (status != OK || size < 0) {
+        return status;
+    }
+
+    setDataPosition(start);
+    str->reset(new std::string());
+    return readUtf8FromUtf16(str->get());
+}
+
 const char* Parcel::readCString() const
 {
     const size_t avail = mDataSize-mDataPos;
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 8a965dd..635020e 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -36,6 +36,8 @@
 # Don't warn about struct padding
 LOCAL_CPPFLAGS += -Wno-padded
 
+LOCAL_CPPFLAGS += -DDEBUG_ONLY_CODE=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
+
 LOCAL_SRC_FILES := \
 	IGraphicBufferConsumer.cpp \
 	IConsumerListener.cpp \
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index de8ff70..036ef1e 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -39,7 +39,8 @@
     mTransformToDisplayInverse(false),
     mSurfaceDamage(),
     mSingleBufferMode(false),
-    mQueuedBuffer(true) {
+    mQueuedBuffer(true),
+    mIsStale(false) {
 }
 
 BufferItem::~BufferItem() {}
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 6f9f21f..9c1bbe4 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -20,6 +20,12 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
+#if DEBUG_ONLY_CODE
+#define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0)
+#else
+#define VALIDATE_CONSISTENCY()
+#endif
+
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueConsumer.h>
 #include <gui/BufferQueueCore.h>
@@ -49,7 +55,7 @@
         // buffer so that the consumer can successfully set up the newly acquired
         // buffer before releasing the old one.
         int numAcquiredBuffers = 0;
-        for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+        for (int s : mCore->mActiveBuffers) {
             if (mSlots[s].mBufferState.isAcquired()) {
                 ++numAcquiredBuffers;
             }
@@ -133,7 +139,8 @@
                 BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
                         " size=%zu",
                         desiredPresent, expectedPresent, mCore->mQueue.size());
-                if (mCore->stillTracking(front)) {
+
+                if (!front->mIsStale) {
                     // Front buffer is still in mSlots, so mark the slot as free
                     mSlots[front->mSlot].mBufferState.freeQueued();
 
@@ -144,13 +151,17 @@
                             mSlots[front->mSlot].mBufferState.isFree()) {
                         mSlots[front->mSlot].mBufferState.mShared = false;
                     }
-                    // Don't put the shared buffer on the free list.
+
+                    // Don't put the shared buffer on the free list
                     if (!mSlots[front->mSlot].mBufferState.isShared()) {
+                        mCore->mActiveBuffers.erase(front->mSlot);
                         mCore->mFreeBuffers.push_back(front->mSlot);
                     }
+
                     listener = mCore->mConnectedProducerListener;
                     ++numDroppedBuffers;
                 }
+
                 mCore->mQueue.erase(front);
                 front = mCore->mQueue.begin();
             }
@@ -205,6 +216,7 @@
             outBuffer->mSurfaceDamage = Region::INVALID_REGION;
             outBuffer->mSingleBufferMode = true;
             outBuffer->mQueuedBuffer = false;
+            outBuffer->mIsStale = false;
         } else {
             slot = front->mSlot;
             *outBuffer = *front;
@@ -216,10 +228,9 @@
 
         BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
                 slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
-        // If the front buffer is still being tracked, update its slot state
-        if (mCore->stillTracking(outBuffer)) {
+
+        if (!outBuffer->mIsStale) {
             mSlots[slot].mAcquireCalled = true;
-            mSlots[slot].mNeedsCleanupOnRelease = false;
             // Don't decrease the queue count if the BufferItem wasn't
             // previously in the queue. This happens in single buffer mode when
             // the queue is empty and the BufferItem is created above.
@@ -247,7 +258,7 @@
 
         ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
 
-        mCore->validateConsistencyLocked();
+        VALIDATE_CONSISTENCY();
     }
 
     if (listener != NULL) {
@@ -270,7 +281,7 @@
         return NO_INIT;
     }
 
-    if (mCore->mSingleBufferMode) {
+    if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
         BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
                 "mode");
         return BAD_VALUE;
@@ -287,9 +298,11 @@
     }
 
     mSlots[slot].mBufferState.detachConsumer();
-    mCore->freeBufferLocked(slot);
+    mCore->mActiveBuffers.erase(slot);
+    mCore->mFreeSlots.insert(slot);
+    mCore->clearBufferSlotLocked(slot);
     mCore->mDequeueCondition.broadcast();
-    mCore->validateConsistencyLocked();
+    VALIDATE_CONSISTENCY();
 
     return NO_ERROR;
 }
@@ -316,7 +329,7 @@
 
     // Make sure we don't have too many acquired buffers
     int numAcquiredBuffers = 0;
-    for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+    for (int s : mCore->mActiveBuffers) {
         if (mSlots[s].mBufferState.isAcquired()) {
             ++numAcquiredBuffers;
         }
@@ -351,14 +364,14 @@
         return NO_MEMORY;
     }
 
+    mCore->mActiveBuffers.insert(found);
     *outSlot = found;
     ATRACE_BUFFER_INDEX(*outSlot);
     BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
 
     mSlots[*outSlot].mGraphicBuffer = buffer;
     mSlots[*outSlot].mBufferState.attachConsumer();
-    mSlots[*outSlot].mAttachedByConsumer = true;
-    mSlots[*outSlot].mNeedsCleanupOnRelease = false;
+    mSlots[*outSlot].mNeedsReallocation = true;
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mFrameNumber = 0;
 
@@ -379,7 +392,7 @@
     // for attached buffers.
     mSlots[*outSlot].mAcquireCalled = false;
 
-    mCore->validateConsistencyLocked();
+    VALIDATE_CONSISTENCY();
 
     return NO_ERROR;
 }
@@ -411,41 +424,35 @@
             return STALE_BUFFER_SLOT;
         }
 
-
-        if (mSlots[slot].mBufferState.isAcquired()) {
-            mSlots[slot].mEglDisplay = eglDisplay;
-            mSlots[slot].mEglFence = eglFence;
-            mSlots[slot].mFence = releaseFence;
-            mSlots[slot].mBufferState.release();
-
-            // After leaving single buffer mode, the shared buffer will
-            // still be around. Mark it as no longer shared if this
-            // operation causes it to be free.
-            if (!mCore->mSingleBufferMode &&
-                    mSlots[slot].mBufferState.isFree()) {
-                mSlots[slot].mBufferState.mShared = false;
-            }
-            // Don't put the shared buffer on the free list.
-            if (!mSlots[slot].mBufferState.isShared()) {
-                mCore->mFreeBuffers.push_back(slot);
-            }
-
-            listener = mCore->mConnectedProducerListener;
-            BQ_LOGV("releaseBuffer: releasing slot %d", slot);
-        } else if (mSlots[slot].mNeedsCleanupOnRelease) {
-            BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
-                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
-            mSlots[slot].mNeedsCleanupOnRelease = false;
-            return STALE_BUFFER_SLOT;
-        } else {
+        if (!mSlots[slot].mBufferState.isAcquired()) {
             BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
                     "but its state was %s", slot,
                     mSlots[slot].mBufferState.string());
             return BAD_VALUE;
         }
 
+        mSlots[slot].mEglDisplay = eglDisplay;
+        mSlots[slot].mEglFence = eglFence;
+        mSlots[slot].mFence = releaseFence;
+        mSlots[slot].mBufferState.release();
+
+        // After leaving single buffer mode, the shared buffer will
+        // still be around. Mark it as no longer shared if this
+        // operation causes it to be free.
+        if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) {
+            mSlots[slot].mBufferState.mShared = false;
+        }
+        // Don't put the shared buffer on the free list.
+        if (!mSlots[slot].mBufferState.isShared()) {
+            mCore->mActiveBuffers.erase(slot);
+            mCore->mFreeBuffers.push_back(slot);
+        }
+
+        listener = mCore->mConnectedProducerListener;
+        BQ_LOGV("releaseBuffer: releasing slot %d", slot);
+
         mCore->mDequeueCondition.broadcast();
-        mCore->validateConsistencyLocked();
+        VALIDATE_CONSISTENCY();
     } // Autolock scope
 
     // Call back without lock held
@@ -497,6 +504,7 @@
     mCore->mConsumerListener = NULL;
     mCore->mQueue.clear();
     mCore->freeAllBuffersLocked();
+    mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
     mCore->mDequeueCondition.broadcast();
     return NO_ERROR;
 }
@@ -579,6 +587,15 @@
         return BAD_VALUE;
     }
 
+    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+            mCore->mDequeueBufferCannotBlock, bufferCount) -
+            mCore->getMaxBufferCountLocked();
+    if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of "
+                "available slots. Delta = %d", delta);
+        return BAD_VALUE;
+    }
+
     mCore->mMaxBufferCount = bufferCount;
     return NO_ERROR;
 }
@@ -612,8 +629,17 @@
         return BAD_VALUE;
     }
 
+    if (!mCore->adjustAvailableSlotsLocked(
+            maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount)) {
+        BQ_LOGE("setMaxAcquiredBufferCount: BufferQueue failed to adjust the "
+                "number of available slots. Delta = %d",
+                maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount);
+        return BAD_VALUE;
+    }
+
     BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
     mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
+    VALIDATE_CONSISTENCY();
     return NO_ERROR;
 }
 
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index c24ad19..5c80694 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -20,6 +20,12 @@
 
 #define EGL_EGLEXT_PROTOTYPES
 
+#if DEBUG_ONLY_CODE
+#define VALIDATE_CONSISTENCY() do { validateConsistencyLocked(); } while (0)
+#else
+#define VALIDATE_CONSISTENCY()
+#endif
+
 #include <inttypes.h>
 
 #include <gui/BufferItem.h>
@@ -52,6 +58,8 @@
     mQueue(),
     mFreeSlots(),
     mFreeBuffers(),
+    mUnusedSlots(),
+    mActiveBuffers(),
     mDequeueCondition(),
     mDequeueBufferCannotBlock(false),
     mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
@@ -82,8 +90,14 @@
             BQ_LOGE("createGraphicBufferAlloc failed");
         }
     }
-    for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
-        mFreeSlots.insert(slot);
+
+    int numStartingBuffers = getMaxBufferCountLocked();
+    for (int s = 0; s < numStartingBuffers; s++) {
+        mFreeSlots.insert(s);
+    }
+    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
+            s++) {
+        mUnusedSlots.push_front(s);
     }
 }
 
@@ -113,32 +127,26 @@
             mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(),
             fifo.string());
 
-    // Trim the free buffers so as to not spam the dump
-    int maxBufferCount = 0;
-    for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
-        const BufferSlot& slot(mSlots[s]);
-        if (!slot.mBufferState.isFree() ||
-                slot.mGraphicBuffer != NULL) {
-            maxBufferCount = s + 1;
-            break;
-        }
+    for (int s : mActiveBuffers) {
+        const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+        result.appendFormat("%s%s[%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+                prefix, (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
+                buffer.get(), mSlots[s].mBufferState.string(), buffer->handle,
+                buffer->width, buffer->height, buffer->stride, buffer->format);
+
+    }
+    for (int s : mFreeBuffers) {
+        const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+        result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+                prefix, s, buffer.get(), mSlots[s].mBufferState.string(),
+                buffer->handle, buffer->width, buffer->height, buffer->stride,
+                buffer->format);
     }
 
-    for (int s = 0; s < maxBufferCount; ++s) {
-        const BufferSlot& slot(mSlots[s]);
-        const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
-        result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
-                (slot.mBufferState.isAcquired()) ? ">" : " ",
-                s, buffer.get(),
-                slot.mBufferState.string());
-
-        if (buffer != NULL) {
-            result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
-                    buffer->width, buffer->height, buffer->stride,
-                    buffer->format);
-        }
-
-        result.append("\n");
+    for (int s : mFreeSlots) {
+        const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+        result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+                buffer.get(), mSlots[s].mBufferState.string());
     }
 }
 
@@ -156,44 +164,33 @@
     return getMinUndequeuedBufferCountLocked() + 1;
 }
 
+int BufferQueueCore::getMaxBufferCountLocked(bool asyncMode,
+        bool dequeueBufferCannotBlock, int maxBufferCount) const {
+    int maxCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
+            ((asyncMode || dequeueBufferCannotBlock) ? 1 : 0);
+    maxCount = std::min(maxBufferCount, maxCount);
+    return maxCount;
+}
+
 int BufferQueueCore::getMaxBufferCountLocked() const {
     int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
-            (mAsyncMode || mDequeueBufferCannotBlock ? 1 : 0);
+            ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0);
 
     // limit maxBufferCount by mMaxBufferCount always
     maxBufferCount = std::min(mMaxBufferCount, maxBufferCount);
 
-    // Any buffers that are dequeued by the producer or sitting in the queue
-    // waiting to be consumed need to have their slots preserved. Such buffers
-    // will temporarily keep the max buffer count up until the slots no longer
-    // need to be preserved.
-    for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        BufferState state = mSlots[s].mBufferState;
-        if (state.isQueued() || state.isDequeued()) {
-            maxBufferCount = s + 1;
-        }
-    }
-
     return maxBufferCount;
 }
 
-void BufferQueueCore::freeBufferLocked(int slot, bool validate) {
-    BQ_LOGV("freeBufferLocked: slot %d", slot);
-    bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL;
+void BufferQueueCore::clearBufferSlotLocked(int slot) {
+    BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
+
     mSlots[slot].mGraphicBuffer.clear();
-    if (mSlots[slot].mBufferState.isAcquired()) {
-        mSlots[slot].mNeedsCleanupOnRelease = true;
-    }
-    if (!mSlots[slot].mBufferState.isFree()) {
-        mFreeSlots.insert(slot);
-    } else if (hadBuffer) {
-        // If the slot was FREE, but we had a buffer, we need to move this slot
-        // from the free buffers list to the the free slots list
-        mFreeBuffers.remove(slot);
-        mFreeSlots.insert(slot);
-    }
-    mSlots[slot].mAcquireCalled = false;
+    mSlots[slot].mBufferState.reset();
+    mSlots[slot].mRequestBufferCalled = false;
     mSlots[slot].mFrameNumber = 0;
+    mSlots[slot].mAcquireCalled = false;
+    mSlots[slot].mNeedsReallocation = true;
 
     // Destroy fence as BufferQueue now takes ownership
     if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
@@ -201,35 +198,63 @@
         mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
     }
     mSlots[slot].mFence = Fence::NO_FENCE;
-    if (validate) {
-        validateConsistencyLocked();
-    }
+    mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
 }
 
 void BufferQueueCore::freeAllBuffersLocked() {
-    mBufferHasBeenQueued = false;
-    for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        freeBufferLocked(s, false);
-        mSlots[s].mBufferState.reset();
+    for (int s : mFreeSlots) {
+        clearBufferSlotLocked(s);
     }
-    mSingleBufferSlot = INVALID_BUFFER_SLOT;
-    validateConsistencyLocked();
+
+    for (int s : mFreeBuffers) {
+        mFreeSlots.insert(s);
+        clearBufferSlotLocked(s);
+    }
+    mFreeBuffers.clear();
+
+    for (int s : mActiveBuffers) {
+        mFreeSlots.insert(s);
+        clearBufferSlotLocked(s);
+    }
+    mActiveBuffers.clear();
+
+    for (auto& b : mQueue) {
+        b.mIsStale = true;
+    }
+
+    VALIDATE_CONSISTENCY();
 }
 
-bool BufferQueueCore::stillTracking(const BufferItem* item) const {
-    const BufferSlot& slot = mSlots[item->mSlot];
-
-    BQ_LOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } "
-            "slot { slot=%d/%" PRIu64 " buffer=%p }",
-            item->mSlot, item->mFrameNumber,
-            (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
-            item->mSlot, slot.mFrameNumber,
-            (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
-
-    // Compare item with its original buffer slot. We can check the slot as
-    // the buffer would not be moved to a different slot by the producer.
-    return (slot.mGraphicBuffer != NULL) &&
-           (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
+bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
+    if (delta >= 0) {
+        while (delta > 0) {
+            if (mUnusedSlots.empty()) {
+                return false;
+            }
+            int slot = mUnusedSlots.back();
+            mUnusedSlots.pop_back();
+            mFreeSlots.insert(slot);
+            delta--;
+        }
+    } else {
+        while (delta < 0) {
+            if (!mFreeSlots.empty()) {
+                auto slot = mFreeSlots.begin();
+                clearBufferSlotLocked(*slot);
+                mUnusedSlots.push_back(*slot);
+                mFreeSlots.erase(slot);
+            } else if (!mFreeBuffers.empty()) {
+                int slot = mFreeBuffers.back();
+                clearBufferSlotLocked(slot);
+                mUnusedSlots.push_back(slot);
+                mFreeBuffers.pop_back();
+            } else {
+                return false;
+            }
+            delta++;
+        }
+    }
+    return true;
 }
 
 void BufferQueueCore::waitWhileAllocatingLocked() const {
@@ -239,49 +264,131 @@
     }
 }
 
+#if DEBUG_ONLY_CODE
 void BufferQueueCore::validateConsistencyLocked() const {
     static const useconds_t PAUSE_TIME = 0;
+    int allocatedSlots = 0;
     for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
         bool isInFreeSlots = mFreeSlots.count(slot) != 0;
         bool isInFreeBuffers =
                 std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
                 mFreeBuffers.cend();
-        if (mSlots[slot].mBufferState.isFree() &&
-                !mSlots[slot].mBufferState.isShared()) {
-            if (mSlots[slot].mGraphicBuffer == NULL) {
-                if (!isInFreeSlots) {
-                    BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot);
-                    usleep(PAUSE_TIME);
-                }
-                if (isInFreeBuffers) {
-                    BQ_LOGE("Slot %d is in mFreeSlots "
-                            "but is also in mFreeBuffers", slot);
-                    usleep(PAUSE_TIME);
-                }
-            } else {
-                if (!isInFreeBuffers) {
-                    BQ_LOGE("Slot %d is FREE but is not in mFreeBuffers", slot);
-                    usleep(PAUSE_TIME);
-                }
-                if (isInFreeSlots) {
-                    BQ_LOGE("Slot %d is in mFreeBuffers "
-                            "but is also in mFreeSlots", slot);
-                    usleep(PAUSE_TIME);
-                }
-            }
-        } else {
+        bool isInActiveBuffers = mActiveBuffers.count(slot) != 0;
+        bool isInUnusedSlots =
+                std::find(mUnusedSlots.cbegin(), mUnusedSlots.cend(), slot) !=
+                mUnusedSlots.cend();
+
+        if (isInFreeSlots || isInFreeBuffers || isInActiveBuffers) {
+            allocatedSlots++;
+        }
+
+        if (isInUnusedSlots) {
             if (isInFreeSlots) {
-                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)",
-                        slot, mSlots[slot].mBufferState.string());
+                BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeSlots", slot);
                 usleep(PAUSE_TIME);
             }
             if (isInFreeBuffers) {
-                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)",
-                        slot, mSlots[slot].mBufferState.string());
+                BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeBuffers", slot);
                 usleep(PAUSE_TIME);
             }
+            if (isInActiveBuffers) {
+                BQ_LOGE("Slot %d is in mUnusedSlots and in mActiveBuffers",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (!mSlots[slot].mBufferState.isFree()) {
+                BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer != NULL) {
+                BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+        } else if (isInFreeSlots) {
+            if (isInUnusedSlots) {
+                BQ_LOGE("Slot %d is in mFreeSlots and in mUnusedSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeBuffers) {
+                BQ_LOGE("Slot %d is in mFreeSlots and in mFreeBuffers", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInActiveBuffers) {
+                BQ_LOGE("Slot %d is in mFreeSlots and in mActiveBuffers", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (!mSlots[slot].mBufferState.isFree()) {
+                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer != NULL) {
+                BQ_LOGE("Slot %d is in mFreeSlots but has a buffer",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+        } else if (isInFreeBuffers) {
+            if (isInUnusedSlots) {
+                BQ_LOGE("Slot %d is in mFreeBuffers and in mUnusedSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeSlots) {
+                BQ_LOGE("Slot %d is in mFreeBuffers and in mFreeSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInActiveBuffers) {
+                BQ_LOGE("Slot %d is in mFreeBuffers and in mActiveBuffers",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (!mSlots[slot].mBufferState.isFree()) {
+                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer == NULL) {
+                BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot);
+                usleep(PAUSE_TIME);
+            }
+        } else if (isInActiveBuffers) {
+            if (isInUnusedSlots) {
+                BQ_LOGE("Slot %d is in mActiveBuffers and in mUnusedSlots",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeSlots) {
+                BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeSlots", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeBuffers) {
+                BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeBuffers",
+                        slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mBufferState.isFree() &&
+                    !mSlots[slot].mBufferState.isShared()) {
+                BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot);
+                usleep(PAUSE_TIME);
+            }
+            if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) {
+                BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot);
+                usleep(PAUSE_TIME);
+            }
+        } else {
+            BQ_LOGE("Slot %d isn't in any of mUnusedSlots, mFreeSlots, "
+                    "mFreeBuffers, or mActiveBuffers", slot);
+            usleep(PAUSE_TIME);
         }
     }
+
+    if (allocatedSlots != getMaxBufferCountLocked()) {
+        BQ_LOGE("Number of allocated slots is incorrect. Allocated = %d, "
+                "Should be %d (%zu free slots, %zu free buffers, "
+                "%zu activeBuffers, %zu unusedSlots)", allocatedSlots,
+                getMaxBufferCountLocked(), mFreeSlots.size(),
+                mFreeBuffers.size(), mActiveBuffers.size(),
+                mUnusedSlots.size());
+    }
 }
+#endif
 
 } // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5b1aaa0..3c06899 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -20,6 +20,12 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
+#if DEBUG_ONLY_CODE
+#define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0)
+#else
+#define VALIDATE_CONSISTENCY()
+#endif
+
 #define EGL_EGLEXT_PROTOTYPES
 
 #include <gui/BufferItem.h>
@@ -96,7 +102,7 @@
         }
 
         // There must be no dequeued buffers when changing the buffer count.
-        for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+        for (int s : mCore->mActiveBuffers) {
             if (mSlots[s].mBufferState.isDequeued()) {
                 BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
                 return BAD_VALUE;
@@ -132,8 +138,15 @@
         // buffers and will release all of its buffer references. We don't
         // clear the queue, however, so that currently queued buffers still
         // get displayed.
-        mCore->freeAllBuffersLocked();
+        if (!mCore->adjustAvailableSlotsLocked(
+                maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount)) {
+            BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue failed to adjust "
+                    "the number of available slots. Delta = %d",
+                    maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount);
+            return BAD_VALUE;
+        }
         mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+        VALIDATE_CONSISTENCY();
         mCore->mDequeueCondition.broadcast();
         listener = mCore->mConsumerListener;
     } // Autolock scope
@@ -172,7 +185,17 @@
             return BAD_VALUE;
         }
 
+        int delta = mCore->getMaxBufferCountLocked(async,
+                mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount)
+                - mCore->getMaxBufferCountLocked();
+
+        if (!mCore->adjustAvailableSlotsLocked(delta)) {
+            BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of "
+                    "available slots. Delta = %d", delta);
+            return BAD_VALUE;
+        }
         mCore->mAsyncMode = async;
+        VALIDATE_CONSISTENCY();
         mCore->mDequeueCondition.broadcast();
         listener = mCore->mConsumerListener;
     } // Autolock scope
@@ -188,25 +211,22 @@
     if (mCore->mFreeBuffers.empty()) {
         return BufferQueueCore::INVALID_BUFFER_SLOT;
     }
-    auto slot = mCore->mFreeBuffers.front();
+    int slot = mCore->mFreeBuffers.front();
     mCore->mFreeBuffers.pop_front();
     return slot;
 }
 
-int BufferQueueProducer::getFreeSlotLocked(int maxBufferCount) const {
+int BufferQueueProducer::getFreeSlotLocked() const {
     if (mCore->mFreeSlots.empty()) {
         return BufferQueueCore::INVALID_BUFFER_SLOT;
     }
-    auto slot = *(mCore->mFreeSlots.begin());
-    if (slot < maxBufferCount) {
-        mCore->mFreeSlots.erase(slot);
-        return slot;
-    }
-    return BufferQueueCore::INVALID_BUFFER_SLOT;
+    auto slot = mCore->mFreeSlots.begin();
+    mCore->mFreeSlots.erase(slot);
+    return *slot;
 }
 
 status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
-        int* found, status_t* returnFlags) const {
+        int* found) const {
     auto callerString = (caller == FreeSlotCaller::Dequeue) ?
             "dequeueBuffer" : "attachBuffer";
     bool tryAgain = true;
@@ -216,20 +236,9 @@
             return NO_INIT;
         }
 
-        const int maxBufferCount = mCore->getMaxBufferCountLocked();
-
-        // Free up any buffers that are in slots beyond the max buffer count
-        for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-            assert(mSlots[s].mBufferState.isFree());
-            if (mSlots[s].mGraphicBuffer != NULL) {
-                mCore->freeBufferLocked(s);
-                *returnFlags |= RELEASE_ALL_BUFFERS;
-            }
-        }
-
         int dequeuedCount = 0;
         int acquiredCount = 0;
-        for (int s = 0; s < maxBufferCount; ++s) {
+        for (int s : mCore->mActiveBuffers) {
             if (mSlots[s].mBufferState.isDequeued()) {
                 ++dequeuedCount;
             }
@@ -254,6 +263,7 @@
         // our slots are empty but we have many buffers in the queue. This can
         // cause us to run out of memory if we outrun the consumer. Wait here if
         // it looks like we have too many buffers queued up.
+        const int maxBufferCount = mCore->getMaxBufferCountLocked();
         bool tooManyBuffers = mCore->mQueue.size()
                             > static_cast<size_t>(maxBufferCount);
         if (tooManyBuffers) {
@@ -268,15 +278,15 @@
             } else {
                 if (caller == FreeSlotCaller::Dequeue) {
                     // If we're calling this from dequeue, prefer free buffers
-                    auto slot = getFreeBufferLocked();
+                    int slot = getFreeBufferLocked();
                     if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                         *found = slot;
                     } else if (mCore->mAllowAllocation) {
-                        *found = getFreeSlotLocked(maxBufferCount);
+                        *found = getFreeSlotLocked();
                     }
                 } else {
                     // If we're calling this from attach, prefer free slots
-                    auto slot = getFreeSlotLocked(maxBufferCount);
+                    int slot = getFreeSlotLocked();
                     if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                         *found = slot;
                     } else {
@@ -369,7 +379,7 @@
         int found = BufferItem::INVALID_BUFFER_SLOT;
         while (found == BufferItem::INVALID_BUFFER_SLOT) {
             status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
-                    &found, &returnFlags);
+                    &found);
             if (status != NO_ERROR) {
                 return status;
             }
@@ -388,24 +398,36 @@
             // requested attributes, we free it and attempt to get another one.
             if (!mCore->mAllowAllocation) {
                 if (buffer->needsReallocation(width, height, format, usage)) {
-                    if (mCore->mSingleBufferMode &&
-                            mCore->mSingleBufferSlot == found) {
+                    if (mCore->mSingleBufferSlot == found) {
                         BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                                 "buffer");
                         return BAD_VALUE;
                     }
-
-                    mCore->freeBufferLocked(found);
+                    mCore->mFreeSlots.insert(found);
+                    mCore->clearBufferSlotLocked(found);
                     found = BufferItem::INVALID_BUFFER_SLOT;
                     continue;
                 }
             }
         }
 
+        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+        if (mCore->mSingleBufferSlot == found &&
+                buffer->needsReallocation(width,  height, format, usage)) {
+            BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
+                    "buffer");
+
+            return BAD_VALUE;
+        }
+
+        if (mCore->mSingleBufferSlot != found) {
+            mCore->mActiveBuffers.insert(found);
+        }
         *outSlot = found;
         ATRACE_BUFFER_INDEX(found);
 
-        attachedByConsumer = mSlots[found].mAttachedByConsumer;
+        attachedByConsumer = mSlots[found].mNeedsReallocation;
+        mSlots[found].mNeedsReallocation = false;
 
         mSlots[found].mBufferState.dequeue();
 
@@ -417,7 +439,6 @@
             mSlots[found].mBufferState.mShared = true;
         }
 
-        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
         if ((buffer == NULL) ||
                 buffer->needsReallocation(width, height, format, usage))
         {
@@ -452,8 +473,6 @@
         *outFence = mSlots[found].mFence;
         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
         mSlots[found].mFence = Fence::NO_FENCE;
-
-        mCore->validateConsistencyLocked();
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -481,6 +500,8 @@
                 BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                 return NO_INIT;
             }
+
+            VALIDATE_CONSISTENCY();
         } // Autolock scope
     }
 
@@ -527,9 +548,8 @@
         return NO_INIT;
     }
 
-    if (mCore->mSingleBufferMode) {
-        BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer"
-                "mode");
+    if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
+        BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode");
         return BAD_VALUE;
     }
 
@@ -548,9 +568,11 @@
     }
 
     mSlots[slot].mBufferState.detachProducer();
-    mCore->freeBufferLocked(slot);
+    mCore->mActiveBuffers.erase(slot);
+    mCore->mFreeSlots.insert(slot);
+    mCore->clearBufferSlotLocked(slot);
     mCore->mDequeueCondition.broadcast();
-    mCore->validateConsistencyLocked();
+    VALIDATE_CONSISTENCY();
 
     return NO_ERROR;
 }
@@ -593,13 +615,14 @@
 
     int found = mCore->mFreeBuffers.front();
     mCore->mFreeBuffers.remove(found);
+    mCore->mFreeSlots.insert(found);
 
     BQ_LOGV("detachNextBuffer detached slot %d", found);
 
     *outBuffer = mSlots[found].mGraphicBuffer;
     *outFence = mSlots[found].mFence;
-    mCore->freeBufferLocked(found);
-    mCore->validateConsistencyLocked();
+    mCore->clearBufferSlotLocked(found);
+    VALIDATE_CONSISTENCY();
 
     return NO_ERROR;
 }
@@ -644,8 +667,7 @@
 
     status_t returnFlags = NO_ERROR;
     int found;
-    status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found,
-            &returnFlags);
+    status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found);
     if (status != NO_ERROR) {
         return status;
     }
@@ -666,8 +688,9 @@
     mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mRequestBufferCalled = true;
-
-    mCore->validateConsistencyLocked();
+    mSlots[*outSlot].mAcquireCalled = false;
+    mCore->mActiveBuffers.insert(found);
+    VALIDATE_CONSISTENCY();
 
     return returnFlags;
 }
@@ -722,11 +745,9 @@
             return NO_INIT;
         }
 
-        const int maxBufferCount = mCore->getMaxBufferCountLocked();
-
-        if (slot < 0 || slot >= maxBufferCount) {
+        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
             BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
-                    slot, maxBufferCount);
+                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
             return BAD_VALUE;
         } else if (!mSlots[slot].mBufferState.isDequeued()) {
             BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -807,9 +828,8 @@
             // state to see if we need to replace it
             BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
             if (front->mIsDroppable) {
-                // If the front queued buffer is still being tracked, we first
-                // mark it as freed
-                if (mCore->stillTracking(front)) {
+
+                if (!front->mIsStale) {
                     mSlots[front->mSlot].mBufferState.freeQueued();
 
                     // After leaving single buffer mode, the shared buffer will
@@ -821,9 +841,11 @@
                     }
                     // Don't put the shared buffer on the free list.
                     if (!mSlots[front->mSlot].mBufferState.isShared()) {
-                        mCore->mFreeBuffers.push_front(front->mSlot);
+                        mCore->mActiveBuffers.erase(front->mSlot);
+                        mCore->mFreeBuffers.push_back(front->mSlot);
                     }
                 }
+
                 // Overwrite the droppable buffer with the incoming one
                 *front = item;
                 frameReplacedListener = mCore->mConsumerListener;
@@ -845,7 +867,7 @@
         // Take a ticket for the callback functions
         callbackTicket = mNextCallbackTicket++;
 
-        mCore->validateConsistencyLocked();
+        VALIDATE_CONSISTENCY();
     } // Autolock scope
 
     // Don't send the GraphicBuffer through the callback, and don't send
@@ -926,11 +948,13 @@
 
     // Don't put the shared buffer on the free list.
     if (!mSlots[slot].mBufferState.isShared()) {
-        mCore->mFreeBuffers.push_front(slot);
+        mCore->mActiveBuffers.erase(slot);
+        mCore->mFreeBuffers.push_back(slot);
     }
+
     mSlots[slot].mFence = fence;
     mCore->mDequeueCondition.broadcast();
-    mCore->validateConsistencyLocked();
+    VALIDATE_CONSISTENCY();
 
     return NO_ERROR;
 }
@@ -1020,6 +1044,17 @@
         return BAD_VALUE;
     }
 
+    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+            mDequeueTimeout < 0 ?
+            mCore->mConsumerControlledByApp && producerControlledByApp : false,
+            mCore->mMaxBufferCount) -
+            mCore->getMaxBufferCountLocked();
+    if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
+                "slots. Delta = %d", delta);
+        return BAD_VALUE;
+    }
+
     int status = NO_ERROR;
     switch (api) {
         case NATIVE_WINDOW_API_EGL:
@@ -1056,8 +1091,9 @@
         mCore->mDequeueBufferCannotBlock =
                 mCore->mConsumerControlledByApp && producerControlledByApp;
     }
-    mCore->mAllowAllocation = true;
 
+    mCore->mAllowAllocation = true;
+    VALIDATE_CONSISTENCY();
     return status;
 }
 
@@ -1094,6 +1130,8 @@
                         token->unlinkToDeath(
                                 static_cast<IBinder::DeathRecipient*>(this));
                     }
+                    mCore->mSingleBufferSlot =
+                            BufferQueueCore::INVALID_BUFFER_SLOT;
                     mCore->mConnectedProducerListener = NULL;
                     mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
                     mCore->mSidebandStream.clear();
@@ -1138,7 +1176,6 @@
         PixelFormat format, uint32_t usage) {
     ATRACE_CALL();
     while (true) {
-        Vector<int> freeSlots;
         size_t newBufferCount = 0;
         uint32_t allocWidth = 0;
         uint32_t allocHeight = 0;
@@ -1154,32 +1191,11 @@
                 return;
             }
 
-            int currentBufferCount = 0;
-            for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
-                if (mSlots[slot].mGraphicBuffer != NULL) {
-                    ++currentBufferCount;
-                } else {
-                    if (!mSlots[slot].mBufferState.isFree()) {
-                        BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
-                                slot);
-                        continue;
-                    }
-
-                    freeSlots.push_back(slot);
-                }
-            }
-
-            int maxBufferCount = mCore->getMaxBufferCountLocked();
-            BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
-                    currentBufferCount, maxBufferCount);
-            if (maxBufferCount <= currentBufferCount)
-                return;
-            newBufferCount =
-                    static_cast<size_t>(maxBufferCount - currentBufferCount);
-            if (freeSlots.size() < newBufferCount) {
-                BQ_LOGE("allocateBuffers: ran out of free slots");
+            newBufferCount = mCore->mFreeSlots.size();
+            if (newBufferCount == 0) {
                 return;
             }
+
             allocWidth = width > 0 ? width : mCore->mDefaultWidth;
             allocHeight = height > 0 ? height : mCore->mDefaultHeight;
             allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
@@ -1221,29 +1237,28 @@
             }
 
             for (size_t i = 0; i < newBufferCount; ++i) {
-                int slot = freeSlots[i];
-                if (!mSlots[slot].mBufferState.isFree()) {
-                    // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
-                    // allocated.
-                    BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
-                            "Dropping allocated buffer.", slot);
+                if (mCore->mFreeSlots.empty()) {
+                    BQ_LOGV("allocateBuffers: a slot was occupied while "
+                            "allocating. Dropping allocated buffer.");
                     continue;
                 }
-                mCore->freeBufferLocked(slot); // Clean up the slot first
-                mSlots[slot].mGraphicBuffer = buffers[i];
-                mSlots[slot].mFence = Fence::NO_FENCE;
+                auto slot = mCore->mFreeSlots.begin();
+                mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
+                mSlots[*slot].mGraphicBuffer = buffers[i];
+                mSlots[*slot].mFence = Fence::NO_FENCE;
 
                 // freeBufferLocked puts this slot on the free slots list. Since
                 // we then attached a buffer, move the slot to free buffer list.
                 mCore->mFreeSlots.erase(slot);
-                mCore->mFreeBuffers.push_front(slot);
+                mCore->mFreeBuffers.push_front(*slot);
 
-                BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
+                BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d",
+                        *slot);
             }
 
             mCore->mIsAllocating = false;
             mCore->mIsAllocatingCondition.broadcast();
-            mCore->validateConsistencyLocked();
+            VALIDATE_CONSISTENCY();
         } // Autolock scope
     }
 }
@@ -1297,8 +1312,18 @@
     BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
 
     Mutex::Autolock lock(mCore->mMutex);
+    int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
+            mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
+    if (!mCore->adjustAvailableSlotsLocked(delta)) {
+        BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of "
+                "available slots. Delta = %d", delta);
+        return BAD_VALUE;
+    }
+
     mDequeueTimeout = timeout;
     mCore->mDequeueBufferCannotBlock = false;
+
+    VALIDATE_CONSISTENCY();
     return NO_ERROR;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9e90ad0..6fc55c3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -759,6 +759,13 @@
         *outFence = Fence::NO_FENCE;
     }
 
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        if (mSlots[i].buffer != NULL &&
+                mSlots[i].buffer->handle == buffer->handle) {
+            mSlots[i].buffer = NULL;
+        }
+    }
+
     return NO_ERROR;
 }
 
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index b0798a1..f368d75 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -21,6 +21,7 @@
 #include "egldefs.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -306,7 +307,8 @@
         // Sanity check the size before trying to mmap it.
         size_t fileSize = statBuf.st_size;
         if (fileSize > maxTotalSize * 2) {
-            ALOGE("cache file is too large: %#llx", statBuf.st_size);
+            ALOGE("cache file is too large: %#" PRIx64,
+                  static_cast<off64_t>(statBuf.st_size));
             close(fd);
             return;
         }
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index ab21c8f..6a9d7b6 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -271,7 +271,7 @@
         // there are no reference to them, it which case, we're free to
         // delete them.
         size_t count = objects.size();
-        ALOGW_IF(count, "eglTerminate() called w/ %d objects remaining", count);
+        ALOGW_IF(count, "eglTerminate() called w/ %zu objects remaining", count);
         for (size_t i=0 ; i<count ; i++) {
             egl_object_t* o = objects.itemAt(i);
             o->destroy();
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index f5a9f58..17a8304 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -37,7 +37,7 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-struct egl_display_t;
+class egl_display_t;
 
 class egl_object_t {
     egl_display_t *display;
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 04919f7..eed14ab 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -134,7 +134,7 @@
     case AMOTION_EVENT_ACTION_POINTER_DOWN:
     case AMOTION_EVENT_ACTION_POINTER_UP: {
         int32_t index = getMotionEventActionPointerIndex(action);
-        return index >= 0 && size_t(index) < pointerCount;
+        return index >= 0 && index < pointerCount;
     }
     case AMOTION_EVENT_ACTION_BUTTON_PRESS:
     case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2d8eaef..7ae36d8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -170,7 +170,7 @@
             << "Should reject motion events with pointer down index too large.";
 
     event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -191,7 +191,7 @@
             << "Should reject motion events with pointer up index too large.";
 
     event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-            AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
             0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerProperties, pointerCoords);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 42bc865..a7fe69c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1528,8 +1528,8 @@
     NotifySwitchArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
-    ASSERT_EQ((1 << SW_LID) | (1 << SW_JACK_PHYSICAL_INSERT), args.switchValues);
-    ASSERT_EQ((1 << SW_LID) | (1 << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
+    ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
+    ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
             args.switchMask);
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 }