Merge "Fix broken opto-electronic transfer functions"
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 3987fce..e11bf30 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -58,8 +58,9 @@
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
 LOCAL_SRC_FILES := \
+        binder/android/os/IDumpstate.aidl \
         binder/android/os/IDumpstateListener.aidl \
-        binder/android/os/IDumpstate.aidl
+        binder/android/os/IDumpstateToken.aidl
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ce78ec6..f2dcf2b 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -25,6 +25,10 @@
 namespace android {
 namespace os {
 
+namespace {
+class DumpstateToken : public BnDumpstateToken {};
+}
+
 DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
 }
 
@@ -45,33 +49,38 @@
 }
 
 binder::Status DumpstateService::setListener(const std::string& name,
-                                             const sp<IDumpstateListener>& listener, bool* set) {
+                                             const sp<IDumpstateListener>& listener,
+                                             sp<IDumpstateToken>* returned_token) {
+    *returned_token = nullptr;
     if (name.empty()) {
         MYLOGE("setListener(): name not set\n");
-        *set = false;
         return binder::Status::ok();
     }
     if (listener == nullptr) {
         MYLOGE("setListener(): listener not set\n");
-        *set = false;
         return binder::Status::ok();
     }
     std::lock_guard<std::mutex> lock(lock_);
     if (ds_.listener_ != nullptr) {
         MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
-        *set = false;
         return binder::Status::ok();
     }
+
     ds_.listener_name_ = name;
     ds_.listener_ = listener;
-    *set = true;
+    *returned_token = new DumpstateToken();
+
     return binder::Status::ok();
 }
 
 status_t DumpstateService::dump(int fd, const Vector<String16>&) {
-    dprintf(fd, "id: %lu\n", ds_.id_);
+    dprintf(fd, "id: %d\n", ds_.id_);
     dprintf(fd, "pid: %d\n", ds_.pid_);
-    dprintf(fd, "progress: %d / %d\n", ds_.progress_, ds_.weight_total_);
+    dprintf(fd, "update_progress: %s\n", ds_.update_progress_ ? "true" : "false");
+    dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_);
+    dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_);
+    dprintf(fd, "progress:\n");
+    ds_.progress_->Dump(fd, "  ");
     dprintf(fd, "args: %s\n", ds_.args_.c_str());
     dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
     dprintf(fd, "version: %s\n", ds_.version_.c_str());
@@ -79,7 +88,8 @@
     dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
     dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
     dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
-    dprintf(fd, "path: %s\n", ds_.extra_options_.c_str());
+    dprintf(fd, "path: %s\n", ds_.path_.c_str());
+    dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
     dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
     dprintf(fd, "name: %s\n", ds_.name_.c_str());
     dprintf(fd, "now: %ld\n", ds_.now_);
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 544a7b6..4352d3d 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -23,6 +23,7 @@
 #include <binder/BinderService.h>
 
 #include "android/os/BnDumpstate.h"
+#include "android/os/BnDumpstateToken.h"
 #include "dumpstate.h"
 
 namespace android {
@@ -37,7 +38,7 @@
 
     status_t dump(int fd, const Vector<String16>& args) override;
     binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
-                               bool* set) override;
+                               sp<IDumpstateToken>* returned_token) override;
 
   private:
     Dumpstate& ds_;
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index e585a0e..4becccf 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.os.IDumpstateListener;
+import android.os.IDumpstateToken;
 
 /**
   * Binder interface for the currently running dumpstate process.
@@ -25,9 +26,10 @@
 interface IDumpstate {
 
     /*
-     * Sets the listener for dumpstate progress.
+     * Sets the listener for this dumpstate progress.
      *
-     * Returns true if the listener was set (it's like a Highlander: There Can be Only One).
+     * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
+     * set (the listener behaves like a Highlander: There Can be Only One).
      */
-    boolean setListener(@utf8InCpp String name, IDumpstateListener listener);
+    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
 }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
new file mode 100644
index 0000000..7f74ceb
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
@@ -0,0 +1,24 @@
+/**
+ * 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.
+ */
+
+package android.os;
+
+/**
+  * Token used by the IDumpstateListener to watch for dumpstate death.
+  * {@hide}
+  */
+interface IDumpstateToken {
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 80a1b07..ea70fe5 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -683,7 +683,7 @@
     JustDumpFile("", "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
     printf("Bugreport format version: %s\n", version_.c_str());
-    printf("Dumpstate info: id=%lu pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
+    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
            dry_run_, args_.c_str(), extra_options_.c_str());
     printf("\n");
 }
@@ -1154,10 +1154,10 @@
     DumpModemLogs();
 
     printf("========================================================\n");
-    printf("== Final progress (pid %d): %d/%d (originally %d)\n", ds.pid_, ds.progress_,
-           ds.weight_total_, WEIGHT_TOTAL);
+    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
+           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
     printf("========================================================\n");
-    printf("== dumpstate: done (id %lu)\n", ds.id_);
+    printf("== dumpstate: done (id %d)\n", ds.id_);
     printf("========================================================\n");
 }
 
@@ -1222,7 +1222,7 @@
     char date[80];
     time_t the_real_now_please_stand_up = time(nullptr);
     strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
-    MYLOGD("dumpstate id %lu finished around %s (%ld s)\n", ds.id_, date,
+    MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
            the_real_now_please_stand_up - ds.now_);
 
     if (!ds.AddZipEntry(entry_name, tmp_path_)) {
@@ -1253,13 +1253,9 @@
     // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
     ds.zip_file.reset(nullptr);
 
-    if (IsUserBuild()) {
-        MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
-        if (remove(tmp_path_.c_str()) != 0) {
-            ALOGW("remove(%s): %s\n", tmp_path_.c_str(), strerror(errno));
-        }
-    } else {
-        MYLOGD("Keeping temporary file %s on non-user build\n", tmp_path_.c_str())
+    MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+    if (remove(tmp_path_.c_str()) != 0) {
+        MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
     }
 
     return true;
@@ -1418,8 +1414,17 @@
         exit(0);
     }
 
+    /* redirect output if needed */
+    bool is_redirecting = !use_socket && use_outfile;
+
+    // TODO: temporarily set progress until it's part of the Dumpstate constructor
+    std::string stats_path =
+        is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile))
+                       : "";
+    ds.progress_.reset(new Progress(stats_path));
+
     /* gets the sequential id */
-    int last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+    uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
     ds.id_ = ++last_id;
     android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
 
@@ -1445,7 +1450,7 @@
         MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
 
-    MYLOGI("dumpstate info: id=%lu, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
+    MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
            ds.extra_options_.c_str());
 
     MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
@@ -1464,9 +1469,6 @@
         ds.update_progress_ = 1;
     }
 
-    /* redirect output if needed */
-    bool is_redirecting = !use_socket && use_outfile;
-
     if (is_redirecting) {
         ds.bugreport_dir_ = dirname(use_outfile);
         ds.base_name_ = basename(use_outfile);
@@ -1517,7 +1519,7 @@
                      "--es", "android.intent.extra.NAME", ds.name_,
                      "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
                      "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
-                     "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+                     "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
                 };
                 // clang-format on
                 send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
@@ -1720,7 +1722,7 @@
                  "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
                  "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
                  "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
-                 "--ei", "android.intent.extra.MAX", std::to_string(ds.weight_total_),
+                 "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
                  "--es", "android.intent.extra.BUGREPORT", ds.path_,
                  "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
             };
@@ -1743,8 +1745,10 @@
         }
     }
 
-    MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weight_total_, WEIGHT_TOTAL);
-    MYLOGI("done (id %lu)\n", ds.id_);
+    MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
+           ds.progress_->GetInitialMax());
+    ds.progress_->Save();
+    MYLOGI("done (id %d)\n", ds.id_);
 
     if (is_redirecting) {
         fclose(stderr);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0680db2..3d3d7ed 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -184,20 +184,65 @@
 };
 
 /*
- * Estimated total weight of bugreport generation.
+ * Keeps track of current progress and estimated max, saving stats on file to tune up future runs.
  *
- * Each section contributes to the total weight by an individual weight, so the overall progress
- * can be calculated by dividing the all completed weight by the total weight.
+ * Each `dumpstate` section contributes to the total weight by an individual weight, so the overall
+ * progress can be calculated by dividing the estimate max progress by the current progress.
  *
- * This value is defined empirically and it need to be adjusted as more sections are added.
+ * The estimated max progress is initially set to a value (`kDefaultMax) defined empirically, but
+ * it's adjusted after each dumpstate run by storing the average duration in a file.
  *
- * It does not need to match the exact sum of all sections, but ideally it should to be slight more
- * than such sum: a value too high will cause the bugreport to finish before the user expected (for
- * example, jumping from 70% to 100%), while a value too low will cause the progress to get stuck
- * at an almost-finished value (like 99%) for a while.
  */
-// TODO: move to dumpstate.cpp / utils.cpp once it's used in just one file
-static const int WEIGHT_TOTAL = 6500;
+class Progress {
+    friend class ProgressTest;
+    friend class DumpstateTest;
+
+  public:
+    /*
+     * Default estimation of the max duration of a bugreport generation.
+     *
+     * It does not need to match the exact sum of all sections, but ideally it should to be slight
+     * more than such sum: a value too high will cause the bugreport to finish before the user
+     * expected (for example, jumping from 70% to 100%), while a value too low will cause the
+     * progress to get stuck at an almost-finished value (like 99%) for a while.
+     *
+     * This constant is only used when the average duration from previous runs cannot be used.
+     */
+    static const int kDefaultMax;
+
+    Progress(const std::string& path = "");
+
+    // Gets the current progress.
+    int32_t Get() const;
+
+    // Gets the current estimated max progress.
+    int32_t GetMax() const;
+
+    // Gets the initial estimated max progress.
+    int32_t GetInitialMax() const;
+
+    // Increments progress (ignored if not positive).
+    // Returns `true` if the max progress increased as well.
+    bool Inc(int32_t delta);
+
+    // Persist the stats.
+    void Save();
+
+    void Dump(int fd, const std::string& prefix) const;
+
+  private:
+    Progress(int32_t initial_max, float growth_factor,
+             const std::string& path = "");                                // Used by test cases.
+    Progress(int32_t initial_max, int32_t progress, float growth_factor);  // Used by test cases.
+    void Load();
+    int32_t initial_max_;
+    int32_t progress_;
+    int32_t max_;
+    float growth_factor_;
+    int32_t n_runs_;
+    int32_t average_max_;
+    const std::string& path_;
+};
 
 /*
  * List of supported zip format versions.
@@ -310,12 +355,17 @@
      */
     void TakeScreenshot(const std::string& path = "");
 
-    // TODO: members below should be private once refactor is finished
+    /////////////////////////////////////////////////////////////////////
+    // TODO: members below should be private once refactor is finished //
+    /////////////////////////////////////////////////////////////////////
+
+    // TODO: temporary method until Dumpstate object is properly set
+    void SetProgress(std::unique_ptr<Progress> progress);
 
     /*
      * Updates the overall progress of the bugreport generation by the given weight increment.
      */
-    void UpdateProgress(int delta);
+    void UpdateProgress(int32_t delta);
 
     /* Prints the dumpstate header on `stdout`. */
     void PrintHeader() const;
@@ -332,7 +382,7 @@
     // TODO: initialize fields on constructor
 
     // dumpstate id - unique after each device reboot.
-    unsigned long id_;
+    uint32_t id_;
 
     // dumpstate pid
     pid_t pid_;
@@ -340,14 +390,17 @@
     // Whether progress updates should be published.
     bool update_progress_ = false;
 
+    // How frequently the progess should be updated;the listener will only be notificated when the
+    // delta from the previous update is more than the threshold.
+    int32_t update_progress_threshold_ = 100;
+
+    // Last progress that triggered a listener updated
+    int32_t last_updated_progress_;
+
     // Whether it should take an screenshot earlier in the process.
     bool do_early_screenshot_ = false;
 
-    // Currrent progress.
-    int progress_ = 0;
-
-    // Total estimated progress.
-    int weight_total_ = WEIGHT_TOTAL;
+    std::unique_ptr<Progress> progress_;
 
     // When set, defines a socket file-descriptor use to report progress to bugreportz.
     int control_socket_fd_ = -1;
diff --git a/cmds/dumpstate/testdata/empty-file.txt b/cmds/dumpstate/testdata/empty-file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/dumpstate/testdata/empty-file.txt
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
new file mode 100644
index 0000000..dad9fe8
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-NAN.txt
@@ -0,0 +1 @@
+SIX_SIX_SIX 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
new file mode 100644
index 0000000..4facef9
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-negative.txt
@@ -0,0 +1 @@
+-666 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
new file mode 100644
index 0000000..42508f1
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-1st-too-big.txt
@@ -0,0 +1 @@
+4815162342 42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
new file mode 100644
index 0000000..a23ba2c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-NAN.txt
@@ -0,0 +1 @@
+666 FORTY_TWO
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
new file mode 100644
index 0000000..dd529b4
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-negative.txt
@@ -0,0 +1 @@
+666 -42
diff --git a/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
new file mode 100644
index 0000000..b148b46
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-2nd-too-big.txt
@@ -0,0 +1 @@
+666 4815162342
diff --git a/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
new file mode 100644
index 0000000..4a9466d
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-invalid-both-NAN.txt
@@ -0,0 +1 @@
+N_RUNS AVERAGE
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-one-run-no-newline.txt b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
new file mode 100644
index 0000000..0aef60c
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-one-run-no-newline.txt
@@ -0,0 +1 @@
+1 10
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/stats-two-runs.txt b/cmds/dumpstate/testdata/stats-two-runs.txt
new file mode 100644
index 0000000..9af1233
--- /dev/null
+++ b/cmds/dumpstate/testdata/stats-two-runs.txt
@@ -0,0 +1 @@
+2 15
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index d692fda..0d68901 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -38,7 +38,9 @@
 using namespace android;
 
 using ::testing::EndsWith;
+using ::testing::IsNull;
 using ::testing::IsEmpty;
+using ::testing::NotNull;
 using ::testing::StrEq;
 using ::testing::StartsWith;
 using ::testing::Test;
@@ -49,6 +51,7 @@
 
 using os::DumpstateService;
 using os::IDumpstateListener;
+using os::IDumpstateToken;
 
 // Not used on test cases yet...
 void dumpstate_board(void) {
@@ -63,12 +66,50 @@
     MOCK_METHOD0(onAsBinder, IBinder*());
 };
 
-class DumpstateTest : public Test {
+// Base class for all tests in this file
+class DumpstateBaseTest : public Test {
+  protected:
+    const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str());
+    const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/";
+    const std::string kTestDataPath = kFixturesPath + "/testdata/";
+    const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture";
+    const std::string kEchoCommand = "/system/bin/echo";
+
+    /*
+     * Copies a text file fixture to a temporary file, returning it's path.
+     *
+     * Useful in cases where the test case changes the content of the tile.
+     */
+    std::string CopyTextFileFixture(const std::string& relative_name) {
+        std::string from = kTestDataPath + relative_name;
+        // Not using TemporaryFile because it's deleted at the end, and it's useful to keep it
+        // around for poking when the test fails.
+        std::string to = kTestDataPath + relative_name + ".tmp";
+        ALOGD("CopyTextFileFixture: from %s to %s\n", from.c_str(), to.c_str());
+        android::base::RemoveFileIfExists(to);
+        CopyTextFile(from, to);
+        return to.c_str();
+    }
+
+  private:
+    // Need a function that returns void to use assertions -
+    // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#assertion-placement
+    void CopyTextFile(const std::string& from, const std::string& to) {
+        std::string content;
+        ASSERT_TRUE(android::base::ReadFileToString(from, &content)) << "could not read from "
+                                                                     << from;
+        ASSERT_TRUE(android::base::WriteStringToFile(content, to)) << "could not write to " << to;
+    }
+};
+
+class DumpstateTest : public DumpstateBaseTest {
   public:
     void SetUp() {
         SetDryRun(false);
         SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+        ds.progress_.reset(new Progress());
         ds.update_progress_ = false;
+        ds.update_progress_threshold_ = 0;
     }
 
     // Runs a command and capture `stdout` and `stderr`.
@@ -112,63 +153,37 @@
         ASSERT_EQ(2000, (int)uid);
     }
 
-    // TODO: remove when progress is set by Binder callbacks.
-    void AssertSystemProperty(const std::string& key, const std::string& expected_value) {
-        std::string actualValue = android::base::GetProperty(key, "not set");
-        EXPECT_THAT(expected_value, StrEq(actualValue)) << "invalid value for property " << key;
+    void SetProgress(long progress, long initial_max, long threshold = 0) {
+        ds.update_progress_ = true;
+        ds.update_progress_threshold_ = threshold;
+        ds.last_updated_progress_ = 0;
+        ds.progress_.reset(new Progress(initial_max, progress, 1.2));
     }
 
-    // TODO: remove when progress is set by Binder callbacks.
-    std::string GetProgressMessageAndAssertSystemProperties(int progress, int weight_total,
-                                                            int old_weight_total = 0) {
-        EXPECT_EQ(progress, ds.progress_) << "invalid progress";
-        EXPECT_EQ(weight_total, ds.weight_total_) << "invalid weight_total";
+    std::string GetProgressMessage(const std::string& listener_name, int progress, int max,
+                                   int old_max = 0, bool update_progress = true) {
+        EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
+        EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max";
 
-        AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
-                             std::to_string(progress));
+        bool max_increased = old_max > 0;
 
-        bool max_increased = old_weight_total > 0;
-
-        std::string adjustment_message = "";
+        std::string message = "";
         if (max_increased) {
-            AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.max", getpid()),
-                                 std::to_string(weight_total));
-            adjustment_message = android::base::StringPrintf(
-                "Adjusting total weight from %d to %d\n", old_weight_total, weight_total);
+            message =
+                android::base::StringPrintf("Adjusting max progress from %d to %d\n", old_max, max);
         }
 
-        return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
-                                           adjustment_message.c_str(), getpid(), progress,
-                                           weight_total);
-    }
-
-    std::string GetProgressMessage(const std::string& listener_name, int progress, int weight_total,
-                                   int old_weight_total = 0) {
-        EXPECT_EQ(progress, ds.progress_) << "invalid progress";
-        EXPECT_EQ(weight_total, ds.weight_total_) << "invalid weight_total";
-
-        bool max_increased = old_weight_total > 0;
-
-        std::string adjustment_message = "";
-        if (max_increased) {
-            adjustment_message = android::base::StringPrintf(
-                "Adjusting total weight from %d to %d\n", old_weight_total, weight_total);
+        if (update_progress) {
+            message += android::base::StringPrintf("Setting progress (%s): %d/%d\n",
+                                                   listener_name.c_str(), progress, max);
         }
 
-        return android::base::StringPrintf("%sSetting progress (%s): %d/%d\n",
-                                           adjustment_message.c_str(), listener_name.c_str(),
-                                           progress, weight_total);
+        return message;
     }
 
     // `stdout` and `stderr` from the last command ran.
     std::string out, err;
 
-    std::string test_path = dirname(android::base::GetExecutablePath().c_str());
-    std::string fixtures_path = test_path + "/../dumpstate_test_fixture/";
-    std::string test_data_path = fixtures_path + "/testdata/";
-    std::string simple_command = fixtures_path + "dumpstate_test_fixture";
-    std::string echo_command = "/system/bin/echo";
-
     Dumpstate& ds = Dumpstate::GetInstance();
 };
 
@@ -177,52 +192,52 @@
 }
 
 TEST_F(DumpstateTest, RunCommandNoTitle) {
-    EXPECT_EQ(0, RunCommand("", {simple_command}));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandWithTitle) {
-    EXPECT_EQ(0, RunCommand("I AM GROOT", {simple_command}));
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
     EXPECT_THAT(err, StrEq("stderr\n"));
     // We don't know the exact duration, so we check the prefix and suffix
     EXPECT_THAT(out,
-                StartsWith("------ I AM GROOT (" + simple_command + ") ------\nstdout\n------"));
+                StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------"));
     EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
     EXPECT_EQ(
-        0, RunCommand("", {simple_command},
+        0, RunCommand("", {kSimpleCommand},
                       CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandRedirectStderr) {
-    EXPECT_EQ(0, RunCommand("", {simple_command},
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand},
                             CommandOptions::WithTimeout(10).RedirectStderr().Build()));
     EXPECT_THAT(out, IsEmpty());
     EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandWithOneArg) {
-    EXPECT_EQ(0, RunCommand("", {echo_command, "one"}));
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one"}));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, StrEq("one\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
-    EXPECT_EQ(0, RunCommand("", {echo_command, "one", "is", "the", "loniest", "number"}));
+    EXPECT_EQ(0, RunCommand("", {kEchoCommand, "one", "is", "the", "loniest", "number"}));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, StrEq("one is the loniest number\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandDryRun) {
     SetDryRun(true);
-    EXPECT_EQ(0, RunCommand("I AM GROOT", {simple_command}));
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
     // We don't know the exact duration, so we check the prefix and suffix
-    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simple_command +
+    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand +
                                 ") ------\n\t(skipped on dry run)\n------"));
     EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
     EXPECT_THAT(err, IsEmpty());
@@ -230,14 +245,14 @@
 
 TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
     SetDryRun(true);
-    EXPECT_EQ(0, RunCommand("", {simple_command}));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}));
     EXPECT_THAT(out, IsEmpty());
     EXPECT_THAT(err, IsEmpty());
 }
 
 TEST_F(DumpstateTest, RunCommandDryRunAlways) {
     SetDryRun(true);
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Always().Build()));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n"));
 }
@@ -249,28 +264,28 @@
 }
 
 TEST_F(DumpstateTest, RunCommandFails) {
-    EXPECT_EQ(42, RunCommand("", {simple_command, "--exit", "42"}));
-    EXPECT_THAT(out, StrEq("stdout\n*** command '" + simple_command +
+    EXPECT_EQ(42, RunCommand("", {kSimpleCommand, "--exit", "42"}));
+    EXPECT_THAT(out, StrEq("stdout\n*** command '" + kSimpleCommand +
                            " --exit 42' failed: exit code 42\n"));
-    EXPECT_THAT(err, StrEq("stderr\n*** command '" + simple_command +
+    EXPECT_THAT(err, StrEq("stderr\n*** command '" + kSimpleCommand +
                            " --exit 42' failed: exit code 42\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandCrashes) {
-    EXPECT_NE(0, RunCommand("", {simple_command, "--crash"}));
+    EXPECT_NE(0, RunCommand("", {kSimpleCommand, "--crash"}));
     // We don't know the exit code, so check just the prefix.
     EXPECT_THAT(
-        out, StartsWith("stdout\n*** command '" + simple_command + " --crash' failed: exit code"));
+        out, StartsWith("stdout\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
     EXPECT_THAT(
-        err, StartsWith("stderr\n*** command '" + simple_command + " --crash' failed: exit code"));
+        err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code"));
 }
 
 TEST_F(DumpstateTest, RunCommandTimesout) {
-    EXPECT_EQ(-1, RunCommand("", {simple_command, "--sleep", "2"},
+    EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"},
                              CommandOptions::WithTimeout(1).Build()));
-    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simple_command +
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand +
                                 " --sleep 2' timed out after 1"));
-    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simple_command +
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand +
                                 " --sleep 2' timed out after 1"));
 }
 
@@ -279,7 +294,7 @@
     CaptureStderr();
 
     std::thread t([=]() {
-        EXPECT_EQ(SIGTERM, ds.RunCommand("", {simple_command, "--pid", "--sleep", "20"},
+        EXPECT_EQ(SIGTERM, ds.RunCommand("", {kSimpleCommand, "--pid", "--sleep", "20"},
                                          CommandOptions::WithTimeout(100).Always().Build()));
     });
 
@@ -306,93 +321,96 @@
     out = GetCapturedStdout();
     err = GetCapturedStderr();
 
-    EXPECT_THAT(out, StrEq("*** command '" + simple_command +
+    EXPECT_THAT(out, StrEq("*** command '" + kSimpleCommand +
                            " --pid --sleep 20' failed: killed by signal 15\n"));
-    EXPECT_THAT(err, StrEq("*** command '" + simple_command +
+    EXPECT_THAT(err, StrEq("*** command '" + kSimpleCommand +
                            " --pid --sleep 20' failed: killed by signal 15\n"));
 }
 
-TEST_F(DumpstateTest, RunCommandProgressNoListener) {
-    ds.update_progress_ = true;
-    ds.progress_ = 0;
-    ds.weight_total_ = 30;
-
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
-    std::string progress_message = GetProgressMessageAndAssertSystemProperties(20, 30);
-    EXPECT_THAT(out, StrEq("stdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
-
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
-    progress_message = GetProgressMessageAndAssertSystemProperties(30, 30);
-    EXPECT_THAT(out, StrEq("stdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
-
-    // Run a command that will increase maximum timeout.
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
-    progress_message = GetProgressMessageAndAssertSystemProperties(31, 36, 30);  // 20% increase
-    EXPECT_THAT(out, StrEq("stdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
-
-    // Make sure command ran while in dry_run is counted.
-    SetDryRun(true);
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
-    progress_message = GetProgressMessageAndAssertSystemProperties(35, 36);
-    EXPECT_THAT(out, IsEmpty());
-    EXPECT_THAT(err, StrEq(progress_message));
-}
-
 TEST_F(DumpstateTest, RunCommandProgress) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     ds.listener_ = listener;
     ds.listener_name_ = "FoxMulder";
-
-    ds.update_progress_ = true;
-    ds.progress_ = 0;
-    ds.weight_total_ = 30;
+    SetProgress(0, 30);
 
     EXPECT_CALL(*listener, onProgressUpdated(20));
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
     std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     EXPECT_CALL(*listener, onProgressUpdated(30));
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build()));
     progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     // Run a command that will increase maximum timeout.
     EXPECT_CALL(*listener, onProgressUpdated(31));
-    EXPECT_CALL(*listener, onMaxProgressUpdated(36));
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
-    progress_message = GetProgressMessage(ds.listener_name_, 31, 36, 30);  // 20% increase
+    EXPECT_CALL(*listener, onMaxProgressUpdated(37));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30);  // 20% increase
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     // Make sure command ran while in dry_run is counted.
     SetDryRun(true);
     EXPECT_CALL(*listener, onProgressUpdated(35));
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
-    progress_message = GetProgressMessage(ds.listener_name_, 35, 36);
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 35, 37);
     EXPECT_THAT(out, IsEmpty());
     EXPECT_THAT(err, StrEq(progress_message));
 
     ds.listener_.clear();
 }
 
+TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) {
+    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+    ds.listener_ = listener;
+    ds.listener_name_ = "FoxMulder";
+    SetProgress(0, 8, 5);  // 8 max, 5 threshold
+
+    // First update should always be sent.
+    EXPECT_CALL(*listener, onProgressUpdated(1));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5).
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+
+    // Third update should be sent because it reaches threshold (6 - 1 = 5).
+    EXPECT_CALL(*listener, onProgressUpdated(6));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 6, 8);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5).
+    // But max update should be sent.
+    EXPECT_CALL(*listener, onMaxProgressUpdated(10));  // 9 * 120% = 10.8 = 10
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build()));
+    progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+    ds.listener_.clear();
+}
+
 TEST_F(DumpstateTest, RunCommandDropRoot) {
     // First check root case - only available when running with 'adb root'.
     uid_t uid = getuid();
     if (uid == 0) {
-        EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"}));
+        EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}));
         EXPECT_THAT(out, StrEq("0\nstdout\n"));
         EXPECT_THAT(err, StrEq("stderr\n"));
         return;
     }
-    // Then drop root.
-
-    EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"},
+    // Then run dropping root.
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
                             CommandOptions::WithTimeout(1).DropRoot().Build()));
     EXPECT_THAT(out, StrEq("2000\nstdout\n"));
     EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
@@ -406,29 +424,14 @@
 
     DropRoot();
 
-    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
 
     // We don't know the exact path of su, so we just check for the 'root ...' commands
     EXPECT_THAT(out, StartsWith("Skipping"));
-    EXPECT_THAT(out, EndsWith("root " + simple_command + "' on user build.\n"));
+    EXPECT_THAT(out, EndsWith("root " + kSimpleCommand + "' on user build.\n"));
     EXPECT_THAT(err, IsEmpty());
 }
 
-TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
-    if (IsUserBuild()) {
-        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
-        return;
-    }
-
-    DropRoot();
-
-    EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"},
-                            CommandOptions::WithTimeout(1).AsRoot().Build()));
-
-    EXPECT_THAT(out, StrEq("0\nstdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n"));
-}
-
 TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
     EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
     EXPECT_THAT(out,
@@ -446,70 +449,54 @@
 }
 
 TEST_F(DumpstateTest, DumpFileSingleLine) {
-    EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
 }
 
 TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
-    EXPECT_EQ(0, DumpFile("", test_data_path + "single-line-with-newline.txt"));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line-with-newline.txt"));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, StrEq("I AM LINE1\n"));
 }
 
 TEST_F(DumpstateTest, DumpFileMultipleLines) {
-    EXPECT_EQ(0, DumpFile("", test_data_path + "multiple-lines.txt"));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines.txt"));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
 }
 
 TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
-    EXPECT_EQ(0, DumpFile("", test_data_path + "multiple-lines-with-newline.txt"));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "multiple-lines-with-newline.txt"));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
 }
 
 TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
     SetDryRun(true);
-    EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
     EXPECT_THAT(err, IsEmpty());
     EXPECT_THAT(out, IsEmpty());
 }
 
 TEST_F(DumpstateTest, DumpFileOnDryRun) {
     SetDryRun(true);
-    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", test_data_path + "single-line.txt"));
+    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", kTestDataPath + "single-line.txt"));
     EXPECT_THAT(err, IsEmpty());
-    EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + test_data_path +
+    EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath +
                                 "single-line.txt) ------\n\t(skipped on dry run)\n------"));
     EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
     EXPECT_THAT(err, IsEmpty());
 }
 
-TEST_F(DumpstateTest, DumpFileUpdateProgressNoListener) {
-    ds.update_progress_ = true;
-    ds.progress_ = 0;
-    ds.weight_total_ = 30;
-
-    EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
-
-    std::string progress_message =
-        GetProgressMessageAndAssertSystemProperties(5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
-
-    EXPECT_THAT(err, StrEq(progress_message));
-    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
-}
-
 TEST_F(DumpstateTest, DumpFileUpdateProgress) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     ds.listener_ = listener;
     ds.listener_name_ = "FoxMulder";
-    ds.update_progress_ = true;
-    ds.progress_ = 0;
-    ds.weight_total_ = 30;
+    SetProgress(0, 30);
 
     EXPECT_CALL(*listener, onProgressUpdated(5));
-    EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
+    EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
 
     std::string progress_message =
         GetProgressMessage(ds.listener_name_, 5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
@@ -519,32 +506,259 @@
     ds.listener_.clear();
 }
 
-class DumpstateServiceTest : public Test {
+class DumpstateServiceTest : public DumpstateBaseTest {
   public:
     DumpstateService dss;
 };
 
 TEST_F(DumpstateServiceTest, SetListenerNoName) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
-    bool result;
-    EXPECT_TRUE(dss.setListener("", listener, &result).isOk());
-    EXPECT_FALSE(result);
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+    ASSERT_THAT(token, IsNull());
 }
 
 TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
-    bool result;
-    EXPECT_TRUE(dss.setListener("whatever", nullptr, &result).isOk());
-    EXPECT_FALSE(result);
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+    ASSERT_THAT(token, IsNull());
 }
 
 TEST_F(DumpstateServiceTest, SetListenerTwice) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
-    bool result;
-    EXPECT_TRUE(dss.setListener("whatever", listener, &result).isOk());
-    EXPECT_TRUE(result);
-
+    sp<IDumpstateToken> token;
+    EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+    ASSERT_THAT(token, NotNull());
     EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
 
-    EXPECT_TRUE(dss.setListener("whatever", listener, &result).isOk());
-    EXPECT_FALSE(result);
+    token.clear();
+    EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+    ASSERT_THAT(token, IsNull());
+    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+}
+
+class ProgressTest : public DumpstateBaseTest {
+  public:
+    Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
+        return Progress(max, growth_factor, path);
+    }
+
+    void AssertStats(const std::string& path, int32_t expected_runs, int32_t expected_average) {
+        std::string expected_content =
+            android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
+        std::string actual_content;
+        ASSERT_TRUE(android::base::ReadFileToString(path, &actual_content))
+            << "could not read statsfrom" << path;
+        ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
+    }
+};
+
+TEST_F(ProgressTest, SimpleTest) {
+    Progress progress;
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+
+    bool max_increased = progress.Inc(1);
+    EXPECT_EQ(1, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Ignore negative increase.
+    max_increased = progress.Inc(-1);
+    EXPECT_EQ(1, progress.Get());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsInsideNewRange) {
+    Progress progress = GetInstance(10, 1.2);  // 20% growth factor
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(10, progress.GetInitialMax());
+    EXPECT_EQ(10, progress.GetMax());
+
+    // No increase
+    bool max_increased = progress.Inc(10);
+    EXPECT_EQ(10, progress.Get());
+    EXPECT_EQ(10, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Increase, with new value < max*20%
+    max_increased = progress.Inc(1);
+    EXPECT_EQ(11, progress.Get());
+    EXPECT_EQ(13, progress.GetMax());  // 11 average * 20% growth = 13.2 = 13
+    EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, MaxGrowsOutsideNewRange) {
+    Progress progress = GetInstance(10, 1.2);  // 20% growth factor
+    EXPECT_EQ(0, progress.Get());
+    EXPECT_EQ(10, progress.GetInitialMax());
+    EXPECT_EQ(10, progress.GetMax());
+
+    // No increase
+    bool max_increased = progress.Inc(10);
+    EXPECT_EQ(10, progress.Get());
+    EXPECT_EQ(10, progress.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Increase, with new value > max*20%
+    max_increased = progress.Inc(5);
+    EXPECT_EQ(15, progress.Get());
+    EXPECT_EQ(18, progress.GetMax());  // 15 average * 20% growth = 18
+    EXPECT_TRUE(max_increased);
+}
+
+TEST_F(ProgressTest, InvalidPath) {
+    Progress progress("/devil/null");
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, EmptyFile) {
+    Progress progress(CopyTextFileFixture("empty-file.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLineBothNAN) {
+    Progress progress(CopyTextFileFixture("stats-invalid-both-NAN.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryNegative) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-negative.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryNegative) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-negative.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine1stEntryTooBig) {
+    Progress progress(CopyTextFileFixture("stats-invalid-1st-too-big.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+TEST_F(ProgressTest, InvalidLine2ndEntryTooBig) {
+    Progress progress(CopyTextFileFixture("stats-invalid-2nd-too-big.txt"));
+    EXPECT_EQ(Progress::kDefaultMax, progress.GetMax());
+}
+
+// Tests stats are properly saved when the file does not exists.
+TEST_F(ProgressTest, FirstTime) {
+    std::string path = kTestDataPath + "FirstTime.txt";
+    android::base::RemoveFileIfExists(path);
+
+    Progress run1(path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetInitialMax());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(Progress::kDefaultMax, run1.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    run1.Save();
+    AssertStats(path, 1, 20);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 1 run.
+// Data on file is 1 run and 109 average.
+TEST_F(ProgressTest, SecondTime) {
+    std::string path = CopyTextFileFixture("stats-one-run-no-newline.txt");
+
+    Progress run1 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(10, run1.GetInitialMax());
+    EXPECT_EQ(10, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(24, run1.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 2 runs and (10 + 20)/ 2 = 15
+    run1.Save();
+    AssertStats(path, 2, 15);
+
+    Progress run2 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run2.Get());
+    EXPECT_EQ(15, run2.GetInitialMax());
+    EXPECT_EQ(15, run2.GetMax());
+
+    max_increased = run2.Inc(25);
+    EXPECT_EQ(25, run2.Get());
+    EXPECT_EQ(30, run2.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 3 runs and (15 * 2 + 25)/ 3 = 18.33 = 18
+    run2.Save();
+    AssertStats(path, 3, 18);
+
+    Progress run3 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run3.Get());
+    EXPECT_EQ(18, run3.GetInitialMax());
+    EXPECT_EQ(18, run3.GetMax());
+
+    // Make sure average decreases as well
+    max_increased = run3.Inc(5);
+    EXPECT_EQ(5, run3.Get());
+    EXPECT_EQ(18, run3.GetMax());
+    EXPECT_FALSE(max_increased);
+
+    // Average now is 4 runs and (18 * 3 + 5)/ 4 = 14.75 = 14
+    run3.Save();
+    AssertStats(path, 4, 14);
+}
+
+// Tests what happens when the persistent settings contains the average duration of 2 runs.
+// Data on file is 2 runs and 15 average.
+TEST_F(ProgressTest, ThirdTime) {
+    std::string path = CopyTextFileFixture("stats-two-runs.txt");
+    AssertStats(path, 2, 15);  // Sanity check
+
+    Progress run1 = GetInstance(-42, 1.2, path);
+    EXPECT_EQ(0, run1.Get());
+    EXPECT_EQ(15, run1.GetInitialMax());
+    EXPECT_EQ(15, run1.GetMax());
+
+    bool max_increased = run1.Inc(20);
+    EXPECT_EQ(20, run1.Get());
+    EXPECT_EQ(24, run1.GetMax());
+    EXPECT_TRUE(max_increased);
+
+    // Average now is 3 runs and (15 * 2 + 20)/ 3 = 16.66 = 16
+    run1.Save();
+    AssertStats(path, 3, 16);
+}
+
+// TODO: RunCommandAsRootNonUserBuild must be the last test because it drops root, which could cause
+// other tests to fail if they're relyin on the process running as root.
+// For now this is fine, but eventually it might need to be moved to its own test case / process.
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+    if (IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
 }
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 34e09d7..b5f328d 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <libgen.h>
 #include <limits.h>
+#include <math.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdarg.h>
@@ -42,6 +43,7 @@
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/debugger.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
@@ -67,7 +69,7 @@
 static bool IsDryRun() {
     return Dumpstate::GetInstance().IsDryRun();
 }
-static void UpdateProgress(int delta) {
+static void UpdateProgress(int32_t delta) {
     ds.UpdateProgress(delta);
 }
 
@@ -90,6 +92,10 @@
 /* Most simple commands have 10 as timeout, so 5 is a good estimate */
 static const int WEIGHT_FILE = 5;
 
+// Reasonable value for max stats.
+static const int STATS_MAX_N_RUNS = 1000;
+static const long STATS_MAX_AVERAGE = 100000;
+
 CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
 CommandOptions CommandOptions::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
 CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
@@ -207,6 +213,115 @@
     return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
 }
 
+const int32_t Progress::kDefaultMax = 5000;
+
+Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) {
+}
+
+Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor)
+    : Progress(initial_max, growth_factor, "") {
+    progress_ = progress;
+}
+
+Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path)
+    : initial_max_(initial_max),
+      progress_(0),
+      max_(initial_max),
+      growth_factor_(growth_factor),
+      n_runs_(0),
+      average_max_(0),
+      path_(path) {
+    if (!path_.empty()) {
+        Load();
+    }
+}
+
+void Progress::Load() {
+    MYLOGD("Loading stats from %s\n", path_.c_str());
+    std::string content;
+    if (!android::base::ReadFileToString(path_, &content)) {
+        MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_);
+        return;
+    }
+    if (content.empty()) {
+        MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_);
+        return;
+    }
+    std::vector<std::string> lines = android::base::Split(content, "\n");
+
+    if (lines.size() < 1) {
+        MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(),
+               (int)lines.size(), max_);
+        return;
+    }
+    char* ptr;
+    n_runs_ = strtol(lines[0].c_str(), &ptr, 10);
+    average_max_ = strtol(ptr, nullptr, 10);
+    if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS ||
+        average_max_ > STATS_MAX_AVERAGE) {
+        MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str());
+        initial_max_ = Progress::kDefaultMax;
+    } else {
+        initial_max_ = average_max_;
+    }
+    max_ = initial_max_;
+
+    MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_);
+}
+
+void Progress::Save() {
+    int32_t total = n_runs_ * average_max_ + progress_;
+    int32_t runs = n_runs_ + 1;
+    int32_t average = floor(((float)total) / runs);
+    MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average,
+           path_.c_str());
+    if (path_.empty()) {
+        return;
+    }
+
+    std::string content = android::base::StringPrintf("%d %d\n", runs, average);
+    if (!android::base::WriteStringToFile(content, path_)) {
+        MYLOGE("Could not save stats on %s\n", path_.c_str());
+    }
+}
+
+int32_t Progress::Get() const {
+    return progress_;
+}
+
+bool Progress::Inc(int32_t delta) {
+    bool changed = false;
+    if (delta >= 0) {
+        progress_ += delta;
+        if (progress_ > max_) {
+            int32_t old_max = max_;
+            max_ = floor((float)progress_ * growth_factor_);
+            MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_);
+            changed = true;
+        }
+    }
+    return changed;
+}
+
+int32_t Progress::GetMax() const {
+    return max_;
+}
+
+int32_t Progress::GetInitialMax() const {
+    return initial_max_;
+}
+
+void Progress::Dump(int fd, const std::string& prefix) const {
+    const char* pr = prefix.c_str();
+    dprintf(fd, "%sprogress: %d\n", pr, progress_);
+    dprintf(fd, "%smax: %d\n", pr, max_);
+    dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_);
+    dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_);
+    dprintf(fd, "%spath: %s\n", pr, path_.c_str());
+    dprintf(fd, "%sn_runs: %d\n", pr, n_runs_);
+    dprintf(fd, "%saverage_max: %d\n", pr, average_max_);
+}
+
 bool Dumpstate::IsDryRun() const {
     return dry_run_;
 }
@@ -224,6 +339,10 @@
                                        name_.c_str(), suffix.c_str());
 }
 
+void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) {
+    progress_ = std::move(progress);
+}
+
 void for_each_userid(void (*func)(int), const char *header) {
     if (IsDryRun()) return;
 
@@ -1320,69 +1439,47 @@
 }
 
 // TODO: make this function thread safe if sections are generated in parallel.
-void Dumpstate::UpdateProgress(int delta) {
-    if (!update_progress_) return;
-
-    progress_ += delta;
-
-    // TODO: remove property support once Shell uses IDumpstateListener
-    char key[PROPERTY_KEY_MAX];
-    char value[PROPERTY_VALUE_MAX];
-
-    // adjusts max on the fly
-    if (progress_ > weight_total_) {
-        int new_total = weight_total_ * 1.2;
-        MYLOGD("Adjusting total weight from %d to %d\n", weight_total_, new_total);
-        weight_total_ = new_total;
-
-        if (listener_ != nullptr) {
-            listener_->onMaxProgressUpdated(weight_total_);
-        } else {
-            snprintf(key, sizeof(key), "dumpstate.%d.max", pid_);
-            snprintf(value, sizeof(value), "%d", weight_total_);
-            int status = property_set(key, value);
-            if (status != 0) {
-                MYLOGE("Could not update max weight by setting system property %s to %s: %d\n", key,
-                       value, status);
-            }
-        }
+void Dumpstate::UpdateProgress(int32_t delta) {
+    if (progress_ == nullptr) {
+        MYLOGE("UpdateProgress: progress_ not set\n");
+        return;
     }
 
+    // Always update progess so stats can be tuned...
+    bool max_changed = progress_->Inc(delta);
+
+    // ...but only notifiy listeners when necessary.
+    if (!update_progress_) return;
+
+    int progress = progress_->Get();
+    int max = progress_->GetMax();
+
+    // adjusts max on the fly
+    if (max_changed && listener_ != nullptr) {
+        listener_->onMaxProgressUpdated(max);
+    }
+
+    int32_t last_update_delta = progress - last_updated_progress_;
+    if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) {
+        return;
+    }
+    last_updated_progress_ = progress;
+
     if (control_socket_fd_ >= 0) {
-        dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress_, weight_total_);
+        dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max);
         fsync(control_socket_fd_);
     }
 
     if (listener_ != nullptr) {
-        if (progress_ % 100 == 0) {
+        if (progress % 100 == 0) {
             // We don't want to spam logcat, so only log multiples of 100.
-            MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress_,
-                   weight_total_);
+            MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
         } else {
             // stderr is ignored on normal invocations, but useful when calling
             // /system/bin/dumpstate directly for debuggging.
-            fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress_,
-                    weight_total_);
+            fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max);
         }
-        listener_->onProgressUpdated(progress_);
-    } else {
-        snprintf(key, sizeof(key), "dumpstate.%d.progress", pid_);
-        snprintf(value, sizeof(value), "%d", progress_);
-
-        if (progress_ % 100 == 0) {
-            // We don't want to spam logcat, so only log multiples of 100.
-            MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total_);
-        } else {
-            // stderr is ignored on normal invocations, but useful when calling
-            // /system/bin/dumpstate directly for debuggging.
-            fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total_);
-        }
-
-        int status = property_set(key, value);
-        if (status) {
-            MYLOGE("Could not update progress by setting system property %s to %s: %d\n", key,
-                   value, status);
-        }
+        listener_->onProgressUpdated(progress);
     }
 }
 
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 078db26..5ffc0c2 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -797,7 +797,7 @@
     ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
 }
 
-static void run_dex2oat(int zip_fd, int oat_fd, int vdex_fd, int image_fd,
+static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,
         const char* input_file_name, const char* output_file_name, int swap_fd,
         const char *instruction_set, const char* compiler_filter, bool vm_safe_mode,
         bool debuggable, bool post_bootcomplete, int profile_fd, const char* shared_libraries) {
@@ -881,7 +881,8 @@
 
     char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
     char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
-    char vdex_fd_arg[strlen("--vdex-fd=") + MAX_INT_LEN];
+    char input_vdex_fd_arg[strlen("--input-vdex-fd=") + MAX_INT_LEN];
+    char output_vdex_fd_arg[strlen("--output-vdex-fd=") + MAX_INT_LEN];
     char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
     char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX];
     char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
@@ -897,7 +898,8 @@
 
     sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
     sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
-    sprintf(vdex_fd_arg, "--vdex-fd=%d", vdex_fd);
+    sprintf(input_vdex_fd_arg, "--input-vdex-fd=%d", input_vdex_fd);
+    sprintf(output_vdex_fd_arg, "--output-vdex-fd=%d", output_vdex_fd);
     sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
     sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
     sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
@@ -960,7 +962,7 @@
 
     ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
 
-    const char* argv[8  // program name, mandatory arguments and the final NULL
+    const char* argv[9  // program name, mandatory arguments and the final NULL
                      + (have_dex2oat_isa_variant ? 1 : 0)
                      + (have_dex2oat_isa_features ? 1 : 0)
                      + (have_dex2oat_Xms_flag ? 2 : 0)
@@ -981,7 +983,8 @@
     argv[i++] = DEX2OAT_BIN;
     argv[i++] = zip_fd_arg;
     argv[i++] = zip_location_arg;
-    argv[i++] = vdex_fd_arg;
+    argv[i++] = input_vdex_fd_arg;
+    argv[i++] = output_vdex_fd_arg;
     argv[i++] = oat_fd_arg;
     argv[i++] = oat_location_arg;
     argv[i++] = instruction_set_arg;
@@ -1507,6 +1510,14 @@
     return true;
 }
 
+static bool IsOutputDalvikCache(const char* oat_dir) {
+  // InstallerConnection.java (which invokes installd) transforms Java null arguments
+  // into '!'. Play it safe by handling it both.
+  // TODO: ensure we never get null.
+  // TODO: pass a flag instead of inferring if the output is dalvik cache.
+  return oat_dir == nullptr || oat_dir[0] == '!';
+}
+
 static bool create_oat_out_path(const char* apk_path, const char* instruction_set,
             const char* oat_dir, /*out*/ char* out_oat_path) {
     // Early best-effort check whether we can fit the the path into our buffers.
@@ -1518,9 +1529,9 @@
         return false;
     }
 
-    if (oat_dir != NULL && oat_dir[0] != '!') {
+    if (!IsOutputDalvikCache(oat_dir)) {
         if (validate_apk_path(oat_dir)) {
-            ALOGE("invalid oat_dir '%s'\n", oat_dir);
+            ALOGE("cannot validate apk path with oat_dir '%s'\n", oat_dir);
             return false;
         }
         if (!calculate_oat_file_path(out_oat_path, oat_dir, apk_path, instruction_set)) {
@@ -1709,22 +1720,6 @@
         return -1;
     }
 
-    // If invoking patchoat, open the VDEX associated with the OAT too.
-    std::string in_vdex_path_str;
-    base::unique_fd input_vdex_fd;
-    if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
-        || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
-        in_vdex_path_str = create_vdex_filename(input_file);
-        if (in_vdex_path_str.empty()) {
-            return -1;
-        }
-        input_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
-        if (input_vdex_fd.get() < 0) {
-            ALOGE("installd cannot open '%s' for input during dexopt\n", in_vdex_path_str.c_str());
-            return -1;
-        }
-    }
-
     // Create the output OAT file.
     const std::string out_oat_path_str(out_oat_path);
     Dex2oatFileWrapper<std::function<void ()>> out_oat_fd(
@@ -1738,6 +1733,47 @@
         return -1;
     }
 
+    // Open the existing VDEX. We do this before creating the new output VDEX, which will
+    // unlink the old one.
+    base::unique_fd in_vdex_fd;
+    std::string in_vdex_path_str;
+    if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
+        || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
+        // `input_file` is the OAT file to be relocated. The VDEX has to be there as well.
+        in_vdex_path_str = create_vdex_filename(input_file);
+        if (in_vdex_path_str.empty()) {
+            ALOGE("installd cannot compute input vdex location for '%s'\n", input_file);
+            return -1;
+        }
+        in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+        if (in_vdex_fd.get() < 0) {
+            ALOGE("installd cannot open '%s' for input during dexopt: %s\n",
+                in_vdex_path_str.c_str(), strerror(errno));
+            return -1;
+        }
+    } else {
+        // Open the possibly existing vdex in the `out_oat_path`. If none exist, we pass -1
+        // to dex2oat for input-vdex-fd.
+        in_vdex_path_str = create_vdex_filename(out_oat_path);
+        if (in_vdex_path_str.empty()) {
+            ALOGE("installd cannot compute input vdex location for '%s'\n", out_oat_path);
+            return -1;
+        }
+        in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+        // If there is no vdex file in out_oat_path, check if we have a vdex
+        // file next to the odex file. For other failures, we will just pass a -1 fd.
+        if (in_vdex_fd.get() < 0 && (errno == ENOENT) && IsOutputDalvikCache(oat_dir)) {
+            if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+              in_vdex_path_str = create_vdex_filename(std::string(in_odex_path));
+              if (in_vdex_path_str.empty()) {
+                  ALOGE("installd cannot compute input vdex location for '%s'\n", in_odex_path);
+                  return -1;
+              }
+              in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+            }
+        }
+    }
+
     // Infer the name of the output VDEX and create it.
     const std::string out_vdex_path_str = create_vdex_filename(out_oat_path_str);
     if (out_vdex_path_str.empty()) {
@@ -1832,7 +1868,7 @@
         if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
             || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
             run_patchoat(input_fd.get(),
-                         input_vdex_fd.get(),
+                         in_vdex_fd.get(),
                          out_oat_fd.get(),
                          out_vdex_fd.get(),
                          input_file,
@@ -1846,6 +1882,7 @@
             const char *input_file_name = get_location_from_path(input_file);
             run_dex2oat(input_fd.get(),
                         out_oat_fd.get(),
+                        in_vdex_fd.get(),
                         out_vdex_fd.get(),
                         image_fd.get(),
                         input_file_name,
diff --git a/data/etc/android.hardware.wifi.nan.xml b/data/etc/android.hardware.wifi.aware.xml
similarity index 90%
rename from data/etc/android.hardware.wifi.nan.xml
rename to data/etc/android.hardware.wifi.aware.xml
index e557610..ae6272e 100644
--- a/data/etc/android.hardware.wifi.nan.xml
+++ b/data/etc/android.hardware.wifi.aware.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<!-- This is the standard feature indicating that the device includes WiFi NAN. -->
+<!-- This is the standard feature indicating that the device includes WiFi Aware. -->
 <permissions>
-    <feature name="android.hardware.wifi.nan" />
+    <feature name="android.hardware.wifi.aware" />
 </permissions>
diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h
index b399905..30d7317 100644
--- a/include/batteryservice/BatteryService.h
+++ b/include/batteryservice/BatteryService.h
@@ -24,25 +24,7 @@
 
 namespace android {
 
-// must be kept in sync with definitions in BatteryManager.java
-enum {
-    BATTERY_STATUS_UNKNOWN = 1, // equals BatteryManager.BATTERY_STATUS_UNKNOWN constant
-    BATTERY_STATUS_CHARGING = 2, // equals BatteryManager.BATTERY_STATUS_CHARGING constant
-    BATTERY_STATUS_DISCHARGING = 3, // equals BatteryManager.BATTERY_STATUS_DISCHARGING constant
-    BATTERY_STATUS_NOT_CHARGING = 4, // equals BatteryManager.BATTERY_STATUS_NOT_CHARGING constant
-    BATTERY_STATUS_FULL = 5, // equals BatteryManager.BATTERY_STATUS_FULL constant
-};
-
-// must be kept in sync with definitions in BatteryManager.java
-enum {
-    BATTERY_HEALTH_UNKNOWN = 1, // equals BatteryManager.BATTERY_HEALTH_UNKNOWN constant
-    BATTERY_HEALTH_GOOD = 2, // equals BatteryManager.BATTERY_HEALTH_GOOD constant
-    BATTERY_HEALTH_OVERHEAT = 3, // equals BatteryManager.BATTERY_HEALTH_OVERHEAT constant
-    BATTERY_HEALTH_DEAD = 4, // equals BatteryManager.BATTERY_HEALTH_DEAD constant
-    BATTERY_HEALTH_OVER_VOLTAGE = 5, // equals BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE constant
-    BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6, // equals BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE constant
-    BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant
-};
+#include "BatteryServiceConstants.h"
 
 // must be kept in sync with definitions in BatteryProperty.java
 enum {
diff --git a/include/batteryservice/BatteryServiceConstants.h b/include/batteryservice/BatteryServiceConstants.h
new file mode 100644
index 0000000..8a90a12
--- /dev/null
+++ b/include/batteryservice/BatteryServiceConstants.h
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+    BATTERY_STATUS_UNKNOWN = 1,
+    BATTERY_STATUS_CHARGING = 2,
+    BATTERY_STATUS_DISCHARGING = 3,
+    BATTERY_STATUS_NOT_CHARGING = 4,
+    BATTERY_STATUS_FULL = 5,
+};
+
+enum {
+    BATTERY_HEALTH_UNKNOWN = 1,
+    BATTERY_HEALTH_GOOD = 2,
+    BATTERY_HEALTH_OVERHEAT = 3,
+    BATTERY_HEALTH_DEAD = 4,
+    BATTERY_HEALTH_OVER_VOLTAGE = 5,
+    BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
+    BATTERY_HEALTH_COLD = 7,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 39deb64..69de136 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -697,8 +697,16 @@
         return UNEXPECTED_NULL;
     }
 
+    if (val->max_size() < static_cast<size_t>(size)) {
+        return NO_MEMORY;
+    }
+
     val->resize(static_cast<size_t>(size));
 
+    if (val->size() < static_cast<size_t>(size)) {
+        return NO_MEMORY;
+    }
+
     for (auto& v: *val) {
         status = (this->*read_func)(&v);
 
diff --git a/include/gui/GraphicBufferAlloc.h b/include/gui/GraphicBufferAlloc.h
index 62e3877..b19a1ac 100644
--- a/include/gui/GraphicBufferAlloc.h
+++ b/include/gui/GraphicBufferAlloc.h
@@ -34,8 +34,9 @@
     GraphicBufferAlloc();
     virtual ~GraphicBufferAlloc();
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
-            std::string requestorName, status_t* error) override;
+            uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint32_t usage, std::string requestorName,
+            status_t* error) override;
 };
 
 
diff --git a/include/gui/IGraphicBufferAlloc.h b/include/gui/IGraphicBufferAlloc.h
index acc2f30..2a7690a 100644
--- a/include/gui/IGraphicBufferAlloc.h
+++ b/include/gui/IGraphicBufferAlloc.h
@@ -38,12 +38,14 @@
     /* Create a new GraphicBuffer for the client to use.
      */
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
-            PixelFormat format, uint32_t usage, std::string requestorName,
-            status_t* error) = 0;
+            PixelFormat format, uint32_t layerCount, uint32_t usage,
+            std::string requestorName, status_t* error) = 0;
 
     sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
-            PixelFormat format, uint32_t usage, status_t* error) {
-        return createGraphicBuffer(w, h, format, usage, "<Unknown>", error);
+            PixelFormat format, uint32_t layerCount, uint32_t usage,
+            status_t* error) {
+        return createGraphicBuffer(w, h, format, layerCount, usage, "<Unknown>",
+                error);
     }
 };
 
diff --git a/include/ui/Gralloc1.h b/include/ui/Gralloc1.h
index cf8c173..64dacd7 100644
--- a/include/ui/Gralloc1.h
+++ b/include/ui/Gralloc1.h
@@ -49,6 +49,7 @@
         mWidth(0),
         mHeight(0),
         mFormat(static_cast<android_pixel_format_t>(0)),
+        mLayerCount(0),
         mProducerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
         mConsumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
 
@@ -58,6 +59,7 @@
 
     gralloc1_error_t setDimensions(uint32_t width, uint32_t height);
     gralloc1_error_t setFormat(android_pixel_format_t format);
+    gralloc1_error_t setLayerCount(uint32_t layerCount);
     gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage);
     gralloc1_error_t setConsumerUsage(gralloc1_consumer_usage_t usage);
 
@@ -68,6 +70,7 @@
     uint32_t mWidth;
     uint32_t mHeight;
     android_pixel_format_t mFormat;
+    uint32_t mLayerCount;
     gralloc1_producer_usage_t mProducerUsage;
     gralloc1_consumer_usage_t mConsumerUsage;
 
@@ -178,6 +181,8 @@
                 GRALLOC1_FUNCTION_SET_DIMENSIONS> setDimensions;
         FunctionLoader<GRALLOC1_PFN_SET_FORMAT,
                 GRALLOC1_FUNCTION_SET_FORMAT> setFormat;
+        FunctionLoader<GRALLOC1_PFN_SET_LAYER_COUNT,
+                GRALLOC1_FUNCTION_SET_LAYER_COUNT> setLayerCount;
         FunctionLoader<GRALLOC1_PFN_SET_PRODUCER_USAGE,
                 GRALLOC1_FUNCTION_SET_PRODUCER_USAGE> setProducerUsage;
         FunctionLoader<GRALLOC1_PFN_GET_BACKING_STORE,
@@ -188,6 +193,8 @@
                 GRALLOC1_FUNCTION_GET_DIMENSIONS> getDimensions;
         FunctionLoader<GRALLOC1_PFN_GET_FORMAT,
                 GRALLOC1_FUNCTION_GET_FORMAT> getFormat;
+        FunctionLoader<GRALLOC1_PFN_GET_LAYER_COUNT,
+                GRALLOC1_FUNCTION_GET_LAYER_COUNT> getLayerCount;
         FunctionLoader<GRALLOC1_PFN_GET_PRODUCER_USAGE,
                 GRALLOC1_FUNCTION_GET_PRODUCER_USAGE> getProducerUsage;
         FunctionLoader<GRALLOC1_PFN_GET_STRIDE,
diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h
index d523c4f..2508ce9 100644
--- a/include/ui/Gralloc1On0Adapter.h
+++ b/include/ui/Gralloc1On0Adapter.h
@@ -131,6 +131,7 @@
             width(0),
             height(0),
             format(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
+            layerCount(1),
             producerUsage(GRALLOC1_PRODUCER_USAGE_NONE),
             consumerUsage(GRALLOC1_CONSUMER_USAGE_NONE) {}
 
@@ -145,6 +146,11 @@
             return GRALLOC1_ERROR_NONE;
         }
 
+        gralloc1_error_t setLayerCount(uint32_t lc) {
+            layerCount = lc;
+            return GRALLOC1_ERROR_NONE;
+        }
+
         gralloc1_error_t setProducerUsage(gralloc1_producer_usage_t usage) {
             producerUsage = usage;
             return GRALLOC1_ERROR_NONE;
@@ -161,6 +167,7 @@
         uint32_t width;
         uint32_t height;
         int32_t format;
+        uint32_t layerCount;
         gralloc1_producer_usage_t producerUsage;
         gralloc1_consumer_usage_t consumerUsage;
     };
@@ -197,6 +204,12 @@
                 &Descriptor::setFormat, format);
     }
 
+    static int32_t setLayerCountHook(gralloc1_device_t* device,
+            gralloc1_buffer_descriptor_t descriptorId, uint32_t layerCount) {
+        return callDescriptorFunction(device, descriptorId,
+                &Descriptor::setLayerCount, layerCount);
+    }
+
     static int32_t setProducerUsageHook(gralloc1_device_t* device,
             gralloc1_buffer_descriptor_t descriptorId, uint64_t intUsage) {
         auto usage = static_cast<gralloc1_producer_usage_t>(intUsage);
@@ -246,6 +259,11 @@
             return GRALLOC1_ERROR_NONE;
         }
 
+        gralloc1_error_t getLayerCount(uint32_t* outLayerCount) const {
+            *outLayerCount = mDescriptor.layerCount;
+            return GRALLOC1_ERROR_NONE;
+        }
+
         gralloc1_error_t getNumFlexPlanes(uint32_t* outNumPlanes) const {
             // TODO: This is conservative, and we could do better by examining
             // the format, but it won't hurt anything for now
diff --git a/include/ui/GrallocMapper.h b/include/ui/GrallocMapper.h
index 5517449..868fd14 100644
--- a/include/ui/GrallocMapper.h
+++ b/include/ui/GrallocMapper.h
@@ -63,6 +63,11 @@
         return mMapper->getFormat(mDevice, handle, &format);
     }
 
+    Error getLayerCount(buffer_handle_t handle, uint32_t& layerCount) const
+    {
+        return mMapper->getLayerCount(mDevice, handle, &layerCount);
+    }
+
     Error getProducerUsageMask(buffer_handle_t handle,
             uint64_t& usageMask) const
     {
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 3e127a1..1bbcee2 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -76,10 +76,15 @@
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inUsage, std::string requestorName = "<Unknown>");
 
+    // creates w * h buffer with a layer count
+    GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+            uint32_t inLayerCount, uint32_t inUsage,
+            std::string requestorName = "<Unknown>");
+
     // create a buffer from an existing handle
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
-            uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle,
-            bool keepOwnership);
+            uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
+            native_handle_t* inHandle, bool keepOwnership);
 
     // create a buffer from an existing ANativeWindowBuffer
     GraphicBuffer(ANativeWindowBuffer* buffer, bool keepOwnership);
@@ -92,6 +97,7 @@
     uint32_t getStride() const          { return static_cast<uint32_t>(stride); }
     uint32_t getUsage() const           { return static_cast<uint32_t>(usage); }
     PixelFormat getPixelFormat() const  { return format; }
+    uint32_t getLayerCount() const      { return static_cast<uint32_t>(layerCount); }
     Rect getBounds() const              { return Rect(width, height); }
     uint64_t getId() const              { return mId; }
 
@@ -101,10 +107,10 @@
     }
 
     status_t reallocate(uint32_t inWidth, uint32_t inHeight,
-            PixelFormat inFormat, uint32_t inUsage);
+            PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage);
 
     bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
-            PixelFormat inFormat, uint32_t inUsage);
+            PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage);
 
     status_t lock(uint32_t inUsage, void** vaddr);
     status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
@@ -160,7 +166,7 @@
     const GraphicBuffer& operator = (const GraphicBuffer& rhs) const;
 
     status_t initSize(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
-            uint32_t inUsage, std::string requestorName);
+            uint32_t inLayerCount, uint32_t inUsage, std::string requestorName);
 
     void free_handle();
 
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 9cc5806..16967d4 100644
--- a/include/ui/GraphicBufferAllocator.h
+++ b/include/ui/GraphicBufferAllocator.h
@@ -65,8 +65,9 @@
     static inline GraphicBufferAllocator& get() { return getInstance(); }
 
     status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
-            uint32_t usage, buffer_handle_t* handle, uint32_t* stride,
-            uint64_t graphicBufferId, std::string requestorName);
+            uint32_t layerCount, uint32_t usage, buffer_handle_t* handle,
+            uint32_t* stride, uint64_t graphicBufferId,
+            std::string requestorName);
 
     status_t free(buffer_handle_t handle);
 
@@ -79,6 +80,7 @@
         uint32_t height;
         uint32_t stride;
         PixelFormat format;
+        uint32_t layerCount;
         uint32_t usage;
         size_t size;
         std::string requestorName;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 470fc59..15c441e 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -514,7 +514,7 @@
         // grow objects
         if (mObjectsCapacity < mObjectsSize + numObjects) {
             size_t newSize = ((mObjectsSize + numObjects)*3)/2;
-            if (newSize < mObjectsSize) return NO_MEMORY;   // overflow
+            if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY;   // overflow
             binder_size_t *objects =
                 (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
             if (objects == (binder_size_t*)0) {
@@ -1314,7 +1314,7 @@
     }
     if (!enoughObjects) {
         size_t newSize = ((mObjectsSize+2)*3)/2;
-        if (newSize < mObjectsSize) return NO_MEMORY;   // overflow
+        if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY;   // overflow
         binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
         if (objects == NULL) return NO_MEMORY;
         mObjects = objects;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 9e9799b..9ba85a6 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -104,7 +104,7 @@
         "liblog",
     ],
 
-    export_shared_lib_headers: ["libbinder"],
+    export_shared_lib_headers: ["libbinder", "libui"],
 }
 
 subdirs = ["tests"]
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 7fbf312..ee4c58c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -716,6 +716,7 @@
 }
 
 sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
+    Mutex::Autolock lock(mCore->mMutex);
     return mCore->mSidebandStream;
 }
 
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 90b4b9d..81d4f31 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -42,6 +42,8 @@
 
 namespace android {
 
+static constexpr uint32_t BQ_LAYER_COUNT = 1;
+
 BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
         bool consumerIsSurfaceFlinger) :
     mCore(core),
@@ -414,7 +416,8 @@
             // buffer. If this buffer would require reallocation to meet the
             // requested attributes, we free it and attempt to get another one.
             if (!mCore->mAllowAllocation) {
-                if (buffer->needsReallocation(width, height, format, usage)) {
+                if (buffer->needsReallocation(width, height, format,
+                        BQ_LAYER_COUNT, usage)) {
                     if (mCore->mSharedBufferSlot == found) {
                         BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                                 "buffer");
@@ -430,7 +433,8 @@
 
         const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
         if (mCore->mSharedBufferSlot == found &&
-                buffer->needsReallocation(width,  height, format, usage)) {
+                buffer->needsReallocation(width, height, format,
+                        BQ_LAYER_COUNT, usage)) {
             BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                     "buffer");
 
@@ -449,7 +453,8 @@
         mSlots[found].mBufferState.dequeue();
 
         if ((buffer == NULL) ||
-                buffer->needsReallocation(width, height, format, usage))
+                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT,
+                usage))
         {
             mSlots[found].mAcquireCalled = false;
             mSlots[found].mGraphicBuffer = NULL;
@@ -500,7 +505,7 @@
         status_t error;
         BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
         sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
-                width, height, format, usage,
+                width, height, format, BQ_LAYER_COUNT, usage,
                 {mConsumerName.string(), mConsumerName.size()}, &error));
         { // Autolock scope
             Mutex::Autolock lock(mCore->mMutex);
@@ -1039,6 +1044,10 @@
         case NATIVE_WINDOW_FORMAT:
             value = static_cast<int32_t>(mCore->mDefaultBufferFormat);
             break;
+        case NATIVE_WINDOW_LAYER_COUNT:
+            // All BufferQueue buffers have a single layer.
+            value = BQ_LAYER_COUNT;
+            break;
         case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
             value = mCore->getMinUndequeuedBufferCountLocked();
             break;
@@ -1287,8 +1296,9 @@
         for (size_t i = 0; i <  newBufferCount; ++i) {
             status_t result = NO_ERROR;
             sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
-                    allocWidth, allocHeight, allocFormat, allocUsage,
-                    {mConsumerName.string(), mConsumerName.size()}, &result));
+                    allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
+                    allocUsage, {mConsumerName.string(), mConsumerName.size()},
+                    &result));
             if (result != NO_ERROR) {
                 BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
                         " %u, usage %u)", width, height, format, usage);
@@ -1366,6 +1376,7 @@
 
 String8 BufferQueueProducer::getConsumerName() const {
     ATRACE_CALL();
+    Mutex::Autolock lock(mCore->mMutex);
     BQ_LOGV("getConsumerName: %s", mConsumerName.string());
     return mConsumerName;
 }
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
index e6150f4..9d045dd 100644
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -32,19 +32,21 @@
 }
 
 sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
-        uint32_t height, PixelFormat format, uint32_t usage,
-        std::string requestorName, status_t* error) {
+        uint32_t height, PixelFormat format, uint32_t layerCount,
+        uint32_t usage, std::string requestorName, status_t* error) {
     sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
-            width, height, format, usage, std::move(requestorName)));
+            width, height, format, layerCount, usage,
+            std::move(requestorName)));
     status_t err = graphicBuffer->initCheck();
     *error = err;
     if (err != 0 || graphicBuffer->handle == 0) {
         if (err == NO_MEMORY) {
             GraphicBuffer::dumpAllocationsToSystemLog();
         }
-        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
+        ALOGE("GraphicBufferAlloc::createGraphicBuffer(w=%u, h=%u, lc=%u) "
              "failed (%s), handle=%p",
-                width, height, strerror(-err), graphicBuffer->handle);
+                width, height, layerCount, strerror(-err),
+                graphicBuffer->handle);
         return 0;
     }
     return graphicBuffer;
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index 2fb380c..a3d3b74 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -45,13 +45,14 @@
     virtual ~BpGraphicBufferAlloc();
 
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width,
-            uint32_t height, PixelFormat format, uint32_t usage,
-            std::string requestorName, status_t* error) {
+            uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint32_t usage, std::string requestorName, status_t* error) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
         data.writeUint32(width);
         data.writeUint32(height);
         data.writeInt32(static_cast<int32_t>(format));
+        data.writeUint32(layerCount);
         data.writeUint32(usage);
         if (requestorName.empty()) {
             requestorName += "[PID ";
@@ -106,12 +107,13 @@
             uint32_t width = data.readUint32();
             uint32_t height = data.readUint32();
             PixelFormat format = static_cast<PixelFormat>(data.readInt32());
+            uint32_t layerCount = data.readUint32();
             uint32_t usage = data.readUint32();
             status_t error = NO_ERROR;
             std::string requestorName;
             data.readUtf8FromUtf16(&requestorName);
             sp<GraphicBuffer> result = createGraphicBuffer(width, height,
-                    format, usage, requestorName, &error);
+                    format, layerCount, usage, requestorName, &error);
             reply->writeInt32(error);
             if (result != 0) {
                 reply->write(*result);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3c7958f..092d597 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -35,7 +35,6 @@
         "libbinder",
         "libcutils",
         "libgui",
-        "libsync",
         "libui",
         "libutils",
     ],
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 0de60c9..1aa03a5 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -140,6 +140,14 @@
     EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
 }
 
+TEST_F(SurfaceTest, LayerCountIsOne) {
+    sp<ANativeWindow> anw(mSurface);
+    int result = -123;
+    int err = anw->query(anw.get(), NATIVE_WINDOW_LAYER_COUNT, &result);
+    EXPECT_EQ(NO_ERROR, err);
+    EXPECT_EQ(1, result);
+}
+
 TEST_F(SurfaceTest, QueryConsumerUsage) {
     const int TEST_USAGE_FLAGS =
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
diff --git a/libs/ui/Gralloc1.cpp b/libs/ui/Gralloc1.cpp
index 4c73ce4..367d1ce 100644
--- a/libs/ui/Gralloc1.cpp
+++ b/libs/ui/Gralloc1.cpp
@@ -77,6 +77,17 @@
             mShimDevice.mDevice, mDeviceId, format, &mFormat);
 }
 
+gralloc1_error_t Descriptor::setLayerCount(uint32_t layerCount)
+{
+    if (mShimDevice.hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+        return setHelper<uint32_t>(mShimDevice.mFunctions.setLayerCount.pfn,
+                mShimDevice.mDevice, mDeviceId, layerCount, &mLayerCount);
+    } else {
+        // Layered buffers are not supported on this device.
+        return GRALLOC1_ERROR_UNSUPPORTED;
+    }
+}
+
 gralloc1_error_t Descriptor::setProducerUsage(gralloc1_producer_usage_t usage)
 {
     return setHelper<uint64_t>(mShimDevice.mFunctions.setProducerUsage.pfn,
@@ -366,6 +377,15 @@
         mFunctions.allocate.load(mDevice, false);
     }
 
+    if (hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+        if (!mFunctions.setLayerCount.load(mDevice, true)) {
+            return false;
+        }
+        if (!mFunctions.getLayerCount.load(mDevice, true)) {
+            return false;
+        }
+    }
+
     return true;
 }
 
diff --git a/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp
index d5b88de..111879a 100644
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ b/libs/ui/Gralloc1On0Adapter.cpp
@@ -97,6 +97,8 @@
             return asFP<GRALLOC1_PFN_SET_DIMENSIONS>(setDimensionsHook);
         case GRALLOC1_FUNCTION_SET_FORMAT:
             return asFP<GRALLOC1_PFN_SET_FORMAT>(setFormatHook);
+        case GRALLOC1_FUNCTION_SET_LAYER_COUNT:
+            return asFP<GRALLOC1_PFN_SET_LAYER_COUNT>(setLayerCountHook);
         case GRALLOC1_FUNCTION_SET_PRODUCER_USAGE:
             return asFP<GRALLOC1_PFN_SET_PRODUCER_USAGE>(setProducerUsageHook);
         case GRALLOC1_FUNCTION_GET_BACKING_STORE:
@@ -113,6 +115,10 @@
             return asFP<GRALLOC1_PFN_GET_FORMAT>(
                     bufferHook<decltype(&Buffer::getFormat),
                     &Buffer::getFormat, int32_t*>);
+        case GRALLOC1_FUNCTION_GET_LAYER_COUNT:
+            return asFP<GRALLOC1_PFN_GET_LAYER_COUNT>(
+                    bufferHook<decltype(&Buffer::getLayerCount),
+                    &Buffer::getLayerCount, uint32_t*>);
         case GRALLOC1_FUNCTION_GET_PRODUCER_USAGE:
             return asFP<GRALLOC1_PFN_GET_PRODUCER_USAGE>(getProducerUsageHook);
         case GRALLOC1_FUNCTION_GET_STRIDE:
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 6d72900..07164a4 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -50,6 +50,7 @@
     height =
     stride =
     format =
+    layerCount =
     usage  = 0;
     handle = NULL;
 }
@@ -63,15 +64,33 @@
     height =
     stride =
     format =
+    layerCount =
     usage  = 0;
     handle = NULL;
-    mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage,
+    mInitCheck = initSize(inWidth, inHeight, inFormat, 1, inUsage,
             std::move(requestorName));
 }
 
 GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage, uint32_t inStride,
-        native_handle_t* inHandle, bool keepOwnership)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+        std::string requestorName)
+    : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
+      mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
+{
+    width  =
+    height =
+    stride =
+    format =
+    layerCount =
+    usage  = 0;
+    handle = NULL;
+    mInitCheck = initSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+            std::move(requestorName));
+}
+
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+        uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
     : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
       mBufferMapper(GraphicBufferMapper::get()),
       mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0)
@@ -80,6 +99,7 @@
     height = static_cast<int>(inHeight);
     stride = static_cast<int>(inStride);
     format = inFormat;
+    layerCount = inLayerCount;
     usage  = static_cast<int>(inUsage);
     handle = inHandle;
 }
@@ -94,6 +114,7 @@
     height = buffer->height;
     stride = buffer->stride;
     format = buffer->format;
+    layerCount = buffer->layerCount;
     usage  = buffer->usage;
     handle = buffer->handle;
 }
@@ -138,7 +159,7 @@
 }
 
 status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage)
 {
     if (mOwner != ownData)
         return INVALID_OPERATION;
@@ -147,6 +168,7 @@
             static_cast<int>(inWidth) == width &&
             static_cast<int>(inHeight) == height &&
             inFormat == format &&
+            inLayerCount == layerCount &&
             static_cast<int>(inUsage) == usage)
         return NO_ERROR;
 
@@ -155,30 +177,34 @@
         allocator.free(handle);
         handle = 0;
     }
-    return initSize(inWidth, inHeight, inFormat, inUsage, "[Reallocation]");
+    return initSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+            "[Reallocation]");
 }
 
 bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage)
 {
     if (static_cast<int>(inWidth) != width) return true;
     if (static_cast<int>(inHeight) != height) return true;
     if (inFormat != format) return true;
+    if (inLayerCount != layerCount) return true;
     if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true;
     return false;
 }
 
 status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inUsage, std::string requestorName)
+        PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+        std::string requestorName)
 {
     GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
     uint32_t outStride = 0;
-    status_t err = allocator.allocate(inWidth, inHeight, inFormat, inUsage,
-            &handle, &outStride, mId, std::move(requestorName));
+    status_t err = allocator.allocate(inWidth, inHeight, inFormat, inLayerCount,
+            inUsage, &handle, &outStride, mId, std::move(requestorName));
     if (err == NO_ERROR) {
         width = static_cast<int>(inWidth);
         height = static_cast<int>(inHeight);
         format = inFormat;
+        layerCount = inLayerCount;
         usage = static_cast<int>(inUsage);
         stride = static_cast<int>(outStride);
     }
@@ -284,7 +310,7 @@
 }
 
 size_t GraphicBuffer::getFlattenedSize() const {
-    return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
+    return static_cast<size_t>(12 + (handle ? handle->numInts : 0)) * sizeof(int);
 }
 
 size_t GraphicBuffer::getFdCount() const {
@@ -304,19 +330,20 @@
     buf[2] = height;
     buf[3] = stride;
     buf[4] = format;
-    buf[5] = usage;
-    buf[6] = static_cast<int32_t>(mId >> 32);
-    buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
-    buf[8] = static_cast<int32_t>(mGenerationNumber);
-    buf[9] = 0;
+    buf[5] = static_cast<int32_t>(layerCount);
+    buf[6] = usage;
+    buf[7] = static_cast<int32_t>(mId >> 32);
+    buf[8] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
+    buf[9] = static_cast<int32_t>(mGenerationNumber);
     buf[10] = 0;
+    buf[11] = 0;
 
     if (handle) {
-        buf[9] = handle->numFds;
-        buf[10] = handle->numInts;
+        buf[10] = handle->numFds;
+        buf[11] = handle->numInts;
         memcpy(fds, handle->data,
                 static_cast<size_t>(handle->numFds) * sizeof(int));
-        memcpy(&buf[11], handle->data + handle->numFds,
+        memcpy(&buf[12], handle->data + handle->numFds,
                 static_cast<size_t>(handle->numInts) * sizeof(int));
     }
 
@@ -332,28 +359,28 @@
 
 status_t GraphicBuffer::unflatten(
         void const*& buffer, size_t& size, int const*& fds, size_t& count) {
-    if (size < 11 * sizeof(int)) return NO_MEMORY;
+    if (size < 12 * sizeof(int)) return NO_MEMORY;
 
     int const* buf = static_cast<int const*>(buffer);
     if (buf[0] != 'GBFR') return BAD_TYPE;
 
-    const size_t numFds  = static_cast<size_t>(buf[9]);
-    const size_t numInts = static_cast<size_t>(buf[10]);
+    const size_t numFds  = static_cast<size_t>(buf[10]);
+    const size_t numInts = static_cast<size_t>(buf[11]);
 
     // Limit the maxNumber to be relatively small. The number of fds or ints
     // should not come close to this number, and the number itself was simply
     // chosen to be high enough to not cause issues and low enough to prevent
     // overflow problems.
     const size_t maxNumber = 4096;
-    if (numFds >= maxNumber || numInts >= (maxNumber - 11)) {
-        width = height = stride = format = usage = 0;
+    if (numFds >= maxNumber || numInts >= (maxNumber - 12)) {
+        width = height = stride = format = layerCount = usage = 0;
         handle = NULL;
         ALOGE("unflatten: numFds or numInts is too large: %zd, %zd",
                 numFds, numInts);
         return BAD_VALUE;
     }
 
-    const size_t sizeNeeded = (11 + numInts) * sizeof(int);
+    const size_t sizeNeeded = (12 + numInts) * sizeof(int);
     if (size < sizeNeeded) return NO_MEMORY;
 
     size_t fdCountNeeded = numFds;
@@ -369,34 +396,35 @@
         height = buf[2];
         stride = buf[3];
         format = buf[4];
-        usage  = buf[5];
+        layerCount = static_cast<uintptr_t>(buf[5]);
+        usage  = buf[6];
         native_handle* h = native_handle_create(
                 static_cast<int>(numFds), static_cast<int>(numInts));
         if (!h) {
-            width = height = stride = format = usage = 0;
+            width = height = stride = format = layerCount = usage = 0;
             handle = NULL;
             ALOGE("unflatten: native_handle_create failed");
             return NO_MEMORY;
         }
         memcpy(h->data, fds, numFds * sizeof(int));
-        memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
+        memcpy(h->data + numFds, &buf[12], numInts * sizeof(int));
         handle = h;
     } else {
-        width = height = stride = format = usage = 0;
+        width = height = stride = format = layerCount = usage = 0;
         handle = NULL;
     }
 
-    mId = static_cast<uint64_t>(buf[6]) << 32;
-    mId |= static_cast<uint32_t>(buf[7]);
+    mId = static_cast<uint64_t>(buf[7]) << 32;
+    mId |= static_cast<uint32_t>(buf[8]);
 
-    mGenerationNumber = static_cast<uint32_t>(buf[8]);
+    mGenerationNumber = static_cast<uint32_t>(buf[9]);
 
     mOwner = ownHandle;
 
     if (handle != 0) {
         status_t err = mBufferMapper.registerBuffer(this);
         if (err != NO_ERROR) {
-            width = height = stride = format = usage = 0;
+            width = height = stride = format = layerCount = usage = 0;
             handle = NULL;
             ALOGE("unflatten: registerBuffer failed: %s (%d)",
                     strerror(-err), err);
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 693c7cb..e333bc1 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -64,15 +64,15 @@
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
         if (rec.size) {
-            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+            snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%08x | %s\n",
                     list.keyAt(i), rec.size/1024.0f,
-                    rec.width, rec.stride, rec.height, rec.format, rec.usage,
-                    rec.requestorName.c_str());
+                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+                    rec.usage, rec.requestorName.c_str());
         } else {
-            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %8X | 0x%08x | %s\n",
+            snprintf(buffer, SIZE, "%10p: unknown     | %4u (%4u) x %4u | %4u | %8X | 0x%08x | %s\n",
                     list.keyAt(i),
-                    rec.width, rec.stride, rec.height, rec.format, rec.usage,
-                    rec.requestorName.c_str());
+                    rec.width, rec.stride, rec.height, rec.layerCount, rec.format,
+                    rec.usage, rec.requestorName.c_str());
         }
         result.append(buffer);
         total += rec.size;
@@ -103,21 +103,22 @@
 public:
     HalBuffer(const Gralloc2::Allocator* allocator,
             uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t usage)
+            PixelFormat format, uint32_t layerCount, uint32_t usage)
         : mAllocator(allocator), mBufferValid(false)
     {
         Gralloc2::IAllocator::BufferDescriptorInfo info = {};
         info.width = width;
         info.height = height;
         info.format = static_cast<Gralloc2::PixelFormat>(format);
+        info.layerCount = layerCount;
         info.producerUsageMask = usage;
         info.consumerUsageMask = usage;
 
         Gralloc2::BufferDescriptor descriptor;
         auto error = mAllocator->createBufferDescriptor(info, descriptor);
         if (error != Gralloc2::Error::NONE) {
-            ALOGE("Failed to create desc (%u x %u) format %d usage %u: %d",
-                    width, height, format, usage, error);
+            ALOGE("Failed to create desc (%u x %u) layerCount %u format %d usage %u: %d",
+                    width, height, layerCount, format, usage, error);
             return;
         }
 
@@ -127,8 +128,8 @@
         }
 
         if (error != Gralloc2::Error::NONE) {
-            ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
-                    width, height, format, usage, error);
+            ALOGE("Failed to allocate (%u x %u) layerCount %u format %d usage %u: %d",
+                    width, height, layerCount, format, usage, error);
             mAllocator->destroyBufferDescriptor(descriptor);
             return;
         }
@@ -195,8 +196,9 @@
 } // namespace
 
 status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t usage, buffer_handle_t* handle,
-        uint32_t* stride, uint64_t graphicBufferId, std::string requestorName)
+        PixelFormat format, uint32_t layerCount, uint32_t usage,
+        buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
+        std::string requestorName)
 {
     ATRACE_CALL();
 
@@ -205,12 +207,17 @@
     if (!width || !height)
         width = height = 1;
 
+    // Ensure that layerCount is valid.
+    if (layerCount < 1)
+        layerCount = 1;
+
     // Filter out any usage bits that should not be passed to the gralloc module
     usage &= GRALLOC_USAGE_ALLOC_MASK;
 
     gralloc1_error_t error;
     if (mAllocator->valid()) {
-        HalBuffer buffer(mAllocator.get(), width, height, format, usage);
+        HalBuffer buffer(mAllocator.get(), width, height, format, layerCount,
+                usage);
         if (!buffer.exportHandle(mMapper, handle, stride)) {
             return NO_MEMORY;
         }
@@ -229,6 +236,17 @@
             ALOGE("Failed to set format to %d: %d", format, error);
             return BAD_VALUE;
         }
+        if (mDevice->hasCapability(GRALLOC1_CAPABILITY_LAYERED_BUFFERS)) {
+            error = descriptor->setLayerCount(layerCount);
+            if (error != GRALLOC1_ERROR_NONE) {
+                ALOGE("Failed to set layer count to %u: %d", layerCount, error);
+                return BAD_VALUE;
+            }
+        } else if (layerCount > 1) {
+            ALOGE("Failed to set layer count to %u: capability unsupported",
+                    layerCount);
+            return BAD_VALUE;
+        }
         error = descriptor->setProducerUsage(
                 static_cast<gralloc1_producer_usage_t>(usage));
         if (error != GRALLOC1_ERROR_NONE) {
@@ -244,8 +262,8 @@
 
         error = mDevice->allocate(descriptor, graphicBufferId, handle);
         if (error != GRALLOC1_ERROR_NONE) {
-            ALOGE("Failed to allocate (%u x %u) format %d usage %u: %d",
-                    width, height, format, usage, error);
+            ALOGE("Failed to allocate (%u x %u) layerCount %u format %d usage %u: %d",
+                    width, height, layerCount, format, usage, error);
             return NO_MEMORY;
         }
 
@@ -264,6 +282,7 @@
         rec.height = height;
         rec.stride = *stride;
         rec.format = format;
+        rec.layerCount = layerCount;
         rec.usage = usage;
         rec.size = static_cast<size_t>(height * (*stride) * bpp);
         rec.requestorName = std::move(requestorName);
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 8e69330..7cb608e 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -607,6 +607,7 @@
 
 #ifndef EGL_ANDROID_create_native_client_buffer
 #define EGL_ANDROID_create_native_client_buffer 1
+#define EGL_LAYER_COUNT_ANDROID 0x3434
 #define EGL_NATIVE_BUFFER_USAGE_ANDROID   0x3143
 #define EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID   0x00000001
 #define EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID   0x00000002
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 3f2861f..0bfefd0 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -1802,6 +1802,7 @@
     uint32_t width = 0;
     uint32_t height = 0;
     uint32_t format = 0;
+    uint32_t layer_count = 1;
     uint32_t red_size = 0;
     uint32_t green_size = 0;
     uint32_t blue_size = 0;
@@ -1827,6 +1828,7 @@
                 GET_NONNEGATIVE_VALUE(EGL_GREEN_SIZE, green_size);
                 GET_NONNEGATIVE_VALUE(EGL_BLUE_SIZE, blue_size);
                 GET_NONNEGATIVE_VALUE(EGL_ALPHA_SIZE, alpha_size);
+                GET_NONNEGATIVE_VALUE(EGL_LAYER_COUNT_ANDROID, layer_count);
                 case EGL_NATIVE_BUFFER_USAGE_ANDROID:
                     if (value & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_PROTECTED;
@@ -1862,7 +1864,7 @@
                alpha_size == 0) {
         format = HAL_PIXEL_FORMAT_RGB_565;
     } else {
-        ALOGE("Invalid native pixel format { r=%d, g=%d, b=%d, a=%d }",
+        ALOGE("Invalid native pixel format { r=%u, g=%u, b=%u, a=%u }",
                 red_size, green_size, blue_size, alpha_size);
         return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
     }
@@ -1907,7 +1909,9 @@
     CHECK_ERROR_CONDITION("Unable to write height");
     err = data.writeInt32(static_cast<int32_t>(format));
     CHECK_ERROR_CONDITION("Unable to write format");
-    err = data.writeUint32(usage);
+    err = data.writeUint32(layer_count);
+    CHECK_ERROR_CONDITION("Unable to write layer count");
+     err = data.writeUint32(usage);
     CHECK_ERROR_CONDITION("Unable to write usage");
     err = data.writeUtf8AsUtf16(
             std::string("[eglCreateNativeClientBufferANDROID pid ") +
@@ -1924,12 +1928,13 @@
 
     err = gBuffer->initCheck();
     if (err != NO_ERROR) {
-        ALOGE("Unable to create native buffer { w=%d, h=%d, f=%d, u=%#x }: %#x",
-                width, height, format, usage, err);
+        ALOGE("Unable to create native buffer "
+                "{ w=%u, h=%u, f=%u, u=%#x, lc=%u}: %#x", width, height, format,
+                usage, layer_count, err);
         goto error_condition;
     }
-    ALOGD("Created new native buffer %p { w=%d, h=%d, f=%d, u=%#x }",
-            gBuffer, width, height, format, usage);
+    ALOGV("Created new native buffer %p { w=%u, h=%u, f=%u, u=%#x, lc=%u}",
+            gBuffer, width, height, format, usage, layer_count);
     return static_cast<EGLClientBuffer>(gBuffer->getNativeBuffer());
 
 #undef CHECK_ERROR_CONDITION
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
index a6fae80..9b2bbbc 100644
--- a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -20,7 +20,7 @@
 
 Version
 
-    Version 1, January 19, 2016
+    Version 1.1, October 26, 2016
 
 Number
 
@@ -53,6 +53,7 @@
 
 New Tokens
 
+    EGL_NATIVE_BUFFER_LAYER_COUNT_ANDROID 0x3434
     EGL_NATIVE_BUFFER_USAGE_ANDROID 0x3143
     EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID 0x00000001
     EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID 0x00000002
@@ -103,6 +104,8 @@
       | EGL_ALPHA_SIZE                  | The bits of Alpha in | 0             |
       |                                 | the color buffer     |               |
       |                                 | buffer data          |               |
+      | EGL_LAYER_COUNT_ANDROID         | The number of image  | 1             |
+      |                                 | layers in the buffer |               |
       | EGL_NATIVE_BUFFER_USAGE_ANDROID | The usage bits of    | 0             |
       |                                 | the buffer data      |               |
       +---------------------------------+----------------------+---------------+
@@ -114,8 +117,10 @@
     EGL_RED_SIZE, EGL_GREEN_SIZE, and EGL_BLUE_SIZE must be non-zero and
     correspond to a valid pixel format for the implementation. If EGL_ALPHA_SIZE
     is non-zero then the combination of all four sizes must correspond to a
-    valid pixel format for the implementation. The
-    EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of the following bits:
+    valid pixel format for the implementation. The value of
+    EGL_LAYER_COUNT_ANDROID must be a valid number of image layers for the
+    implementation. The EGL_NATIVE_BUFFER_USAGE_ANDROID flag may include any of
+    the following bits:
 
         EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID: Indicates that the
         created buffer must have a hardware-protected path to external display
@@ -189,6 +194,10 @@
 
 Revision History
 
+#3 (Craig Donner, October 26, 2016)
+    - Added EGL_LAYER_COUNT_ANDROID for creating buffers that back texture
+    arrays.
+
 #2 (Craig Donner, April 15, 2016)
     - Set color formats and usage bits explicitly using additional attributes,
     and add value for new token EGL_NATIVE_BUFFER_USAGE_ANDROID.
diff --git a/services/sensorservice/SensorDeviceTreble.cpp b/services/sensorservice/SensorDeviceTreble.cpp
index 66b0f5a..6f946d0 100644
--- a/services/sensorservice/SensorDeviceTreble.cpp
+++ b/services/sensorservice/SensorDeviceTreble.cpp
@@ -27,7 +27,7 @@
 #include "SensorDevice.h"
 #include "SensorService.h"
 
-#include "convert.h"
+#include <sensors/convert.h>
 
 using android::hardware::sensors::V1_0::ISensors;
 using android::hardware::hidl_vec;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index ba036dd..3800ce8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -15,6 +15,7 @@
     GpuService.cpp \
     Layer.cpp \
     LayerDim.cpp \
+    LayerRejecter.cpp \
     MessageQueue.cpp \
     MonitoredProducer.cpp \
     SurfaceFlingerConsumer.cpp \
@@ -51,7 +52,11 @@
     LOCAL_SRC_FILES += \
         SurfaceFlinger.cpp \
         DisplayHardware/HWComposer.cpp
+    ifeq ($(TARGET_USES_HWC2ON1ADAPTER), true)
+        LOCAL_CFLAGS += -DBYPASS_IHWC
+    endif
 else
+    LOCAL_CFLAGS += -DBYPASS_IHWC
     LOCAL_SRC_FILES += \
         SurfaceFlinger_hwc1.cpp \
         DisplayHardware/HWComposer_hwc1.cpp
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index c79caf4..31af8a1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -614,6 +614,8 @@
     auto error = static_cast<Error>(intError);
 
     if (error != Error::None) {
+        ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
+        *outConfig = nullptr;
         return error;
     }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 1145ba1..1c709b2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,10 +17,6 @@
 #ifndef ANDROID_SF_HWC2_H
 #define ANDROID_SF_HWC2_H
 
-#ifndef USE_HWC2
-#define BYPASS_IHWC
-#endif
-
 #define HWC2_INCLUDE_STRINGIFICATION
 #define HWC2_USE_CPP11
 #include <hardware/hwcomposer2.h>
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index b699b2c..1107c99 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -1841,29 +1841,39 @@
 
     auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
             mDevice.mHwc1Device, mHwc1Id);
-    if (activeConfig >= 0) {
-        for (const auto& config : mConfigs) {
-            if (config->hasHwc1Id(activeConfig)) {
-                ALOGV("Setting active config to %d for HWC1 config %u",
-                        config->getId(), activeConfig);
-                mActiveConfig = config;
-                if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
-                    // This should never happen since we checked for the config's presence before
-                    // setting it as active.
-                    ALOGE("Unable to find color mode for active HWC1 config %d",
-                            config->getId());
-                    mActiveColorMode = HAL_COLOR_MODE_NATIVE;
-                }
-                break;
+
+    // Some devices startup without an activeConfig:
+    // We need to set one ourselves.
+    if (activeConfig == HWC_ERROR) {
+        ALOGV("There is no active configuration: Picking the first one: 0.");
+        const int defaultIndex = 0;
+        mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device, mHwc1Id, defaultIndex);
+        activeConfig = defaultIndex;
+    }
+
+    for (const auto& config : mConfigs) {
+        if (config->hasHwc1Id(activeConfig)) {
+            ALOGE("Setting active config to %d for HWC1 config %u", config->getId(), activeConfig);
+            mActiveConfig = config;
+            if (config->getColorModeForHwc1Id(activeConfig, &mActiveColorMode) != Error::None) {
+                // This should never happen since we checked for the config's presence before
+                // setting it as active.
+                ALOGE("Unable to find color mode for active HWC1 config %d", config->getId());
+                mActiveColorMode = HAL_COLOR_MODE_NATIVE;
             }
-        }
-        if (!mActiveConfig) {
-            ALOGV("Unable to find active HWC1 config %u, defaulting to "
-                    "config 0", activeConfig);
-            mActiveConfig = mConfigs[0];
-            mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+            break;
         }
     }
+    if (!mActiveConfig) {
+        ALOGV("Unable to find active HWC1 config %u, defaulting to "
+                "config 0", activeConfig);
+        mActiveConfig = mConfigs[0];
+        mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+    }
+
+
+
+
 }
 
 void HWC2On1Adapter::Display::reallocateHwc1Contents()
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index bb2e45a..82a900c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -346,14 +346,14 @@
 std::shared_ptr<const HWC2::Display::Config>
         HWComposer::getActiveConfig(int32_t displayId) const {
     if (!isValidDisplay(displayId)) {
-        ALOGE("getActiveConfigs: Attempted to access invalid display %d",
+        ALOGV("getActiveConfigs: Attempted to access invalid display %d",
                 displayId);
         return nullptr;
     }
     std::shared_ptr<const HWC2::Display::Config> config;
     auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config);
     if (error == HWC2::Error::BadConfig) {
-        ALOGV("getActiveConfig: No config active, returning null");
+        ALOGE("getActiveConfig: No config active, returning null");
         return nullptr;
     } else if (error != HWC2::Error::None) {
         ALOGE("getActiveConfig failed for display %d: %s (%d)", displayId,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f9e93d3..5f96f66 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -44,6 +44,7 @@
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "LayerRejecter.h"
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 
@@ -1786,6 +1787,32 @@
 #endif
 }
 
+bool Layer::allTransactionsSignaled() {
+    auto headFrameNumber = getHeadFrameNumber();
+    bool matchingFramesFound = false;
+    bool allTransactionsApplied = true;
+    Mutex::Autolock lock(mLocalSyncPointMutex);
+
+    for (auto& point : mLocalSyncPoints) {
+        if (point->getFrameNumber() > headFrameNumber) {
+            break;
+        }
+        matchingFramesFound = true;
+
+        if (!point->frameIsAvailable()) {
+           // We haven't notified the remote layer that the frame for
+           // this point is available yet. Notify it now, and then
+           // abort this attempt to latch.
+           point->setFrameAvailable();
+           allTransactionsApplied = false;
+           break;
+        }
+
+        allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied();
+    }
+    return !matchingFramesFound || allTransactionsApplied;
+}
+
 Region Layer::latchBuffer(bool& recomputeVisibleRegions)
 {
     ATRACE_CALL();
@@ -1829,163 +1856,7 @@
     const bool oldOpacity = isOpaque(s);
     sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
 
-    struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
-        Layer::State& front;
-        Layer::State& current;
-        bool& recomputeVisibleRegions;
-        bool stickyTransformSet;
-        const char* name;
-        int32_t overrideScalingMode;
-        bool& freezePositionUpdates;
-
-        Reject(Layer::State& front, Layer::State& current,
-                bool& recomputeVisibleRegions, bool stickySet,
-                const char* name,
-                int32_t overrideScalingMode,
-                bool& freezePositionUpdates)
-            : front(front), current(current),
-              recomputeVisibleRegions(recomputeVisibleRegions),
-              stickyTransformSet(stickySet),
-              name(name),
-              overrideScalingMode(overrideScalingMode),
-              freezePositionUpdates(freezePositionUpdates) {
-        }
-
-        virtual bool reject(const sp<GraphicBuffer>& buf,
-                const BufferItem& item) {
-            if (buf == NULL) {
-                return false;
-            }
-
-            uint32_t bufWidth  = buf->getWidth();
-            uint32_t bufHeight = buf->getHeight();
-
-            // check that we received a buffer of the right size
-            // (Take the buffer's orientation into account)
-            if (item.mTransform & Transform::ROT_90) {
-                swap(bufWidth, bufHeight);
-            }
-
-            int actualScalingMode = overrideScalingMode >= 0 ?
-                    overrideScalingMode : item.mScalingMode;
-            bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-            if (front.active != front.requested) {
-
-                if (isFixedSize ||
-                        (bufWidth == front.requested.w &&
-                         bufHeight == front.requested.h))
-                {
-                    // Here we pretend the transaction happened by updating the
-                    // current and drawing states. Drawing state is only accessed
-                    // in this thread, no need to have it locked
-                    front.active = front.requested;
-
-                    // We also need to update the current state so that
-                    // we don't end-up overwriting the drawing state with
-                    // this stale current state during the next transaction
-                    //
-                    // NOTE: We don't need to hold the transaction lock here
-                    // because State::active is only accessed from this thread.
-                    current.active = front.active;
-                    current.modified = true;
-
-                    // recompute visible region
-                    recomputeVisibleRegions = true;
-                }
-
-                ALOGD_IF(DEBUG_RESIZE,
-                        "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                        "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
-                        "            requested={ wh={%4u,%4u} }}\n",
-                        name,
-                        bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                        front.active.w, front.active.h,
-                        front.crop.left,
-                        front.crop.top,
-                        front.crop.right,
-                        front.crop.bottom,
-                        front.crop.getWidth(),
-                        front.crop.getHeight(),
-                        front.requested.w, front.requested.h);
-            }
-
-            if (!isFixedSize && !stickyTransformSet) {
-                if (front.active.w != bufWidth ||
-                    front.active.h != bufHeight) {
-                    // reject this buffer
-                    ALOGE("[%s] rejecting buffer: "
-                            "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
-                            name, bufWidth, bufHeight, front.active.w, front.active.h);
-                    return true;
-                }
-            }
-
-            // if the transparent region has changed (this test is
-            // conservative, but that's fine, worst case we're doing
-            // a bit of extra work), we latch the new one and we
-            // trigger a visible-region recompute.
-            if (!front.activeTransparentRegion.isTriviallyEqual(
-                    front.requestedTransparentRegion)) {
-                front.activeTransparentRegion = front.requestedTransparentRegion;
-
-                // We also need to update the current state so that
-                // we don't end-up overwriting the drawing state with
-                // this stale current state during the next transaction
-                //
-                // NOTE: We don't need to hold the transaction lock here
-                // because State::active is only accessed from this thread.
-                current.activeTransparentRegion = front.activeTransparentRegion;
-
-                // recompute visible region
-                recomputeVisibleRegions = true;
-            }
-
-            if (front.crop != front.requestedCrop) {
-                front.crop = front.requestedCrop;
-                current.crop = front.requestedCrop;
-                recomputeVisibleRegions = true;
-            }
-            freezePositionUpdates = false;
-
-            return false;
-        }
-    };
-
-    Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-            getProducerStickyTransform() != 0, mName.string(),
-            mOverrideScalingMode, mFreezePositionUpdates);
-
-
-    // Check all of our local sync points to ensure that all transactions
-    // which need to have been applied prior to the frame which is about to
-    // be latched have signaled
-
-    auto headFrameNumber = getHeadFrameNumber();
-    bool matchingFramesFound = false;
-    bool allTransactionsApplied = true;
-    {
-        Mutex::Autolock lock(mLocalSyncPointMutex);
-        for (auto& point : mLocalSyncPoints) {
-            if (point->getFrameNumber() > headFrameNumber) {
-                break;
-            }
-
-            matchingFramesFound = true;
-
-            if (!point->frameIsAvailable()) {
-                // We haven't notified the remote layer that the frame for
-                // this point is available yet. Notify it now, and then
-                // abort this attempt to latch.
-                point->setFrameAvailable();
-                allTransactionsApplied = false;
-                break;
-            }
-
-            allTransactionsApplied &= point->transactionIsApplied();
-        }
-    }
-
-    if (matchingFramesFound && !allTransactionsApplied) {
+    if (!allTransactionsSignaled()) {
         mFlinger->signalLayerUpdate();
         return outDirtyRegion;
     }
@@ -1995,6 +1866,9 @@
     // BufferItem's that weren't actually queued. This can happen in shared
     // buffer mode.
     bool queuedBuffer = false;
+    LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+                    getProducerStickyTransform() != 0, mName.string(),
+                    mOverrideScalingMode, mFreezePositionUpdates);
     status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
             mFlinger->mPrimaryDispSync, &mAutoRefresh, &queuedBuffer,
             mLastFrameNumberReceived);
@@ -2056,12 +1930,6 @@
         mFlinger->signalLayerUpdate();
     }
 
-    if (updateResult != NO_ERROR) {
-        // something happened!
-        recomputeVisibleRegions = true;
-        return outDirtyRegion;
-    }
-
     // update the active buffer
     mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
     if (mActiveBuffer == NULL) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index efb0fb8..64b049c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -558,6 +558,11 @@
 
     // -----------------------------------------------------------------------
 
+    // Check all of the local sync points to ensure that all transactions
+    // which need to have been applied prior to the frame which is about to
+    // be latched have signaled
+    bool allTransactionsSignaled();
+
     // constants
     sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
new file mode 100644
index 0000000..2bc0605
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 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 "LayerRejecter.h"
+
+#include "clz.h"
+
+#define DEBUG_RESIZE 0
+
+namespace android {
+
+LayerRejecter::LayerRejecter(Layer::State& front,
+                             Layer::State& current,
+                             bool& recomputeVisibleRegions,
+                             bool stickySet,
+                             const char* name,
+                             int32_t overrideScalingMode,
+                             bool& freezePositionUpdates)
+  : mFront(front),
+    mCurrent(current),
+    mRecomputeVisibleRegions(recomputeVisibleRegions),
+    mStickyTransformSet(stickySet),
+    mName(name),
+    mOverrideScalingMode(overrideScalingMode),
+    mFreezePositionUpdates(freezePositionUpdates) {}
+
+bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
+    if (buf == NULL) {
+        return false;
+    }
+
+    uint32_t bufWidth = buf->getWidth();
+    uint32_t bufHeight = buf->getHeight();
+
+    // check that we received a buffer of the right size
+    // (Take the buffer's orientation into account)
+    if (item.mTransform & Transform::ROT_90) {
+        swap(bufWidth, bufHeight);
+    }
+
+    int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+    bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
+    if (mFront.active != mFront.requested) {
+        if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) {
+            // Here we pretend the transaction happened by updating the
+            // current and drawing states. Drawing state is only accessed
+            // in this thread, no need to have it locked
+            mFront.active = mFront.requested;
+
+            // We also need to update the current state so that
+            // we don't end-up overwriting the drawing state with
+            // this stale current state during the next transaction
+            //
+            // NOTE: We don't need to hold the transaction lock here
+            // because State::active is only accessed from this thread.
+            mCurrent.active = mFront.active;
+            mCurrent.modified = true;
+
+            // recompute visible region
+            mRecomputeVisibleRegions = true;
+        }
+
+        ALOGD_IF(DEBUG_RESIZE,
+                 "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
+                 "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) "
+                 "}\n"
+                 "            requested={ wh={%4u,%4u} }}\n",
+                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w,
+                 mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right,
+                 mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(),
+                 mFront.requested.w, mFront.requested.h);
+    }
+
+    if (!isFixedSize && !mStickyTransformSet) {
+        if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) {
+            // reject this buffer
+            ALOGE("[%s] rejecting buffer: "
+                  "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
+                  mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h);
+            return true;
+        }
+    }
+
+    // if the transparent region has changed (this test is
+    // conservative, but that's fine, worst case we're doing
+    // a bit of extra work), we latch the new one and we
+    // trigger a visible-region recompute.
+    if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) {
+        mFront.activeTransparentRegion = mFront.requestedTransparentRegion;
+
+        // We also need to update the current state so that
+        // we don't end-up overwriting the drawing state with
+        // this stale current state during the next transaction
+        //
+        // NOTE: We don't need to hold the transaction lock here
+        // because State::active is only accessed from this thread.
+        mCurrent.activeTransparentRegion = mFront.activeTransparentRegion;
+
+        // recompute visible region
+        mRecomputeVisibleRegions = true;
+    }
+
+    if (mFront.crop != mFront.requestedCrop) {
+        mFront.crop = mFront.requestedCrop;
+        mCurrent.crop = mFront.requestedCrop;
+        mRecomputeVisibleRegions = true;
+    }
+    mFreezePositionUpdates = false;
+
+    return false;
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
new file mode 100644
index 0000000..c2a9483
--- /dev/null
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_REJECTER_H
+#define ANDROID_LAYER_REJECTER_H
+
+#include "Layer.h"
+#include "SurfaceFlingerConsumer.h"
+
+namespace android {
+    class LayerRejecter : public SurfaceFlingerConsumer::BufferRejecter {
+    public:
+        LayerRejecter(Layer::State &front,
+                      Layer::State &current,
+                      bool &recomputeVisibleRegions,
+                      bool stickySet,
+                      const char *name,
+                      int32_t overrideScalingMode,
+                      bool &freezePositionUpdates);
+
+        virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
+
+    private:
+        Layer::State &mFront;
+        Layer::State &mCurrent;
+        bool &mRecomputeVisibleRegions;
+        bool mStickyTransformSet;
+        const char *mName;
+        int32_t mOverrideScalingMode;
+        bool &mFreezePositionUpdates;
+    };
+}  // namespace android
+
+#endif  // ANDROID_LAYER_REJECTER_H
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 99a9265..37fd70d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -91,19 +91,6 @@
 
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
-// Workaround for b/30067360: /proc/self/environ inaccessible in SurfaceFlinger
-// => ASan fails to read ASAN_OPTIONS => alloc-dealloc-mismatch bug is not
-// suppressed and prevents the device from booting.
-#ifndef __has_feature
-#define __has_feature(x) 0
-#endif
-#if __has_feature(address_sanitizer)
-__attribute__((visibility("default")))
-extern "C" const char* __asan_default_options() {
-  return "alloc_dealloc_mismatch=0";
-}
-#endif
-
 namespace android {
 
 // This is the phase offset in nanoseconds of the software vsync event
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 3ae2c04..8e5c565 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -93,19 +93,6 @@
 
 EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
-// Workaround for b/30067360: /proc/self/environ inaccessible in SurfaceFlinger
-// => ASan fails to read ASAN_OPTIONS => alloc-dealloc-mismatch bug is not
-// suppressed and prevents the device from booting.
-#ifndef __has_feature
-#define __has_feature(x) 0
-#endif
-#if __has_feature(address_sanitizer)
-__attribute__((visibility("default")))
-extern "C" const char *__asan_default_options() {
-  return "alloc_dealloc_mismatch=0";
-}
-#endif
-
 namespace android {
 
 // This is the phase offset in nanoseconds of the software vsync event