Merge "libGLES_android: Add generic 32bpp BGRA EGLConfig"
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp
index 194a565..69ed416 100644
--- a/cmds/atrace/Android.bp
+++ b/cmds/atrace/Android.bp
@@ -6,6 +6,10 @@
 
     shared_libs: [
         "libbinder",
+        "libhwbinder",
+        "android.hidl.manager@1.0",
+        "libhidlbase",
+        "libhidltransport",
         "liblog",
         "libcutils",
         "libutils",
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 290d7a8..320d11d 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -37,6 +37,8 @@
 #include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
 #include <cutils/properties.h>
 
 #include <utils/String8.h>
@@ -47,6 +49,7 @@
 
 using namespace android;
 
+using std::string;
 #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
 
 #define MAX_SYS_FILES 10
@@ -105,6 +108,8 @@
     { "pm",         "Package Manager",  ATRACE_TAG_PACKAGE_MANAGER, { } },
     { "ss",         "System Server",    ATRACE_TAG_SYSTEM_SERVER, { } },
     { "database",   "Database",         ATRACE_TAG_DATABASE, { } },
+    { "network",    "Network",          ATRACE_TAG_NETWORK, { } },
+    { "adb",        "ADB",              ATRACE_TAG_ADB, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
         { REQ,      "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
@@ -505,6 +510,52 @@
     return true;
 }
 
+// Poke all the HAL processes in the system to get them to re-read
+// their system properties.
+static void pokeHalServices()
+{
+    using ::android::hidl::base::V1_0::IBase;
+    using ::android::hidl::manager::V1_0::IServiceManager;
+    using ::android::hardware::hidl_string;
+    using ::android::hardware::Return;
+
+    sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
+
+    if (sm == nullptr) {
+        fprintf(stderr, "failed to get IServiceManager to poke hal services\n");
+        return;
+    }
+
+    auto listRet = sm->list([&](const auto &interfaces) {
+        for (size_t i = 0; i < interfaces.size(); i++) {
+            string fqInstanceName = interfaces[i];
+            string::size_type n = fqInstanceName.find("/");
+            if (n == std::string::npos || interfaces[i].size() == n+1)
+                continue;
+            hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
+            hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
+            Return<sp<IBase>> interfaceRet = sm->get(fqInterfaceName, instanceName);
+            if (!interfaceRet.isOk()) {
+                fprintf(stderr, "failed to get service %s: %s\n",
+                        fqInstanceName.c_str(),
+                        interfaceRet.description().c_str());
+                continue;
+            }
+            sp<IBase> interface = interfaceRet;
+            auto notifyRet = interface->notifySyspropsChanged();
+            if (!notifyRet.isOk()) {
+                fprintf(stderr, "failed to notifySyspropsChanged on service %s: %s\n",
+                        fqInstanceName.c_str(),
+                        notifyRet.description().c_str());
+            }
+        }
+    });
+    if (!listRet.isOk()) {
+        // TODO(b/34242478) fix this when we determine the correct ACL
+        //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
+    }
+}
+
 // Set the trace tags that userland tracing uses, and poke the running
 // processes to pick up the new value.
 static bool setTagsProperty(uint64_t tags)
@@ -747,6 +798,7 @@
     }
     ok &= setAppCmdlineProperty(&packageList[0]);
     ok &= pokeBinderServices();
+    pokeHalServices();
 
     // Disable all the sysfs enables.  This is done as a separate loop from
     // the enables to allow the same enable to exist in multiple categories.
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index e3cc9da..747cc69 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -1,6 +1,6 @@
 ## Permissions to allow system-wide tracing to the kernel trace buffer.
 ##
-on fs
+on post-fs
 
 # Allow writing to the kernel trace log.
     chmod 0222 /sys/kernel/debug/tracing/trace_marker
diff --git a/cmds/bugreportz/.clang-format b/cmds/bugreportz/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/bugreportz/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/bugreportz/Android.mk b/cmds/bugreportz/Android.mk
index 14ba225..880bc75 100644
--- a/cmds/bugreportz/Android.mk
+++ b/cmds/bugreportz/Android.mk
@@ -1,12 +1,43 @@
 LOCAL_PATH:= $(call my-dir)
+
+# bugreportz
+# ==========
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= bugreportz.cpp
+LOCAL_SRC_FILES:= \
+   bugreportz.cpp \
+   main.cpp \
 
 LOCAL_MODULE:= bugreportz
 
-LOCAL_CFLAGS := -Wall
+LOCAL_CFLAGS := -Werror -Wall
 
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
 
 include $(BUILD_EXECUTABLE)
+
+# bugreportz_test
+# ===============
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bugreportz_test
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := -Werror -Wall
+
+LOCAL_SRC_FILES := \
+    bugreportz.cpp \
+    bugreportz_test.cpp \
+
+LOCAL_STATIC_LIBRARIES := \
+    libgmock \
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp
index 312dceb..75855cf 100644
--- a/cmds/bugreportz/bugreportz.cpp
+++ b/cmds/bugreportz/bugreportz.cpp
@@ -15,89 +15,38 @@
  */
 
 #include <errno.h>
-#include <getopt.h>
 #include <stdio.h>
-#include <sys/socket.h>
-#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
+#include <string>
 
-static constexpr char VERSION[] = "1.0";
+#include <android-base/file.h>
+#include <android-base/strings.h>
 
-static void show_usage() {
-  fprintf(stderr,
-          "usage: bugreportz [-h | -v]\n"
-          "  -h: to display this help message\n"
-          "  -v: to display the version\n"
-          "  or no arguments to generate a zipped bugreport\n");
+#include "bugreportz.h"
+
+static constexpr char BEGIN_PREFIX[] = "BEGIN:";
+static constexpr char PROGRESS_PREFIX[] = "PROGRESS:";
+
+static void write_line(const std::string& line, bool show_progress) {
+    if (line.empty()) return;
+
+    // When not invoked with the -p option, it must skip BEGIN and PROGRESS lines otherwise it
+    // will break adb (which is expecting either OK or FAIL).
+    if (!show_progress && (android::base::StartsWith(line, PROGRESS_PREFIX) ||
+                           android::base::StartsWith(line, BEGIN_PREFIX)))
+        return;
+
+    android::base::WriteStringToFd(line, STDOUT_FILENO);
 }
 
-static void show_version() {
-  fprintf(stderr, "%s\n", VERSION);
-}
-
-int main(int argc, char *argv[]) {
-
-    if (argc > 1) {
-        /* parse arguments */
-        int c;
-        while ((c = getopt(argc, argv, "vh")) != -1) {
-            switch (c) {
-                case 'h':
-                    show_usage();
-                    return EXIT_SUCCESS;
-                case 'v':
-                    show_version();
-                    return EXIT_SUCCESS;
-                default:
-                    show_usage();
-                    return EXIT_FAILURE;
-            }
-        }
-        // passed an argument not starting with -
-        if (optind > 1 || argv[optind] != nullptr) {
-            show_usage();
-            return EXIT_FAILURE;
-        }
-    }
-
-    // TODO: code below was copy-and-pasted from bugreport.cpp (except by the timeout value);
-    // should be reused instead.
-
-    // Start the dumpstatez service.
-    property_set("ctl.start", "dumpstatez");
-
-    // Socket will not be available until service starts.
-    int s;
-    for (int i = 0; i < 20; i++) {
-        s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
-        if (s >= 0)
-            break;
-        // Try again in 1 second.
-        sleep(1);
-    }
-
-    if (s == -1) {
-        printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
-        return EXIT_SUCCESS;
-    }
-
-    // Set a timeout so that if nothing is read in 10 minutes, we'll stop
-    // reading and quit. No timeout in dumpstate is longer than 60 seconds,
-    // so this gives lots of leeway in case of unforeseen time outs.
-    struct timeval tv;
-    tv.tv_sec = 10 * 60;
-    tv.tv_usec = 0;
-    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
-        fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno));
-    }
-
+int bugreportz(int s, bool show_progress) {
+    std::string line;
     while (1) {
         char buffer[65536];
-        ssize_t bytes_read = TEMP_FAILURE_RETRY(
-                read(s, buffer, sizeof(buffer)));
+        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)));
         if (bytes_read == 0) {
             break;
         } else if (bytes_read == -1) {
@@ -109,21 +58,18 @@
             break;
         }
 
-        ssize_t bytes_to_send = bytes_read;
-        ssize_t bytes_written;
-        do {
-            bytes_written = TEMP_FAILURE_RETRY(
-                    write(STDOUT_FILENO, buffer + bytes_read - bytes_to_send,
-                            bytes_to_send));
-            if (bytes_written == -1) {
-                fprintf(stderr,
-                        "Failed to write data to stdout: read %zd, trying to send %zd (%s)\n",
-                        bytes_read, bytes_to_send, strerror(errno));
-                break;
+        // Writes line by line.
+        for (int i = 0; i < bytes_read; i++) {
+            char c = buffer[i];
+            line.append(1, c);
+            if (c == '\n') {
+                write_line(line, show_progress);
+                line.clear();
             }
-            bytes_to_send -= bytes_written;
-        } while (bytes_written != 0 && bytes_to_send > 0);
+        }
     }
+    // Process final line, in case it didn't finish with newline
+    write_line(line, show_progress);
 
     if (close(s) == -1) {
         fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno));
diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h
new file mode 100644
index 0000000..304e4b3
--- /dev/null
+++ b/cmds/bugreportz/bugreportz.h
@@ -0,0 +1,21 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// 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 BUGREPORTZ_H
+#define BUGREPORTZ_H
+
+// Calls dumpstate using the given socket and output its result to stdout.
+int bugreportz(int s, bool show_progress);
+
+#endif  // BUGREPORTZ_H
diff --git a/cmds/bugreportz/bugreportz_test.cpp b/cmds/bugreportz/bugreportz_test.cpp
new file mode 100644
index 0000000..58b23d1
--- /dev/null
+++ b/cmds/bugreportz/bugreportz_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "bugreportz.h"
+
+using ::testing::StrEq;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStdout;
+
+class BugreportzTest : public ::testing::Test {
+  public:
+    // Creates the pipe used to communicate with bugreportz()
+    void SetUp() {
+        int fds[2];
+        ASSERT_EQ(0, pipe(fds));
+        read_fd_ = fds[0];
+        write_fd_ = fds[1];
+    }
+
+    // Closes the pipe FDs.
+    // If a FD is closed manually during a test, set it to -1 to prevent TearDown() trying to close
+    // it again.
+    void TearDown() {
+        for (int fd : {read_fd_, write_fd_}) {
+            if (fd >= 0) {
+                close(fd);
+            }
+        }
+    }
+
+    // Emulates dumpstate output by writing to the socket passed to bugreportz()
+    void WriteToSocket(const std::string& data) {
+        if (write_fd_ < 0) {
+            ADD_FAILURE() << "cannot write '" << data << "' because socket is already closed";
+            return;
+        }
+        int expected = data.length();
+        int actual = write(write_fd_, data.data(), data.length());
+        ASSERT_EQ(expected, actual) << "wrong number of bytes written to socket";
+    }
+
+    void AssertStdoutEquals(const std::string& expected) {
+        ASSERT_THAT(stdout_, StrEq(expected)) << "wrong stdout output";
+    }
+
+    // Calls bugreportz() using the internal pipe.
+    //
+    // Tests must call WriteToSocket() to set what's written prior to calling it, since the writing
+    // end of the pipe will be closed before calling bugreportz() (otherwise that function would
+    // hang).
+    void Bugreportz(bool show_progress) {
+        close(write_fd_);
+        write_fd_ = -1;
+
+        CaptureStdout();
+        int status = bugreportz(read_fd_, show_progress);
+
+        close(read_fd_);
+        read_fd_ = -1;
+        stdout_ = GetCapturedStdout();
+
+        ASSERT_EQ(0, status) << "bugrepotz() call failed (stdout: " << stdout_ << ")";
+    }
+
+  private:
+    int read_fd_;
+    int write_fd_;
+    std::string stdout_;
+};
+
+// Tests 'bugreportz', without any argument - it will ignore progress lines.
+TEST_F(BugreportzTest, NoArgument) {
+    WriteToSocket("BEGIN:THE IGNORED PATH WARS HAS!\n");  // Should be ommited.
+    WriteToSocket("What happens on 'dumpstate',");
+    WriteToSocket("stays on 'bugreportz'.\n");
+    WriteToSocket("PROGRESS:Y U NO OMITTED?\n");  // Should be ommited.
+    WriteToSocket("But ");
+    WriteToSocket("PROGRESS IN THE MIDDLE");  // Ok - not starting a line.
+    WriteToSocket(" is accepted\n");
+
+    Bugreportz(false);
+
+    AssertStdoutEquals(
+        "What happens on 'dumpstate',stays on 'bugreportz'.\n"
+        "But PROGRESS IN THE MIDDLE is accepted\n");
+}
+
+// Tests 'bugreportz -p' - it will just echo dumpstate's output to stdout
+TEST_F(BugreportzTest, WithProgress) {
+    WriteToSocket("BEGIN:I AM YOUR PATH\n");
+    WriteToSocket("What happens on 'dumpstate',");
+    WriteToSocket("stays on 'bugreportz'.\n");
+    WriteToSocket("PROGRESS:IS INEVITABLE\n");
+    WriteToSocket("PROG");
+    WriteToSocket("RESS:IS NOT AUTOMATIC\n");
+    WriteToSocket("Newline is optional");
+
+    Bugreportz(true);
+
+    AssertStdoutEquals(
+        "BEGIN:I AM YOUR PATH\n"
+        "What happens on 'dumpstate',stays on 'bugreportz'.\n"
+        "PROGRESS:IS INEVITABLE\n"
+        "PROGRESS:IS NOT AUTOMATIC\n"
+        "Newline is optional");
+}
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp
new file mode 100644
index 0000000..a3ae1ff
--- /dev/null
+++ b/cmds/bugreportz/main.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#include "bugreportz.h"
+
+static constexpr char VERSION[] = "1.1";
+
+static void show_usage() {
+    fprintf(stderr,
+            "usage: bugreportz [-h | -v]\n"
+            "  -h: to display this help message\n"
+            "  -p: display progress\n"
+            "  -v: to display the version\n"
+            "  or no arguments to generate a zipped bugreport\n");
+}
+
+static void show_version() {
+    fprintf(stderr, "%s\n", VERSION);
+}
+
+int main(int argc, char* argv[]) {
+    bool show_progress = false;
+    if (argc > 1) {
+        /* parse arguments */
+        int c;
+        while ((c = getopt(argc, argv, "hpv")) != -1) {
+            switch (c) {
+                case 'h':
+                    show_usage();
+                    return EXIT_SUCCESS;
+                case 'p':
+                    show_progress = true;
+                    break;
+                case 'v':
+                    show_version();
+                    return EXIT_SUCCESS;
+                default:
+                    show_usage();
+                    return EXIT_FAILURE;
+            }
+        }
+    }
+
+    // TODO: code below was copy-and-pasted from bugreport.cpp (except by the
+    // timeout value);
+    // should be reused instead.
+
+    // Start the dumpstatez service.
+    property_set("ctl.start", "dumpstatez");
+
+    // Socket will not be available until service starts.
+    int s;
+    for (int i = 0; i < 20; i++) {
+        s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
+        if (s >= 0) break;
+        // Try again in 1 second.
+        sleep(1);
+    }
+
+    if (s == -1) {
+        printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno));
+        return EXIT_SUCCESS;
+    }
+
+    // Set a timeout so that if nothing is read in 10 minutes, we'll stop
+    // reading and quit. No timeout in dumpstate is longer than 60 seconds,
+    // so this gives lots of leeway in case of unforeseen time outs.
+    struct timeval tv;
+    tv.tv_sec = 10 * 60;
+    tv.tv_usec = 0;
+    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
+        fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno));
+    }
+
+    bugreportz(s, show_progress);
+}
diff --git a/cmds/bugreportz/readme.md b/cmds/bugreportz/readme.md
index 85aafce..2697f09 100644
--- a/cmds/bugreportz/readme.md
+++ b/cmds/bugreportz/readme.md
@@ -3,6 +3,13 @@
 `bugreportz` is used to generate a zippped bugreport whose path is passed back to `adb`, using
 the simple protocol defined below.
 
+# Version 1.1
+On version 1.1, in addition to the `OK` and `FAILURE` lines, when `bugreportz` is invoked with
+`-p`, it outputs the following lines:
+
+- `BEGIN:<path_to_bugreport_file>` right away.
+- `PROGRESS:<progress>/<total>` as `dumpstate` progresses (where `<progress>` is the current
+progress units out of a max of `<total>`).
 
 ## Version 1.0
 On version 1.0, `bugreportz` does not generate any output on `stdout` until the bugreport is
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 0433f31..e478651 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_MODULE := dumpstate
 
-LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase
+LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux libbase libhardware_legacy
 # ZipArchive support, the order matters here to get all symbols.
 LOCAL_STATIC_LIBRARIES := libziparchive libz libcrypto_static
 LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 6c615eb..5b01be1 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -23,6 +23,7 @@
 #include <memory>
 #include <regex>
 #include <set>
+#include <signal.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,7 +38,9 @@
 
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
+#include <android-base/file.h>
 #include <cutils/properties.h>
+#include <hardware_legacy/power.h>
 
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
@@ -53,14 +56,14 @@
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
 
-// TODO: should be part of dumpstate object
+// TODO: variables below should be part of dumpstate object
 static unsigned long id;
 static char build_type[PROPERTY_VALUE_MAX];
 static time_t now;
 static std::unique_ptr<ZipWriter> zip_writer;
 static std::set<std::string> mount_points;
 void add_mountinfo();
-static int control_socket_fd;
+int control_socket_fd = -1;
 /* suffix of the bugreport files - it's typically the date (when invoked with -d),
  * although it could be changed by the user using a system property */
 static std::string suffix;
@@ -68,16 +71,19 @@
 #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
 #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
 
-#define RAFT_DIR "/data/misc/raft/"
+#define RAFT_DIR "/data/misc/raft"
 #define RECOVERY_DIR "/cache/recovery"
 #define RECOVERY_DATA_DIR "/data/misc/recovery"
 #define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
+#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
 #define TOMBSTONE_DIR "/data/tombstones"
 #define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
 /* Can accomodate a tombstone number up to 9999. */
 #define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
 #define NUM_TOMBSTONES  10
 #define WLUTIL "/vendor/xbin/wlutil"
+#define WAKE_LOCK_NAME "dumpstate_wakelock"
 
 typedef struct {
   char name[TOMBSTONE_MAX_LEN];
@@ -176,6 +182,136 @@
     closedir(d);
 }
 
+// return pid of a userspace process. If not found or error, return 0.
+static unsigned int pid_of_process(const char* ps_name) {
+    DIR *proc_dir;
+    struct dirent *ps;
+    unsigned int pid;
+    std::string cmdline;
+
+    if (!(proc_dir = opendir("/proc"))) {
+        MYLOGE("Can't open /proc\n");
+        return 0;
+    }
+
+    while ((ps = readdir(proc_dir))) {
+        if (!(pid = atoi(ps->d_name))) {
+            continue;
+        }
+        android::base::ReadFileToString("/proc/"
+                + std::string(ps->d_name) + "/cmdline", &cmdline);
+        if (cmdline.find(ps_name) == std::string::npos) {
+            continue;
+        } else {
+            closedir(proc_dir);
+            return pid;
+        }
+    }
+    closedir(proc_dir);
+    return 0;
+}
+
+// dump anrd's trace and add to the zip file.
+// 1. check if anrd is running on this device.
+// 2. send a SIGUSR1 to its pid which will dump anrd's trace.
+// 3. wait until the trace generation completes and add to the zip file.
+static bool dump_anrd_trace() {
+    unsigned int pid;
+    char buf[50], path[PATH_MAX];
+    struct dirent *trace;
+    struct stat st;
+    DIR *trace_dir;
+    int retry = 5;
+    long max_ctime = 0, old_mtime;
+    long long cur_size = 0;
+    const char *trace_path = "/data/misc/anrd/";
+
+    if (!zip_writer) {
+        MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+        return false;
+    }
+
+    // find anrd's pid if it is running.
+    pid = pid_of_process("/system/xbin/anrd");
+
+    if (pid > 0) {
+        if (stat(trace_path, &st) == 0) {
+            old_mtime = st.st_mtime;
+        } else {
+            MYLOGE("Failed to find: %s\n", trace_path);
+            return false;
+        }
+
+        // send SIGUSR1 to the anrd to generate a trace.
+        sprintf(buf, "%u", pid);
+        if (run_command("ANRD_DUMP", 1, "kill", "-SIGUSR1", buf, NULL)) {
+            MYLOGE("anrd signal timed out. Please manually collect trace\n");
+            return false;
+        }
+
+        while (retry-- > 0 && old_mtime == st.st_mtime) {
+            sleep(1);
+            stat(trace_path, &st);
+        }
+
+        if (retry < 0 && old_mtime == st.st_mtime) {
+            MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path);
+            return false;
+        }
+
+        // identify the trace file by its creation time.
+        if (!(trace_dir = opendir(trace_path))) {
+            MYLOGE("Can't open trace file under %s\n", trace_path);
+        }
+        while ((trace = readdir(trace_dir))) {
+            if (strcmp(trace->d_name, ".") == 0
+                    || strcmp(trace->d_name, "..") == 0) {
+                continue;
+            }
+            sprintf(path, "%s%s", trace_path, trace->d_name);
+            if (stat(path, &st) == 0) {
+                if (st.st_ctime > max_ctime) {
+                    max_ctime = st.st_ctime;
+                    sprintf(buf, "%s", trace->d_name);
+                }
+            }
+        }
+        closedir(trace_dir);
+
+        // Wait until the dump completes by checking the size of the trace.
+        if (max_ctime > 0) {
+            sprintf(path, "%s%s", trace_path, buf);
+            while(true) {
+                sleep(1);
+                if (stat(path, &st) == 0) {
+                    if (st.st_size == cur_size) {
+                        break;
+                    } else if (st.st_size > cur_size) {
+                        cur_size = st.st_size;
+                    } else {
+                        return false;
+                    }
+                } else {
+                    MYLOGE("Cant stat() %s anymore\n", path);
+                    return false;
+                }
+            }
+            // Add to the zip file.
+            if (!add_zip_entry("anrd_trace.txt", path)) {
+                MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
+            } else {
+                if (remove(path)) {
+                    MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno));
+                }
+                return true;
+            }
+        } else {
+            MYLOGE("Can't stats any trace file under %s\n", trace_path);
+        }
+    }
+    return false;
+}
+
 static void dump_systrace() {
     if (!zip_writer) {
         MYLOGD("Not dumping systrace because zip_writer is not set\n");
@@ -217,6 +353,40 @@
     }
 }
 
+static void dump_raft() {
+    if (is_user_build()) {
+        return;
+    }
+
+    std::string raft_log_path = bugreport_dir + "/raft_log.txt";
+    if (raft_log_path.empty()) {
+        MYLOGD("raft_log_path is empty\n");
+        return;
+    }
+
+    struct stat s;
+    if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) {
+        MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR);
+        return;
+    }
+
+    if (!zip_writer) {
+        // Write compressed and encoded raft logs to stdout if not zip_writer.
+        run_command("RAFT LOGS", 600, "logcompressor", "-r", RAFT_DIR, NULL);
+        return;
+    }
+
+    run_command("RAFT LOGS", 600, "logcompressor", "-n", "-r", RAFT_DIR,
+            "-o", raft_log_path.c_str(), NULL);
+    if (!add_zip_entry("raft_log.txt", raft_log_path)) {
+        MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+    } else {
+        if (remove(raft_log_path.c_str())) {
+            MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+        }
+    }
+}
+
 static bool skip_not_stat(const char *path) {
     static const char stat[] = "/stat";
     size_t len = strlen(path);
@@ -417,7 +587,7 @@
     property_get("ro.build.display.id", build, "(unknown)");
     property_get("ro.build.fingerprint", fingerprint, "(unknown)");
     property_get("ro.build.type", build_type, "(unknown)");
-    property_get("ro.baseband", radio, "(unknown)");
+    property_get("gsm.version.baseband", radio, "(unknown)");
     property_get("ro.bootloader", bootloader, "(unknown)");
     property_get("gsm.operator.alpha", network, "(unknown)");
     strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
@@ -441,18 +611,39 @@
     printf("\n");
 }
 
+// List of file extensions that can cause a zip file attachment to be rejected by some email
+// service providers.
+static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
+      ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp",
+      ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct",
+      ".shb", ".sys", ".vb",  ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
+};
+
 bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
     if (!zip_writer) {
         MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
                 entry_name.c_str());
         return false;
     }
+    std::string valid_name = entry_name;
+
+    // Rename extension if necessary.
+    size_t idx = entry_name.rfind(".");
+    if (idx != std::string::npos) {
+        std::string extension = entry_name.substr(idx);
+        std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+        if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) {
+            valid_name = entry_name + ".renamed";
+            MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str());
+        }
+    }
+
     // Logging statement  below is useful to time how long each entry takes, but it's too verbose.
     // MYLOGD("Adding zip entry %s\n", entry_name.c_str());
-    int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(),
+    int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
             ZipWriter::kCompress, get_mtime(fd, now));
     if (err) {
-        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
                 ZipWriter::ErrorCodeString(err));
         return false;
     }
@@ -498,6 +689,7 @@
     return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
 }
 
+// TODO: move to util.cpp
 void add_dir(const char *dir, bool recursive) {
     if (!zip_writer) {
         MYLOGD("Not adding dir %s because zip_writer is not set\n", dir);
@@ -541,10 +733,12 @@
 static void dump_iptables() {
     run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
     run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
-    run_command("IPTABLE NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
+    run_command("IPTABLES NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
     /* no ip6 nat */
-    run_command("IPTABLE RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
-    run_command("IP6TABLE RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
+    run_command("IPTABLES MANGLE", 10, "iptables", "-t", "mangle", "-L", "-nvx", NULL);
+    run_command("IP6TABLES MANGLE", 10, "ip6tables", "-t", "mangle", "-L", "-nvx", NULL);
+    run_command("IPTABLES RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
+    run_command("IP6TABLES RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
 }
 
 static void dumpstate(const std::string& screenshot_path, const std::string& version) {
@@ -576,7 +770,7 @@
     run_command("LIBRANK", 10, SU_PATH, "root", "librank", NULL);
 
     run_command("PRINTENV", 10, "printenv", NULL);
-    run_command("NETSTAT", 10, "netstat", "-n", NULL);
+    run_command("NETSTAT", 10, "netstat", "-nW", NULL);
     run_command("LSMOD", 10, "lsmod", NULL);
 
     do_dmesg();
@@ -626,8 +820,6 @@
 
     run_command("LOG STATISTICS", 10, "logcat", "-b", "all", "-S", NULL);
 
-    run_command("RAFT LOGS", 600, SU_PATH, "root", "logcompressor", "-r", RAFT_DIR, NULL);
-
     /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != NULL) {
         dump_file("VM TRACES JUST NOW", dump_traces_path);
@@ -841,7 +1033,7 @@
     printf("== Running Application Providers\n");
     printf("========================================================\n");
 
-    run_command("APP SERVICES", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
+    run_command("APP PROVIDERS", 30, "dumpsys", "-t", "30", "activity", "provider", "all", NULL);
 
 
     printf("========================================================\n");
@@ -875,11 +1067,31 @@
           VERSION_DEFAULT.c_str());
 }
 
-static void sigpipe_handler(int n) {
-    // don't complain to stderr or stdout
+static void wake_lock_releaser() {
+    if (release_wake_lock(WAKE_LOCK_NAME) < 0) {
+        MYLOGE("Failed to release wake lock: %s \n", strerror(errno));
+    } else {
+        MYLOGD("Wake lock released.\n");
+    }
+}
+
+static void sig_handler(int signo) {
+    wake_lock_releaser();
     _exit(EXIT_FAILURE);
 }
 
+static void register_sig_handler() {
+    struct sigaction sa;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    sa.sa_handler = sig_handler;
+    sigaction(SIGPIPE, &sa, NULL); // broken pipe
+    sigaction(SIGSEGV, &sa, NULL); // segment fault
+    sigaction(SIGINT, &sa, NULL); // ctrl-c
+    sigaction(SIGTERM, &sa, NULL); // killed
+    sigaction(SIGQUIT, &sa, NULL); // quit
+}
+
 /* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
    temporary file.
  */
@@ -948,7 +1160,6 @@
 }
 
 int main(int argc, char *argv[]) {
-    struct sigaction sigact;
     int do_add_date = 0;
     int do_zip_file = 0;
     int do_vibrate = 1;
@@ -965,6 +1176,14 @@
 
     MYLOGI("begin\n");
 
+    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
+        MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
+    } else {
+        MYLOGD("Wake lock acquired.\n");
+        atexit(wake_lock_releaser);
+        register_sig_handler();
+    }
+
     /* gets the sequential id */
     char last_id[PROPERTY_VALUE_MAX];
     property_get("dumpstate.last_id", last_id, "0");
@@ -973,17 +1192,20 @@
     property_set("dumpstate.last_id", last_id);
     MYLOGI("dumpstate id: %lu\n", id);
 
-    /* clear SIGPIPE handler */
-    memset(&sigact, 0, sizeof(sigact));
-    sigact.sa_handler = sigpipe_handler;
-    sigaction(SIGPIPE, &sigact, NULL);
-
     /* set as high priority, and protect from OOM killer */
     setpriority(PRIO_PROCESS, 0, -20);
-    FILE *oom_adj = fopen("/proc/self/oom_adj", "we");
+
+    FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
     if (oom_adj) {
-        fputs("-17", oom_adj);
+        fputs("-1000", oom_adj);
         fclose(oom_adj);
+    } else {
+        /* fallback to kernels <= 2.6.35 */
+        oom_adj = fopen("/proc/self/oom_adj", "we");
+        if (oom_adj) {
+            fputs("-17", oom_adj);
+            fclose(oom_adj);
+        }
     }
 
     /* parse arguments */
@@ -1050,6 +1272,7 @@
     if (use_control_socket) {
         MYLOGD("Opening control socket\n");
         control_socket_fd = open_socket("dumpstate");
+        do_update_progress = 1;
     }
 
     /* full path of the temporary file containing the bugreport */
@@ -1123,14 +1346,21 @@
         }
 
         if (do_update_progress) {
-            std::vector<std::string> am_args = {
-                 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
-                 "--es", "android.intent.extra.NAME", suffix,
-                 "--ei", "android.intent.extra.ID", std::to_string(id),
-                 "--ei", "android.intent.extra.PID", std::to_string(getpid()),
-                 "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
-            };
-            send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+            if (do_broadcast) {
+                // clang-format off
+                std::vector<std::string> am_args = {
+                     "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
+                     "--es", "android.intent.extra.NAME", suffix,
+                     "--ei", "android.intent.extra.ID", std::to_string(id),
+                     "--ei", "android.intent.extra.PID", std::to_string(getpid()),
+                     "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
+                };
+                // clang-format on
+                send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
+            }
+            if (use_control_socket) {
+                dprintf(control_socket_fd, "BEGIN:%s\n", path.c_str());
+            }
         }
     }
 
@@ -1192,7 +1422,14 @@
     print_header(version);
 
     // Dumps systrace right away, otherwise it will be filled with unnecessary events.
-    dump_systrace();
+    // First try to dump anrd trace if the daemon is running. Otherwise, dump
+    // the raw trace.
+    if (!dump_anrd_trace()) {
+        dump_systrace();
+    }
+
+    // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
+    dump_raft();
 
     // Invoking the following dumpsys calls before dump_traces() to try and
     // keep the system stats as close to its initial state as possible.
@@ -1207,9 +1444,19 @@
     add_dir(RECOVERY_DIR, true);
     add_dir(RECOVERY_DATA_DIR, true);
     add_dir(LOGPERSIST_DATA_DIR, false);
+    if (!is_user_build()) {
+        add_dir(PROFILE_DATA_DIR_CUR, true);
+        add_dir(PROFILE_DATA_DIR_REF, true);
+    }
     add_mountinfo();
     dump_iptables();
 
+    // Capture any IPSec policies in play.  No keys are exposed here.
+    run_command("IP XFRM POLICY", 10, "ip", "xfrm", "policy", nullptr);
+
+    // Run ss as root so we can see socket marks.
+    run_command("DETAILED SOCKET STATE", 10, "ss", "-eionptu", NULL);
+
     if (!drop_root_user()) {
         return -1;
     }
@@ -1306,6 +1553,7 @@
     if (do_broadcast) {
         if (!path.empty()) {
             MYLOGI("Final bugreport path: %s\n", path.c_str());
+            // clang-format off
             std::vector<std::string> am_args = {
                  "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
                  "--ei", "android.intent.extra.ID", std::to_string(id),
@@ -1314,6 +1562,7 @@
                  "--es", "android.intent.extra.BUGREPORT", path,
                  "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
             };
+            // clang-format on
             if (do_fb) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.SCREENSHOT");
@@ -1339,9 +1588,9 @@
         fclose(stderr);
     }
 
-    if (use_control_socket && control_socket_fd >= 0) {
-        MYLOGD("Closing control socket\n");
-        close(control_socket_fd);
+    if (use_control_socket && control_socket_fd != -1) {
+      MYLOGD("Closing control socket\n");
+      close(control_socket_fd);
     }
 
     return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 4dde125..905fc22 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -79,7 +79,7 @@
  * It would be better to take advantage of the C++ migration and encapsulate the state in an object,
  * but that will be better handled in a major C++ refactoring, which would also get rid of other C
  * idioms (like using std::string instead of char*, removing varargs, etc...) */
-extern int do_update_progress, progress, weight_total;
+extern int do_update_progress, progress, weight_total, control_socket_fd;
 
 /* full path of the directory where the bugreport files will be written */
 extern std::string bugreport_dir;
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 1f56d21..3448e91 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -38,3 +38,11 @@
     class main
     disabled
     oneshot
+
+# bugreportwear is a wearable version of bugreport that displays progress and takes early
+# screenshot.
+service bugreportwear /system/bin/dumpstate -d -B -P -p -z \
+        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
+    class main
+    disabled
+    oneshot
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 5527b24..afb52e7 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "dumpstate"
+
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -23,26 +25,25 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string>
 #include <string.h>
 #include <sys/capability.h>
 #include <sys/inotify.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/wait.h>
-#include <sys/klog.h>
 #include <time.h>
 #include <unistd.h>
-#include <vector>
-#include <sys/prctl.h>
 
-#define LOG_TAG "dumpstate"
+#include <string>
+#include <vector>
 
 #include <android-base/file.h>
 #include <cutils/debugger.h>
-#include <cutils/log.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
 #include <selinux/android.h>
@@ -828,7 +829,7 @@
     }
 
     gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
-            AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC,
+            AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC, AID_WAKELOCK,
             AID_BLUETOOTH };
     if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
         MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
@@ -850,8 +851,10 @@
     capheader.version = _LINUX_CAPABILITY_VERSION_3;
     capheader.pid = 0;
 
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
-    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted =
+            (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
+    capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective =
+            (CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_BLOCK_SUSPEND));
     capdata[0].inheritable = 0;
     capdata[1].inheritable = 0;
 
@@ -1215,6 +1218,11 @@
         fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total);
     }
 
+    if (control_socket_fd >= 0) {
+        dprintf(control_socket_fd, "PROGRESS:%d/%d\n", progress, weight_total);
+        fsync(control_socket_fd);
+    }
+
     int status = property_set(key, value);
     if (status) {
         MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
diff --git a/cmds/dumpsys/.clang-format b/cmds/dumpsys/.clang-format
new file mode 100644
index 0000000..fc4eb1b
--- /dev/null
+++ b/cmds/dumpsys/.clang-format
@@ -0,0 +1,13 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+AccessModifierOffset: -2
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp
index 38442e7..3476964 100644
--- a/cmds/dumpsys/Android.bp
+++ b/cmds/dumpsys/Android.bp
@@ -1,7 +1,14 @@
-cc_binary {
-    name: "dumpsys",
+cc_defaults {
+    name: "dumpsys_defaults",
 
-    srcs: ["dumpsys.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    srcs: [
+        "dumpsys.cpp",
+    ],
 
     shared_libs: [
         "libbase",
@@ -10,6 +17,34 @@
         "libbinder",
     ],
 
-    cflags: ["-DXP_UNIX"],
-    //shared_libs: ["librt"],
+    clang: true,
 }
+
+//
+// Static library used in testing and executable
+//
+
+cc_library_static {
+    name: "libdumpsys",
+
+    defaults: ["dumpsys_defaults"],
+
+    export_include_dirs: ["."],
+}
+
+
+//
+// Executable
+//
+
+cc_binary {
+    name: "dumpsys",
+
+    defaults: ["dumpsys_defaults"],
+
+    srcs: [
+        "main.cpp",
+    ],
+}
+
+subdirs = ["tests"]
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 0601c87..f0e7200 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -1,17 +1,26 @@
 /*
- * Command that dumps interesting system state to the log.
+ * Copyright (C) 2009 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.
  */
 
-#define LOG_TAG "dumpsys"
-
 #include <algorithm>
 #include <chrono>
 #include <thread>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <binder/IServiceManager.h>
 #include <binder/Parcel.h>
 #include <binder/ProcessState.h>
 #include <binder/TextOutput.h>
@@ -29,7 +38,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "dumpsys.h"
+
 using namespace android;
+using android::base::StringPrintf;
 using android::base::unique_fd;
 using android::base::WriteFully;
 
@@ -51,7 +63,7 @@
             "         SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
 }
 
-bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
+static bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
     for (const auto& candidate : skipped) {
         if (candidate == service) {
             return true;
@@ -60,17 +72,7 @@
     return false;
 }
 
-int main(int argc, char* const argv[])
-{
-    signal(SIGPIPE, SIG_IGN);
-    sp<IServiceManager> sm = defaultServiceManager();
-    fflush(stdout);
-    if (sm == NULL) {
-        ALOGE("Unable to get default service manager!");
-        aerr << "dumpsys: Unable to get default service manager!" << endl;
-        return 20;
-    }
-
+int Dumpsys::main(int argc, char* const argv[]) {
     Vector<String16> services;
     Vector<String16> args;
     Vector<String16> skippedServices;
@@ -83,6 +85,9 @@
         {     0,           0, 0,  0 }
     };
 
+    // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
+    // happens on test cases).
+    optind = 1;
     while (1) {
         int c;
         int optionIndex = 0;
@@ -145,7 +150,7 @@
 
     if (services.empty() || showListOnly) {
         // gets all services
-        services = sm->listServices();
+        services = sm_->listServices();
         services.sort(sort_func);
         args.add(String16("-a"));
     }
@@ -157,8 +162,9 @@
         aout << "Currently running services:" << endl;
 
         for (size_t i=0; i<N; i++) {
-            sp<IBinder> service = sm->checkService(services[i]);
-            if (service != NULL) {
+            sp<IBinder> service = sm_->checkService(services[i]);
+
+            if (service != nullptr) {
                 bool skipped = IsSkipped(skippedServices, services[i]);
                 aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
             }
@@ -173,8 +179,8 @@
         String16 service_name = std::move(services[i]);
         if (IsSkipped(skippedServices, service_name)) continue;
 
-        sp<IBinder> service = sm->checkService(service_name);
-        if (service != NULL) {
+        sp<IBinder> service = sm_->checkService(service_name);
+        if (service != nullptr) {
             int sfd[2];
 
             if (pipe(sfd) != 0) {
@@ -210,7 +216,8 @@
             });
 
             auto timeout = std::chrono::seconds(timeoutArg);
-            auto end = std::chrono::steady_clock::now() + timeout;
+            auto start = std::chrono::steady_clock::now();
+            auto end = start + timeout;
 
             struct pollfd pfd = {
                 .fd = local_end.get(),
@@ -259,7 +266,10 @@
             }
 
             if (timed_out) {
-                aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
+                aout << endl
+                     << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArg
+                     << "s) EXPIRED ***" << endl
+                     << endl;
             }
 
             if (timed_out || error) {
@@ -267,6 +277,13 @@
             } else {
                 dump_thread.join();
             }
+
+            if (N > 1) {
+              std::chrono::duration<double> elapsed_seconds =
+                  std::chrono::steady_clock::now() - start;
+              aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
+                   << "was the duration of dumpsys " << service_name << endl;
+            }
         } else {
             aerr << "Can't find service: " << service_name << endl;
         }
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
new file mode 100644
index 0000000..2534dde
--- /dev/null
+++ b/cmds/dumpsys/dumpsys.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+class Dumpsys {
+  public:
+    Dumpsys(android::IServiceManager* sm) : sm_(sm) {
+    }
+    int main(int argc, char* const argv[]);
+
+  private:
+    android::IServiceManager* sm_;
+};
+}
+
+#endif  // FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp
new file mode 100644
index 0000000..8ba0eba
--- /dev/null
+++ b/cmds/dumpsys/main.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/*
+ * Command that dumps interesting system state to the log.
+ */
+
+#include "dumpsys.h"
+
+#include <binder/IServiceManager.h>
+#include <binder/TextOutput.h>
+
+#include <signal.h>
+#include <stdio.h>
+
+using namespace android;
+
+int main(int argc, char* const argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+    sp<IServiceManager> sm = defaultServiceManager();
+    fflush(stdout);
+    if (sm == nullptr) {
+        ALOGE("Unable to get default service manager!");
+        aerr << "dumpsys: Unable to get default service manager!" << endl;
+        return 20;
+    }
+
+    Dumpsys dumpsys(sm.get());
+    return dumpsys.main(argc, argv);
+}
diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp
new file mode 100644
index 0000000..7698ed5
--- /dev/null
+++ b/cmds/dumpsys/tests/Android.bp
@@ -0,0 +1,19 @@
+// Build the unit tests for dumpsys
+cc_test {
+    name: "dumpsys_test",
+
+    srcs: ["dumpsys_test.cpp"],
+
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libdumpsys",
+        "libgmock",
+    ],
+
+    clang: true,
+}
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
new file mode 100644
index 0000000..a61cb00
--- /dev/null
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../dumpsys.h"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::DoAll;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::MakeAction;
+using ::testing::Not;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::WithArg;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class ServiceManagerMock : public IServiceManager {
+  public:
+    MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&));
+    MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&));
+    MOCK_METHOD3(addService, status_t(const String16&, const sp<IBinder>&, bool));
+    MOCK_METHOD0(listServices, Vector<String16>());
+
+  protected:
+    MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
+class BinderMock : public BBinder {
+  public:
+    BinderMock() {
+    }
+
+    MOCK_METHOD2(dump, status_t(int, const Vector<String16>&));
+};
+
+// gmock black magic to provide a WithArg<0>(WriteOnFd(output)) matcher
+typedef void WriteOnFdFunction(int);
+
+class WriteOnFdAction : public ActionInterface<WriteOnFdFunction> {
+  public:
+    explicit WriteOnFdAction(const std::string& output) : output_(output) {
+    }
+    virtual Result Perform(const ArgumentTuple& args) {
+        int fd = ::std::tr1::get<0>(args);
+        android::base::WriteStringToFd(output_, fd);
+    }
+
+  private:
+    std::string output_;
+};
+
+// Matcher used to emulate dump() by writing on its file descriptor.
+Action<WriteOnFdFunction> WriteOnFd(const std::string& output) {
+    return MakeAction(new WriteOnFdAction(output));
+}
+
+// Matcher for args using Android's Vector<String16> format
+// TODO: move it to some common testing library
+MATCHER_P(AndroidElementsAre, expected, "") {
+    std::ostringstream errors;
+    if (arg.size() != expected.size()) {
+        errors << " sizes do not match (expected " << expected.size() << ", got " << arg.size()
+               << ")\n";
+    }
+    int i = 0;
+    std::ostringstream actual_stream, expected_stream;
+    for (String16 actual : arg) {
+        std::string actual_str = String16::std_string(actual);
+        std::string expected_str = expected[i];
+        actual_stream << "'" << actual_str << "' ";
+        expected_stream << "'" << expected_str << "' ";
+        if (actual_str != expected_str) {
+            errors << " element mismatch at index " << i << "\n";
+        }
+        i++;
+    }
+
+    if (!errors.str().empty()) {
+        errors << "\nExpected args: " << expected_stream.str()
+               << "\nActual args: " << actual_stream.str();
+        *result_listener << errors.str();
+        return false;
+    }
+    return true;
+}
+
+// Custom action to sleep for timeout seconds
+ACTION_P(Sleep, timeout) {
+    sleep(timeout);
+}
+
+class DumpsysTest : public Test {
+  public:
+    DumpsysTest() : sm_(), dump_(&sm_), stdout_(), stderr_() {
+    }
+
+    void ExpectListServices(std::vector<std::string> services) {
+        Vector<String16> services16;
+        for (auto& service : services) {
+            services16.add(String16(service.c_str()));
+        }
+        EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16));
+    }
+
+    sp<BinderMock> ExpectCheckService(const char* name, bool running = true) {
+        sp<BinderMock> binder_mock;
+        if (running) {
+            binder_mock = new BinderMock;
+        }
+        EXPECT_CALL(sm_, checkService(String16(name))).WillRepeatedly(Return(binder_mock));
+        return binder_mock;
+    }
+
+    void ExpectDump(const char* name, const std::string& output) {
+        sp<BinderMock> binder_mock = ExpectCheckService(name);
+        EXPECT_CALL(*binder_mock, dump(_, _))
+            .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
+    }
+
+    void ExpectDumpWithArgs(const char* name, std::vector<std::string> args,
+                            const std::string& output) {
+        sp<BinderMock> binder_mock = ExpectCheckService(name);
+        EXPECT_CALL(*binder_mock, dump(_, AndroidElementsAre(args)))
+            .WillRepeatedly(DoAll(WithArg<0>(WriteOnFd(output)), Return(0)));
+    }
+
+    void ExpectDumpAndHang(const char* name, int timeout_s, const std::string& output) {
+        sp<BinderMock> binder_mock = ExpectCheckService(name);
+        EXPECT_CALL(*binder_mock, dump(_, _))
+            .WillRepeatedly(DoAll(Sleep(timeout_s), WithArg<0>(WriteOnFd(output)), Return(0)));
+    }
+
+    void CallMain(const std::vector<std::string>& args) {
+        const char* argv[1024] = {"/some/virtual/dir/dumpsys"};
+        int argc = (int)args.size() + 1;
+        int i = 1;
+        for (const std::string& arg : args) {
+            argv[i++] = arg.c_str();
+        }
+        CaptureStdout();
+        CaptureStderr();
+        int status = dump_.main(argc, const_cast<char**>(argv));
+        stdout_ = GetCapturedStdout();
+        stderr_ = GetCapturedStderr();
+        EXPECT_THAT(status, Eq(0));
+    }
+
+    void AssertRunningServices(const std::vector<std::string>& services) {
+        std::string expected("Currently running services:\n");
+        for (const std::string& service : services) {
+            expected.append("  ").append(service).append("\n");
+        }
+        EXPECT_THAT(stdout_, HasSubstr(expected));
+    }
+
+    void AssertOutput(const std::string& expected) {
+        EXPECT_THAT(stdout_, StrEq(expected));
+    }
+
+    void AssertOutputContains(const std::string& expected) {
+        EXPECT_THAT(stdout_, HasSubstr(expected));
+    }
+
+    void AssertDumped(const std::string& service, const std::string& dump) {
+        EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
+    }
+
+    void AssertNotDumped(const std::string& dump) {
+        EXPECT_THAT(stdout_, Not(HasSubstr(dump)));
+    }
+
+    void AssertStopped(const std::string& service) {
+        EXPECT_THAT(stderr_, HasSubstr("Can't find service: " + service + "\n"));
+    }
+
+    ServiceManagerMock sm_;
+    Dumpsys dump_;
+
+  private:
+    std::string stdout_, stderr_;
+};
+
+// Tests 'dumpsys -l' when all services are running
+TEST_F(DumpsysTest, ListAllServices) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet");
+
+    CallMain({"-l"});
+
+    AssertRunningServices({"Locksmith", "Valet"});
+}
+
+// Tests 'dumpsys -l' when a service is not running
+TEST_F(DumpsysTest, ListRunningServices) {
+    ExpectListServices({"Locksmith", "Valet"});
+    ExpectCheckService("Locksmith");
+    ExpectCheckService("Valet", false);
+
+    CallMain({"-l"});
+
+    AssertRunningServices({"Locksmith"});
+    AssertNotDumped({"Valet"});
+}
+
+// Tests 'dumpsys service_name' on a service is running
+TEST_F(DumpsysTest, DumpRunningService) {
+    ExpectDump("Valet", "Here's your car");
+
+    CallMain({"Valet"});
+
+    AssertOutput("Here's your car");
+}
+
+// Tests 'dumpsys -t 1 service_name' on a service that times out after 2s
+TEST_F(DumpsysTest, DumpRunningServiceTimeout) {
+    ExpectDumpAndHang("Valet", 2, "Here's your car");
+
+    CallMain({"-t", "1", "Valet"});
+
+    AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED");
+    AssertNotDumped("Here's your car");
+
+    // Must wait so binder mock is deleted, otherwise test will fail with a leaked object
+    sleep(1);
+}
+
+// Tests 'dumpsys service_name Y U NO HAVE ARGS' on a service that is running
+TEST_F(DumpsysTest, DumpWithArgsRunningService) {
+    ExpectDumpWithArgs("SERVICE", {"Y", "U", "NO", "HANDLE", "ARGS"}, "I DO!");
+
+    CallMain({"SERVICE", "Y", "U", "NO", "HANDLE", "ARGS"});
+
+    AssertOutput("I DO!");
+}
+
+// Tests 'dumpsys' with no arguments
+TEST_F(DumpsysTest, DumpMultipleServices) {
+    ExpectListServices({"running1", "stopped2", "running3"});
+    ExpectDump("running1", "dump1");
+    ExpectCheckService("stopped2", false);
+    ExpectDump("running3", "dump3");
+
+    CallMain({});
+
+    AssertRunningServices({"running1", "running3"});
+    AssertDumped("running1", "dump1");
+    AssertStopped("stopped2");
+    AssertDumped("running3", "dump3");
+}
+
+// Tests 'dumpsys --skip skipped3 skipped5', which should skip these services
+TEST_F(DumpsysTest, DumpWithSkip) {
+    ExpectListServices({"running1", "stopped2", "skipped3", "running4", "skipped5"});
+    ExpectDump("running1", "dump1");
+    ExpectCheckService("stopped2", false);
+    ExpectDump("skipped3", "dump3");
+    ExpectDump("running4", "dump4");
+    ExpectDump("skipped5", "dump5");
+
+    CallMain({"--skip", "skipped3", "skipped5"});
+
+    AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"});
+    AssertDumped("running1", "dump1");
+    AssertDumped("running4", "dump4");
+    AssertStopped("stopped2");
+    AssertNotDumped("dump3");
+    AssertNotDumped("dump5");
+}
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index ddf3aa8..5c04f6c 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -365,6 +365,7 @@
     if (!result) {
         fprintf(stderr, "Shader source:\n");
         printShaderSource(lines);
+        delete[] src;
         return false;
     }
     delete[] src;
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index e3048c7..93174bf 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -6,16 +6,20 @@
         "-Werror",
     ],
     srcs: [
-        "commands.cpp",
+        "InstalldNativeService.cpp",
+        "dexopt.cpp",
         "globals.cpp",
         "utils.cpp",
+        "binder/android/os/IInstalld.aidl",
     ],
     shared_libs: [
         "libbase",
+        "libbinder",
         "libcutils",
         "liblog",
         "liblogwrap",
         "libselinux",
+        "libutils",
     ],
 
     clang: true,
@@ -30,6 +34,9 @@
     defaults: ["installd_defaults"],
 
     export_include_dirs: ["."],
+    aidl: {
+        export_aidl_headers: true,
+    },
 }
 
 //
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index d29e5ba..8bf0b53 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -24,19 +24,32 @@
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
 
-LOCAL_SRC_FILES := otapreopt.cpp commands.cpp globals.cpp utils.cpp
+LOCAL_SRC_FILES := otapreopt.cpp InstalldNativeService.cpp globals.cpp utils.cpp dexopt.cpp binder/android/os/IInstalld.aidl
 LOCAL_SHARED_LIBRARIES := \
     libbase \
+    libbinder \
     libcutils \
     liblog \
     liblogwrap \
     libselinux \
+    libutils \
 
 LOCAL_STATIC_LIBRARIES := libdiskusage
 LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
 LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
+# OTA slot script
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= otapreopt_slot
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := otapreopt_slot.sh
+LOCAL_INIT_RC := otapreopt.rc
+
+include $(BUILD_PREBUILT)
+
 # OTA postinstall script
 
 include $(CLEAR_VARS)
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
new file mode 100644
index 0000000..c1413ff
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -0,0 +1,1843 @@
+/*
+** Copyright 2008, 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 "InstalldNativeService.h"
+
+#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
+
+#include <errno.h>
+#include <inttypes.h>
+#include <fstream>
+#include <fts.h>
+#include <regex>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/file.h>
+#include <sys/resource.h>
+#include <sys/quota.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>               // TODO: Move everything to base/logging.
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+#include <system/thread_defs.h>
+#include <utils/Trace.h>
+
+#include "dexopt.h"
+#include "globals.h"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
+
+#include "MatchExtensionGen.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "installd"
+#endif
+
+using android::base::StringPrintf;
+using std::endl;
+
+namespace android {
+namespace installd {
+
+static constexpr const char* kCpPath = "/system/bin/cp";
+static constexpr const char* kXattrDefault = "user.default";
+
+static constexpr const char* PKG_LIB_POSTFIX = "/lib";
+static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
+static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
+
+static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
+static constexpr const char* IDMAP_SUFFIX = "@idmap";
+
+// NOTE: keep in sync with StorageManager
+static constexpr int FLAG_STORAGE_DE = 1 << 0;
+static constexpr int FLAG_STORAGE_CE = 1 << 1;
+
+// NOTE: keep in sync with Installer
+static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
+static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+static constexpr int FLAG_USE_QUOTA = 1 << 12;
+
+#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
+namespace {
+
+constexpr const char* kDump = "android.permission.DUMP";
+
+static binder::Status ok() {
+    return binder::Status::ok();
+}
+
+static binder::Status exception(uint32_t code, const std::string& msg) {
+    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+}
+
+static binder::Status error() {
+    return binder::Status::fromServiceSpecificError(errno);
+}
+
+static binder::Status error(const std::string& msg) {
+    PLOG(ERROR) << msg;
+    return binder::Status::fromServiceSpecificError(errno, String8(msg.c_str()));
+}
+
+static binder::Status error(uint32_t code, const std::string& msg) {
+    LOG(ERROR) << msg << " (" << code << ")";
+    return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
+}
+
+binder::Status checkPermission(const char* permission) {
+    pid_t pid;
+    uid_t uid;
+
+    if (checkCallingPermission(String16(permission), reinterpret_cast<int32_t*>(&pid),
+            reinterpret_cast<int32_t*>(&uid))) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_SECURITY,
+                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
+    }
+}
+
+binder::Status checkUid(uid_t expectedUid) {
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (uid == expectedUid || uid == AID_ROOT) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_SECURITY,
+                StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
+    }
+}
+
+binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) {
+    if (!uuid || is_valid_filename(*uuid)) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("UUID %s is malformed", uuid->c_str()));
+    }
+}
+
+binder::Status checkArgumentPackageName(const std::string& packageName) {
+    if (is_valid_package_name(packageName.c_str())) {
+        return ok();
+    } else {
+        return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+                StringPrintf("Package name %s is malformed", packageName.c_str()));
+    }
+}
+
+#define ENFORCE_UID(uid) {                                  \
+    binder::Status status = checkUid((uid));                \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_UUID(uuid) {                         \
+    binder::Status status = checkArgumentUuid((uuid));      \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+#define CHECK_ARGUMENT_PACKAGE_NAME(packageName) {          \
+    binder::Status status =                                 \
+            checkArgumentPackageName((packageName));        \
+    if (!status.isOk()) {                                   \
+        return status;                                      \
+    }                                                       \
+}
+
+}  // namespace
+
+status_t InstalldNativeService::start() {
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+    status_t ret = BinderService<InstalldNativeService>::publish();
+    if (ret != android::OK) {
+        return ret;
+    }
+    sp<ProcessState> ps(ProcessState::self());
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+    return android::OK;
+}
+
+status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */) {
+    auto out = std::fstream(StringPrintf("/proc/self/fd/%d", fd));
+    const binder::Status dump_permission = checkPermission(kDump);
+    if (!dump_permission.isOk()) {
+        out << dump_permission.toString8() << endl;
+        return PERMISSION_DENIED;
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    out << "installd is happy!" << endl << endl;
+    out << "Devices with quota support:" << endl;
+    for (const auto& n : mQuotaDevices) {
+        out << "    " << n.first << " = " << n.second << endl;
+    }
+    out << endl;
+    out.flush();
+
+    return NO_ERROR;
+}
+
+/**
+ * Perform restorecon of the given path, but only perform recursive restorecon
+ * if the label of that top-level file actually changed.  This can save us
+ * significant time by avoiding no-op traversals of large filesystem trees.
+ */
+static int restorecon_app_data_lazy(const std::string& path, const std::string& seInfo, uid_t uid,
+        bool existing) {
+    int res = 0;
+    char* before = nullptr;
+    char* after = nullptr;
+
+    // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
+    // libselinux. Not needed here.
+
+    if (lgetfilecon(path.c_str(), &before) < 0) {
+        PLOG(ERROR) << "Failed before getfilecon for " << path;
+        goto fail;
+    }
+    if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid, 0) < 0) {
+        PLOG(ERROR) << "Failed top-level restorecon for " << path;
+        goto fail;
+    }
+    if (lgetfilecon(path.c_str(), &after) < 0) {
+        PLOG(ERROR) << "Failed after getfilecon for " << path;
+        goto fail;
+    }
+
+    // If the initial top-level restorecon above changed the label, then go
+    // back and restorecon everything recursively
+    if (strcmp(before, after)) {
+        if (existing) {
+            LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at "
+                    << path << "; running recursive restorecon";
+        }
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
+                SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
+            PLOG(ERROR) << "Failed recursive restorecon for " << path;
+            goto fail;
+        }
+    }
+
+    goto done;
+fail:
+    res = -1;
+done:
+    free(before);
+    free(after);
+    return res;
+}
+
+static int restorecon_app_data_lazy(const std::string& parent, const char* name,
+        const std::string& seInfo, uid_t uid, bool existing) {
+    return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid,
+            existing);
+}
+
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
+    if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+        PLOG(ERROR) << "Failed to prepare " << path;
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * Prepare an app cache directory, which offers to fix-up the GID and
+ * directory mode flags during a platform upgrade.
+ */
+static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
+        uid_t uid, gid_t gid) {
+    auto path = StringPrintf("%s/%s", parent.c_str(), name);
+    struct stat st;
+    if (stat(path.c_str(), &st) != 0) {
+        if (errno == ENOENT) {
+            // This is fine, just create it
+            if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) {
+                PLOG(ERROR) << "Failed to prepare " << path;
+                return -1;
+            } else {
+                return 0;
+            }
+        } else {
+            PLOG(ERROR) << "Failed to stat " << path;
+            return -1;
+        }
+    }
+
+    mode_t actual_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
+    if (st.st_uid != uid) {
+        // Mismatched UID is real trouble; we can't recover
+        LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid
+                << " but expected " << uid;
+        return -1;
+    } else if (st.st_gid == gid && actual_mode == target_mode) {
+        // Everything looks good!
+        return 0;
+    }
+
+    // Directory is owned correctly, but GID or mode mismatch means it's
+    // probably a platform upgrade so we need to fix them
+    FTS *fts;
+    FTSENT *p;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+        PLOG(ERROR) << "Failed to fts_open " << path;
+        return -1;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        switch (p->fts_info) {
+        case FTS_DP:
+            if (chmod(p->fts_accpath, target_mode) != 0) {
+                PLOG(WARNING) << "Failed to chmod " << p->fts_path;
+            }
+            // Intentional fall through to also set GID
+        case FTS_F:
+            if (chown(p->fts_accpath, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        case FTS_SL:
+        case FTS_SLNONE:
+            if (lchown(p->fts_accpath, -1, gid) != 0) {
+                PLOG(WARNING) << "Failed to chown " << p->fts_path;
+            }
+            break;
+        }
+    }
+    fts_close(fts);
+    return 0;
+}
+
+binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+        const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    // Assume invalid inode unless filled in below
+    if (_aidl_return != nullptr) *_aidl_return = -1;
+
+    int32_t uid = multiuser_get_uid(userId, appId);
+    int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
+    mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+
+    // If UID doesn't have a specific cache GID, use UID value
+    if (cacheGid == -1) {
+        cacheGid = uid;
+    }
+
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+        bool existing = (access(path.c_str(), F_OK) == 0);
+
+        if (prepare_app_dir(path, targetMode, uid) ||
+                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+            return error("Failed to prepare " + path);
+        }
+
+        // Consider restorecon over contents if label changed
+        if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
+                restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
+                restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
+            return error("Failed to restorecon " + path);
+        }
+
+        // Remember inode numbers of cache directories so that we can clear
+        // contents while CE storage is locked
+        if (write_path_inode(path, "cache", kXattrInodeCache) ||
+                write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
+            return error("Failed to write_path_inode for " + path);
+        }
+
+        // And return the CE inode of the top-level data directory so we can
+        // clear contents while CE storage is locked
+        if ((_aidl_return != nullptr)
+                && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
+            return error("Failed to get_path_inode for " + path);
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+        bool existing = (access(path.c_str(), F_OK) == 0);
+
+        if (prepare_app_dir(path, targetMode, uid) ||
+                prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+                prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+            return error("Failed to prepare " + path);
+        }
+
+        // Consider restorecon over contents if label changed
+        if (restorecon_app_data_lazy(path, seInfo, uid, existing)) {
+            return error("Failed to restorecon " + path);
+        }
+
+        if (property_get_bool("dalvik.vm.usejitprofiles", false)) {
+            const std::string profile_path = create_data_user_profile_package_path(userId, pkgname);
+            // read-write-execute only for the app user.
+            if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
+                return error("Failed to prepare " + profile_path);
+            }
+            std::string profile_file = create_primary_profile(profile_path);
+            // read-write only for the app user.
+            if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
+                return error("Failed to prepare " + profile_path);
+            }
+            const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
+            // dex2oat/profman runs under the shared app gid and it needs to read/write reference
+            // profiles.
+            int shared_app_gid = multiuser_get_shared_gid(0, appId);
+            if ((shared_app_gid != -1) && fs_prepare_dir_strict(
+                    ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
+                return error("Failed to prepare " + ref_profile_path);
+            }
+        }
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    // This method only exists to upgrade system apps that have requested
+    // forceDeviceEncrypted, so their default storage always lives in a
+    // consistent location.  This only works on non-FBE devices, since we
+    // never want to risk exposing data on a device with real CE/DE storage.
+
+    auto ce_path = create_data_user_ce_package_path(uuid_, userId, pkgname);
+    auto de_path = create_data_user_de_package_path(uuid_, userId, pkgname);
+
+    // If neither directory is marked as default, assume CE is default
+    if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
+            && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
+        if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
+            return error("Failed to mark default storage " + ce_path);
+        }
+    }
+
+    // Migrate default data location if needed
+    auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path;
+    auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path;
+
+    if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) {
+        LOG(WARNING) << "Requested default storage " << target
+                << " is not active; migrating from " << source;
+        if (delete_dir_contents_and_dir(target) != 0) {
+            return error("Failed to delete " + target);
+        }
+        if (rename(source.c_str(), target.c_str()) != 0) {
+            return error("Failed to rename " + source + " to " + target);
+        }
+    }
+
+    return ok();
+}
+
+
+binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* pkgname = packageName.c_str();
+    binder::Status res = ok();
+    if (!clear_reference_profile(pkgname)) {
+        res = error("Failed to clear reference profile for " + packageName);
+    }
+    if (!clear_current_profiles(pkgname)) {
+        res = error("Failed to clear current profiles for " + packageName);
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            path = read_path_inode(path, "cache", kXattrInodeCache);
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            path = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
+        }
+        if (access(path.c_str(), F_OK) == 0) {
+            if (delete_dir_contents(path) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        std::string suffix = "";
+        bool only_cache = false;
+        if (flags & FLAG_CLEAR_CACHE_ONLY) {
+            suffix = CACHE_DIR_POSTFIX;
+            only_cache = true;
+        } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+            suffix = CODE_CACHE_DIR_POSTFIX;
+            only_cache = true;
+        }
+
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
+        if (access(path.c_str(), F_OK) == 0) {
+            if (delete_dir_contents(path) != 0) {
+                res = error("Failed to delete contents of " + path);
+            }
+        }
+        if (!only_cache) {
+            if (!clear_current_profile(pkgname, userId)) {
+                res = error("Failed to clear current profile for " + packageName);
+            }
+        }
+    }
+    return res;
+}
+
+static int destroy_app_reference_profile(const char *pkgname) {
+    return delete_dir_contents_and_dir(
+        create_data_ref_profile_package_path(pkgname),
+        /*ignore_if_missing*/ true);
+}
+
+static int destroy_app_current_profiles(const char *pkgname, userid_t userid) {
+    return delete_dir_contents_and_dir(
+        create_data_user_profile_package_path(userid, pkgname),
+        /*ignore_if_missing*/ true);
+}
+
+binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* pkgname = packageName.c_str();
+    binder::Status res = ok();
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        if (destroy_app_current_profiles(pkgname, user) != 0) {
+            res = error("Failed to destroy current profiles for " + packageName);
+        }
+    }
+    if (destroy_app_reference_profile(pkgname) != 0) {
+        res = error("Failed to destroy reference profile for " + packageName);
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+
+    binder::Status res = ok();
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode);
+        if (delete_dir_contents_and_dir(path) != 0) {
+            res = error("Failed to delete " + path);
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
+        if (delete_dir_contents_and_dir(path) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        destroy_app_current_profiles(pkgname, userId);
+        // TODO(calin): If the package is still installed by other users it's probably
+        // beneficial to keep the reference profile around.
+        // Verify if it's ok to do that.
+        destroy_app_reference_profile(pkgname);
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+        const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+        const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+        int32_t targetSdkVersion) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(fromUuid);
+    CHECK_ARGUMENT_UUID(toUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
+    const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
+    const char* package_name = packageName.c_str();
+    const char* data_app_name = dataAppName.c_str();
+
+    binder::Status res = ok();
+    std::vector<userid_t> users = get_known_users(from_uuid);
+
+    // Copy app
+    {
+        auto from = create_data_app_package_path(from_uuid, data_app_name);
+        auto to = create_data_app_package_path(to_uuid, data_app_name);
+        auto to_parent = create_data_app_path(to_uuid);
+
+        char *argv[] = {
+            (char*) kCpPath,
+            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+            (char*) "-p", /* preserve timestamps, ownership, and permissions */
+            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+            (char*) "-P", /* Do not follow symlinks [default] */
+            (char*) "-d", /* don't dereference symlinks */
+            (char*) from.c_str(),
+            (char*) to_parent.c_str()
+        };
+
+        LOG(DEBUG) << "Copying " << from << " to " << to;
+        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+        if (rc != 0) {
+            res = error(rc, "Failed copying " + from + " to " + to);
+            goto fail;
+        }
+
+        if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
+            res = error("Failed to restorecon " + to);
+            goto fail;
+        }
+    }
+
+    // Copy private data for all known users
+    for (auto user : users) {
+
+        // Data source may not exist for all users; that's okay
+        auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
+        if (access(from_ce.c_str(), F_OK) != 0) {
+            LOG(INFO) << "Missing source " << from_ce;
+            continue;
+        }
+
+        if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
+                seInfo, targetSdkVersion, nullptr).isOk()) {
+            res = error("Failed to create package target");
+            goto fail;
+        }
+
+        char *argv[] = {
+            (char*) kCpPath,
+            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
+            (char*) "-p", /* preserve timestamps, ownership, and permissions */
+            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
+            (char*) "-P", /* Do not follow symlinks [default] */
+            (char*) "-d", /* don't dereference symlinks */
+            nullptr,
+            nullptr
+        };
+
+        {
+            auto from = create_data_user_de_package_path(from_uuid, user, package_name);
+            auto to = create_data_user_de_path(to_uuid, user);
+            argv[6] = (char*) from.c_str();
+            argv[7] = (char*) to.c_str();
+
+            LOG(DEBUG) << "Copying " << from << " to " << to;
+            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+            if (rc != 0) {
+                res = error(rc, "Failed copying " + from + " to " + to);
+                goto fail;
+            }
+        }
+        {
+            auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
+            auto to = create_data_user_ce_path(to_uuid, user);
+            argv[6] = (char*) from.c_str();
+            argv[7] = (char*) to.c_str();
+
+            LOG(DEBUG) << "Copying " << from << " to " << to;
+            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
+            if (rc != 0) {
+                res = error(rc, "Failed copying " + from + " to " + to);
+                goto fail;
+            }
+        }
+
+        if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+                appId, seInfo).isOk()) {
+            res = error("Failed to restorecon");
+            goto fail;
+        }
+    }
+
+    // We let the framework scan the new location and persist that before
+    // deleting the data in the old location; this ordering ensures that
+    // we can recover from things like battery pulls.
+    return ok();
+
+fail:
+    // Nuke everything we might have already copied
+    {
+        auto to = create_data_app_package_path(to_uuid, data_app_name);
+        if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+            LOG(WARNING) << "Failed to rollback " << to;
+        }
+    }
+    for (auto user : users) {
+        {
+            auto to = create_data_user_de_package_path(to_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
+        }
+        {
+            auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
+            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
+                LOG(WARNING) << "Failed to rollback " << to;
+            }
+        }
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    if (flags & FLAG_STORAGE_DE) {
+        if (uuid_ == nullptr) {
+            if (ensure_config_user_dirs(userId) != 0) {
+                return error(StringPrintf("Failed to ensure dirs for %d", userId));
+            }
+        }
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    binder::Status res = ok();
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_path(uuid_, userId);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        if (uuid_ == nullptr) {
+            path = create_data_misc_legacy_path(userId);
+            if (delete_dir_contents_and_dir(path, true) != 0) {
+                res = error("Failed to delete " + path);
+            }
+            path = create_data_user_profile_path(userId);
+            if (delete_dir_contents_and_dir(path, true) != 0) {
+                res = error("Failed to delete " + path);
+            }
+        }
+    }
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_path(uuid_, userId);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+        path = create_data_media_path(uuid_, userId);
+        if (delete_dir_contents_and_dir(path, true) != 0) {
+            res = error("Failed to delete " + path);
+        }
+    }
+    return res;
+}
+
+/* Try to ensure free_size bytes of storage are available.
+ * Returns 0 on success.
+ * This is rather simple-minded because doing a full LRU would
+ * be potentially memory-intensive, and without atime it would
+ * also require that apps constantly modify file metadata even
+ * when just reading from the cache, which is pretty awful.
+ */
+binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
+        int64_t freeStorageSize) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    cache_t* cache;
+    int64_t avail;
+
+    auto data_path = create_data_path(uuid_);
+
+    avail = data_disk_free(data_path);
+    if (avail < 0) {
+        return error("Failed to determine free space for " + data_path);
+    }
+
+    ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", freeStorageSize, avail);
+    if (avail >= freeStorageSize) {
+        return ok();
+    }
+
+    cache = start_cache_collection();
+
+    auto users = get_known_users(uuid_);
+    for (auto user : users) {
+        add_cache_files(cache, create_data_user_ce_path(uuid_, user));
+        add_cache_files(cache, create_data_user_de_path(uuid_, user));
+        add_cache_files(cache,
+                StringPrintf("%s/Android/data", create_data_media_path(uuid_, user).c_str()));
+    }
+
+    clear_cache_files(data_path, cache, freeStorageSize);
+    finish_cache_collection(cache);
+
+    avail = data_disk_free(data_path);
+    if (avail >= freeStorageSize) {
+        return ok();
+    } else {
+        return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
+                freeStorageSize, data_path.c_str(), avail));
+    }
+}
+
+binder::Status InstalldNativeService::rmdex(const std::string& codePath,
+        const std::string& instructionSet) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    char dex_path[PKG_PATH_MAX];
+
+    const char* path = codePath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+
+    if (validate_apk_path(path) && validate_system_app_path(path)) {
+        return error("Invalid path " + codePath);
+    }
+
+    if (!create_cache_path(dex_path, path, instruction_set)) {
+        return error("Failed to create cache path for " + codePath);
+    }
+
+    ALOGV("unlink %s\n", dex_path);
+    if (unlink(dex_path) < 0) {
+        return error(StringPrintf("Failed to unlink %s", dex_path));
+    } else {
+        return ok();
+    }
+}
+
+struct stats {
+    int64_t codeSize;
+    int64_t dataSize;
+    int64_t cacheSize;
+};
+
+#if MEASURE_DEBUG
+static std::string toString(std::vector<int64_t> values) {
+    std::stringstream res;
+    res << "[";
+    for (size_t i = 0; i < values.size(); i++) {
+        res << values[i];
+        if (i < values.size() - 1) {
+            res << ",";
+        }
+    }
+    res << "]";
+    return res.str();
+}
+#endif
+
+static void collectQuotaStats(const std::string& device, int32_t userId,
+        int32_t appId, struct stats* stats, struct stats* extStats ATTRIBUTE_UNUSED) {
+    if (device.empty()) return;
+
+    struct dqblk dq;
+
+    uid_t uid = multiuser_get_uid(userId, appId);
+    if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+            reinterpret_cast<char*>(&dq)) != 0) {
+        if (errno != ESRCH) {
+            PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+        }
+    } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+        stats->dataSize += dq.dqb_curspace;
+    }
+
+    int cacheGid = multiuser_get_cache_gid(userId, appId);
+    if (cacheGid != -1) {
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid;
+            }
+        } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace;
+#endif
+            stats->cacheSize += dq.dqb_curspace;
+        }
+    }
+
+    int sharedGid = multiuser_get_shared_app_gid(uid);
+    if (sharedGid != -1) {
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid;
+            }
+        } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << sharedGid << " " << dq.dqb_curspace;
+#endif
+            stats->codeSize += dq.dqb_curspace;
+        }
+    }
+
+#if MEASURE_EXTERNAL
+    // TODO: measure using external GIDs
+#endif
+}
+
+static void collectManualStats(const std::string& path, struct stats* stats) {
+    DIR *d;
+    int dfd;
+    struct dirent *de;
+    struct stat s;
+
+    d = opendir(path.c_str());
+    if (d == nullptr) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Failed to open " << path;
+        }
+        return;
+    }
+    dfd = dirfd(d);
+    while ((de = readdir(d))) {
+        const char *name = de->d_name;
+
+        int64_t size = 0;
+        if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+            size = s.st_blocks * 512;
+        }
+
+        if (de->d_type == DT_DIR) {
+            if (!strcmp(name, ".")) {
+                // Don't recurse, but still count node size
+            } else if (!strcmp(name, "..")) {
+                // Don't recurse or count node size
+                continue;
+            } else {
+                // Measure all children nodes
+                size = 0;
+                calculate_tree_size(StringPrintf("%s/%s", path.c_str(), name), &size);
+            }
+
+            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
+                stats->cacheSize += size;
+            }
+        }
+
+        // Legacy symlink isn't owned by app
+        if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
+            continue;
+        }
+
+        // Everything found inside is considered data
+        stats->dataSize += size;
+    }
+    closedir(d);
+}
+
+static void collectManualStatsForUser(const std::string& path, struct stats* stats,
+        bool exclude_apps = false) {
+    DIR *d;
+    int dfd;
+    struct dirent *de;
+    struct stat s;
+
+    d = opendir(path.c_str());
+    if (d == nullptr) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Failed to open " << path;
+        }
+        return;
+    }
+    dfd = dirfd(d);
+    while ((de = readdir(d))) {
+        if (de->d_type == DT_DIR) {
+            const char *name = de->d_name;
+            if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) != 0) {
+                continue;
+            }
+            if (!strcmp(name, ".") || !strcmp(name, "..")) {
+                continue;
+            } else if (exclude_apps && (s.st_uid >= AID_APP_START && s.st_uid <= AID_APP_END)) {
+                continue;
+            } else {
+                collectManualStats(StringPrintf("%s/%s", path.c_str(), name), stats);
+            }
+        }
+    }
+    closedir(d);
+}
+
+binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid,
+        const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+        int32_t appId, const std::vector<int64_t>& ceDataInodes,
+        const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    for (auto packageName : packageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetAppSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring user " << userId << " app " << appId;
+#endif
+
+    // Here's a summary of the common storage locations across the platform,
+    // and how they're each tagged:
+    //
+    // /data/app/com.example                           UID system
+    // /data/app/com.example/oat                       UID system
+    // /data/user/0/com.example                        UID u0_a10      GID u0_a10
+    // /data/user/0/com.example/cache                  UID u0_a10      GID u0_a10_cache
+    // /data/media/0/foo.txt                           UID u0_media_rw
+    // /data/media/0/bar.jpg                           UID u0_media_rw GID u0_media_image
+    // /data/media/0/Android/data/com.example          UID u0_media_rw GID u0_a10_ext
+    // /data/media/0/Android/data/com.example/cache    UID u0_media_rw GID u0_a10_ext_cache
+    // /data/media/obb/com.example                     UID system
+
+    struct stats stats;
+    struct stats extStats;
+    memset(&stats, 0, sizeof(stats));
+    memset(&extStats, 0, sizeof(extStats));
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto device = findQuotaDeviceForUuid(uuid);
+    if (device.empty()) {
+        flags &= ~FLAG_USE_QUOTA;
+    }
+
+    ATRACE_BEGIN("obb");
+    for (auto packageName : packageNames) {
+        auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str());
+        calculate_tree_size(obbCodePath, &extStats.codeSize);
+    }
+    ATRACE_END();
+
+    if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
+        ATRACE_BEGIN("code");
+        for (auto codePath : codePaths) {
+            calculate_tree_size(codePath, &stats.codeSize, -1,
+                    multiuser_get_shared_gid(userId, appId));
+        }
+        ATRACE_END();
+
+        ATRACE_BEGIN("quota");
+        collectQuotaStats(device, userId, appId, &stats, &extStats);
+        ATRACE_END();
+
+    } else {
+        ATRACE_BEGIN("code");
+        for (auto codePath : codePaths) {
+            calculate_tree_size(codePath, &stats.codeSize);
+        }
+        ATRACE_END();
+
+        for (size_t i = 0; i < packageNames.size(); i++) {
+            const char* pkgname = packageNames[i].c_str();
+
+            ATRACE_BEGIN("data");
+            auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInodes[i]);
+            collectManualStats(cePath, &stats);
+            auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname);
+            collectManualStats(dePath, &stats);
+            ATRACE_END();
+
+            ATRACE_BEGIN("profiles");
+            auto userProfilePath = create_data_user_profile_package_path(userId, pkgname);
+            calculate_tree_size(userProfilePath, &stats.dataSize);
+            auto refProfilePath = create_data_ref_profile_package_path(pkgname);
+            calculate_tree_size(refProfilePath, &stats.codeSize);
+            ATRACE_END();
+
+#if MEASURE_EXTERNAL
+            ATRACE_BEGIN("external");
+            auto extPath = create_data_media_package_path(uuid_, userId, pkgname, "data");
+            collectManualStats(extPath, &extStats);
+            auto mediaPath = create_data_media_package_path(uuid_, userId, pkgname, "media");
+            calculate_tree_size(mediaPath, &extStats.dataSize);
+            ATRACE_END();
+#endif
+        }
+
+        ATRACE_BEGIN("dalvik");
+        int32_t sharedGid = multiuser_get_shared_gid(userId, appId);
+        if (sharedGid != -1) {
+            calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+                    sharedGid, -1);
+        }
+        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+                multiuser_get_uid(userId, appId), -1);
+        ATRACE_END();
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(stats.codeSize);
+    ret.push_back(stats.dataSize);
+    ret.push_back(stats.cacheSize);
+    ret.push_back(extStats.codeSize);
+    ret.push_back(extStats.dataSize);
+    ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+        std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetUserSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring user " << userId;
+#endif
+
+    struct stats stats;
+    struct stats extStats;
+    memset(&stats, 0, sizeof(stats));
+    memset(&extStats, 0, sizeof(extStats));
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    ATRACE_BEGIN("obb");
+    auto obbPath = create_data_path(uuid_) + "/media/obb";
+    calculate_tree_size(obbPath, &extStats.codeSize);
+    ATRACE_END();
+
+    auto device = findQuotaDeviceForUuid(uuid);
+    if (device.empty()) {
+        flags &= ~FLAG_USE_QUOTA;
+    }
+
+    if (flags & FLAG_USE_QUOTA) {
+        ATRACE_BEGIN("code");
+        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize, -1, -1, true);
+        ATRACE_END();
+
+        ATRACE_BEGIN("data");
+        auto cePath = create_data_user_ce_path(uuid_, userId);
+        collectManualStatsForUser(cePath, &stats, true);
+        auto dePath = create_data_user_de_path(uuid_, userId);
+        collectManualStatsForUser(dePath, &stats, true);
+        ATRACE_END();
+
+        ATRACE_BEGIN("profile");
+        auto userProfilePath = create_data_user_profile_path(userId);
+        calculate_tree_size(userProfilePath, &stats.dataSize, -1, -1, true);
+        auto refProfilePath = create_data_ref_profile_path();
+        calculate_tree_size(refProfilePath, &stats.codeSize, -1, -1, true);
+        ATRACE_END();
+
+#if MEASURE_EXTERNAL
+        ATRACE_BEGIN("external");
+        // TODO: measure external storage paths
+        ATRACE_END();
+#endif
+
+        ATRACE_BEGIN("dalvik");
+        calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize,
+                -1, -1, true);
+        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize,
+                -1, -1, true);
+        ATRACE_END();
+
+        ATRACE_BEGIN("quota");
+        for (auto appId : appIds) {
+            if (appId >= AID_APP_START) {
+                collectQuotaStats(device, userId, appId, &stats, &extStats);
+#if MEASURE_DEBUG
+                // Sleep to make sure we don't lose logs
+                usleep(1);
+#endif
+            }
+        }
+        ATRACE_END();
+    } else {
+        ATRACE_BEGIN("code");
+        calculate_tree_size(create_data_app_path(uuid_), &stats.codeSize);
+        ATRACE_END();
+
+        ATRACE_BEGIN("data");
+        auto cePath = create_data_user_ce_path(uuid_, userId);
+        collectManualStatsForUser(cePath, &stats);
+        auto dePath = create_data_user_de_path(uuid_, userId);
+        collectManualStatsForUser(dePath, &stats);
+        ATRACE_END();
+
+        ATRACE_BEGIN("profile");
+        auto userProfilePath = create_data_user_profile_path(userId);
+        calculate_tree_size(userProfilePath, &stats.dataSize);
+        auto refProfilePath = create_data_ref_profile_path();
+        calculate_tree_size(refProfilePath, &stats.codeSize);
+        ATRACE_END();
+
+#if MEASURE_EXTERNAL
+        ATRACE_BEGIN("external");
+        // TODO: measure external storage paths
+        ATRACE_END();
+#endif
+
+        ATRACE_BEGIN("dalvik");
+        calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize);
+        calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize);
+        ATRACE_END();
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(stats.codeSize);
+    ret.push_back(stats.dataSize);
+    ret.push_back(stats.cacheSize);
+    ret.push_back(extStats.codeSize);
+    ret.push_back(extStats.dataSize);
+    ret.push_back(extStats.cacheSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid,
+        int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    // When modifying this logic, always verify using tests:
+    // runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java -m testGetExternalSize
+
+#if MEASURE_DEBUG
+    LOG(INFO) << "Measuring external " << userId;
+#endif
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    int64_t totalSize = 0;
+    int64_t audioSize = 0;
+    int64_t videoSize = 0;
+    int64_t imageSize = 0;
+
+    auto device = findQuotaDeviceForUuid(uuid);
+    if (device.empty()) {
+        flags &= ~FLAG_USE_QUOTA;
+    }
+
+    if (flags & FLAG_USE_QUOTA) {
+        struct dqblk dq;
+
+        uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW);
+        if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid,
+                reinterpret_cast<char*>(&dq)) != 0) {
+            if (errno != ESRCH) {
+                PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid;
+            }
+        } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace;
+#endif
+            totalSize = dq.dqb_curspace;
+        }
+
+        gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), audioGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace;
+#endif
+            audioSize = dq.dqb_curspace;
+        }
+        gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), videoGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace;
+#endif
+            videoSize = dq.dqb_curspace;
+        }
+        gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE);
+        if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), imageGid,
+                reinterpret_cast<char*>(&dq)) == 0) {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for GID " << imageGid << " " << dq.dqb_curspace;
+#endif
+            imageSize = dq.dqb_curspace;
+        }
+    } else {
+        FTS *fts;
+        FTSENT *p;
+        auto path = create_data_media_path(uuid_, userId);
+        char *argv[] = { (char*) path.c_str(), nullptr };
+        if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+            return error("Failed to fts_open " + path);
+        }
+        while ((p = fts_read(fts)) != NULL) {
+            char* ext;
+            int64_t size = (p->fts_statp->st_blocks * 512);
+            switch (p->fts_info) {
+            case FTS_F:
+                // Only categorize files not belonging to apps
+                if (p->fts_statp->st_gid < AID_APP_START) {
+                    ext = strrchr(p->fts_name, '.');
+                    if (ext != nullptr) {
+                        switch (MatchExtension(++ext)) {
+                        case AID_MEDIA_AUDIO: audioSize += size; break;
+                        case AID_MEDIA_VIDEO: videoSize += size; break;
+                        case AID_MEDIA_IMAGE: imageSize += size; break;
+                        }
+                    }
+                }
+                // Fall through to always count against total
+            case FTS_D:
+            case FTS_DEFAULT:
+            case FTS_SL:
+            case FTS_SLNONE:
+                totalSize += size;
+                break;
+            }
+        }
+        fts_close(fts);
+    }
+
+    std::vector<int64_t> ret;
+    ret.push_back(totalSize);
+    ret.push_back(audioSize);
+    ret.push_back(videoSize);
+    ret.push_back(imageSize);
+#if MEASURE_DEBUG
+    LOG(DEBUG) << "Final result " << toString(ret);
+#endif
+    *_aidl_return = ret;
+    return ok();
+}
+
+// Dumps the contents of a profile file, using pkgname's dex files for pretty
+// printing the result.
+binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName,
+        const std::string& codePaths, bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* pkgname = packageName.c_str();
+    const char* code_paths = codePaths.c_str();
+
+    *_aidl_return = dump_profiles(uid, pkgname, code_paths);
+    return ok();
+}
+
+// TODO: Consider returning error codes.
+binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName,
+        bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* pkgname = packageName.c_str();
+    *_aidl_return = analyse_profiles(uid, pkgname);
+    return ok();
+}
+
+binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
+        const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
+        int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
+        const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
+        const std::unique_ptr<std::string>& sharedLibraries) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    if (packageName && *packageName != "*") {
+        CHECK_ARGUMENT_PACKAGE_NAME(*packageName);
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* apk_path = apkPath.c_str();
+    const char* pkgname = packageName ? packageName->c_str() : "*";
+    const char* instruction_set = instructionSet.c_str();
+    const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+    const char* compiler_filter = compilerFilter.c_str();
+    const char* volume_uuid = uuid ? uuid->c_str() : nullptr;
+    const char* shared_libraries = sharedLibraries ? sharedLibraries->c_str() : nullptr;
+
+    int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
+            oat_dir, dexFlags, compiler_filter, volume_uuid, shared_libraries);
+    return res ? error(res, "Failed to dexopt") : ok();
+}
+
+binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* instruction_set = instructionSet.c_str();
+
+    char boot_marker_path[PKG_PATH_MAX];
+    sprintf(boot_marker_path,
+          "%s/%s/%s/.booting",
+          android_data_dir.path,
+          DALVIK_CACHE,
+          instruction_set);
+
+    ALOGV("mark_boot_complete : %s", boot_marker_path);
+    if (unlink(boot_marker_path) != 0) {
+        return error(StringPrintf("Failed to unlink %s", boot_marker_path));
+    }
+    return ok();
+}
+
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
+        struct stat* statbuf)
+{
+    while (path[basepos] != 0) {
+        if (path[basepos] == '/') {
+            path[basepos] = 0;
+            if (lstat(path, statbuf) < 0) {
+                ALOGV("Making directory: %s\n", path);
+                if (mkdir(path, mode) == 0) {
+                    chown(path, uid, gid);
+                } else {
+                    ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
+                }
+            }
+            path[basepos] = '/';
+            basepos++;
+        }
+        basepos++;
+    }
+}
+
+binder::Status InstalldNativeService::linkNativeLibraryDirectory(
+        const std::unique_ptr<std::string>& uuid, const std::string& packageName,
+        const std::string& nativeLibPath32, int32_t userId) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgname = packageName.c_str();
+    const char* asecLibDir = nativeLibPath32.c_str();
+    struct stat s, libStat;
+    binder::Status res = ok();
+
+    auto _pkgdir = create_data_user_ce_package_path(uuid_, userId, pkgname);
+    auto _libsymlink = _pkgdir + PKG_LIB_POSTFIX;
+
+    const char* pkgdir = _pkgdir.c_str();
+    const char* libsymlink = _libsymlink.c_str();
+
+    if (stat(pkgdir, &s) < 0) {
+        return error("Failed to stat " + _pkgdir);
+    }
+
+    if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
+        return error("Failed to chown " + _pkgdir);
+    }
+
+    if (chmod(pkgdir, 0700) < 0) {
+        res = error("Failed to chmod " + _pkgdir);
+        goto out;
+    }
+
+    if (lstat(libsymlink, &libStat) < 0) {
+        if (errno != ENOENT) {
+            res = error("Failed to stat " + _libsymlink);
+            goto out;
+        }
+    } else {
+        if (S_ISDIR(libStat.st_mode)) {
+            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
+                res = error("Failed to delete " + _libsymlink);
+                goto out;
+            }
+        } else if (S_ISLNK(libStat.st_mode)) {
+            if (unlink(libsymlink) < 0) {
+                res = error("Failed to unlink " + _libsymlink);
+                goto out;
+            }
+        }
+    }
+
+    if (symlink(asecLibDir, libsymlink) < 0) {
+        res = error("Failed to symlink " + _libsymlink + " to " + nativeLibPath32);
+        goto out;
+    }
+
+out:
+    if (chmod(pkgdir, s.st_mode) < 0) {
+        auto msg = "Failed to cleanup chmod " + _pkgdir;
+        if (res.isOk()) {
+            res = error(msg);
+        } else {
+            PLOG(ERROR) << msg;
+        }
+    }
+
+    if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
+        auto msg = "Failed to cleanup chown " + _pkgdir;
+        if (res.isOk()) {
+            res = error(msg);
+        } else {
+            PLOG(ERROR) << msg;
+        }
+    }
+
+    return res;
+}
+
+static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
+{
+    static const char *IDMAP_BIN = "/system/bin/idmap";
+    static const size_t MAX_INT_LEN = 32;
+    char idmap_str[MAX_INT_LEN];
+
+    snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd);
+
+    execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL);
+    ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno));
+}
+
+// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
+// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
+static int flatten_path(const char *prefix, const char *suffix,
+        const char *overlay_path, char *idmap_path, size_t N)
+{
+    if (overlay_path == NULL || idmap_path == NULL) {
+        return -1;
+    }
+    const size_t len_overlay_path = strlen(overlay_path);
+    // will access overlay_path + 1 further below; requires absolute path
+    if (len_overlay_path < 2 || *overlay_path != '/') {
+        return -1;
+    }
+    const size_t len_idmap_root = strlen(prefix);
+    const size_t len_suffix = strlen(suffix);
+    if (SIZE_MAX - len_idmap_root < len_overlay_path ||
+            SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
+        // additions below would cause overflow
+        return -1;
+    }
+    if (N < len_idmap_root + len_overlay_path + len_suffix) {
+        return -1;
+    }
+    memset(idmap_path, 0, N);
+    snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
+    char *ch = idmap_path + len_idmap_root;
+    while (*ch != '\0') {
+        if (*ch == '/') {
+            *ch = '@';
+        }
+        ++ch;
+    }
+    return 0;
+}
+
+binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
+        const std::string& overlayApkPath, int32_t uid) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* target_apk = targetApkPath.c_str();
+    const char* overlay_apk = overlayApkPath.c_str();
+    ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
+
+    int idmap_fd = -1;
+    char idmap_path[PATH_MAX];
+
+    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
+                idmap_path, sizeof(idmap_path)) == -1) {
+        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
+        goto fail;
+    }
+
+    unlink(idmap_path);
+    idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+    if (idmap_fd < 0) {
+        ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
+        goto fail;
+    }
+    if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
+        ALOGE("idmap cannot chown '%s'\n", idmap_path);
+        goto fail;
+    }
+    if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+        ALOGE("idmap cannot chmod '%s'\n", idmap_path);
+        goto fail;
+    }
+
+    pid_t pid;
+    pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        if (setgid(uid) != 0) {
+            ALOGE("setgid(%d) failed during idmap\n", uid);
+            exit(1);
+        }
+        if (setuid(uid) != 0) {
+            ALOGE("setuid(%d) failed during idmap\n", uid);
+            exit(1);
+        }
+        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
+            ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
+            exit(1);
+        }
+
+        run_idmap(target_apk, overlay_apk, idmap_fd);
+        exit(1); /* only if exec call to idmap failed */
+    } else {
+        int status = wait_child(pid);
+        if (status != 0) {
+            ALOGE("idmap failed, status=0x%04x\n", status);
+            goto fail;
+        }
+    }
+
+    close(idmap_fd);
+    return ok();
+fail:
+    if (idmap_fd >= 0) {
+        close(idmap_fd);
+        unlink(idmap_path);
+    }
+    return error();
+}
+
+binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
+        const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+        const std::string& seInfo) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    binder::Status res = ok();
+
+    // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
+    unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    const char* pkgName = packageName.c_str();
+    const char* seinfo = seInfo.c_str();
+
+    uid_t uid = multiuser_get_uid(userId, appId);
+    if (flags & FLAG_STORAGE_CE) {
+        auto path = create_data_user_ce_package_path(uuid_, userId, pkgName);
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
+            res = error("restorecon failed for " + path);
+        }
+    }
+    if (flags & FLAG_STORAGE_DE) {
+        auto path = create_data_user_de_package_path(uuid_, userId, pkgName);
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
+            res = error("restorecon failed for " + path);
+        }
+    }
+    return res;
+}
+
+binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
+        const std::string& instructionSet) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* oat_dir = oatDir.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    char oat_instr_dir[PKG_PATH_MAX];
+
+    if (validate_apk_path(oat_dir)) {
+        return error("Invalid path " + oatDir);
+    }
+    if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
+        return error("Failed to prepare " + oatDir);
+    }
+    if (selinux_android_restorecon(oat_dir, 0)) {
+        return error("Failed to restorecon " + oatDir);
+    }
+    snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
+    if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
+        return error(StringPrintf("Failed to prepare %s", oat_instr_dir));
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    if (validate_apk_path(packageDir.c_str())) {
+        return error("Invalid path " + packageDir);
+    }
+    if (delete_dir_contents_and_dir(packageDir) != 0) {
+        return error("Failed to delete " + packageDir);
+    }
+    return ok();
+}
+
+binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
+        const std::string& fromBase, const std::string& toBase) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* relative_path = relativePath.c_str();
+    const char* from_base = fromBase.c_str();
+    const char* to_base = toBase.c_str();
+    char from_path[PKG_PATH_MAX];
+    char to_path[PKG_PATH_MAX];
+    snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
+    snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path);
+
+    if (validate_apk_path_subdirs(from_path)) {
+        return error(StringPrintf("Invalid from path %s", from_path));
+    }
+
+    if (validate_apk_path_subdirs(to_path)) {
+        return error(StringPrintf("Invalid to path %s", to_path));
+    }
+
+    if (link(from_path, to_path) < 0) {
+        return error(StringPrintf("Failed to link from %s to %s", from_path, to_path));
+    }
+
+    return ok();
+}
+
+binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
+        const std::string& instructionSet, const std::string& outputPath) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* apk_path = apkPath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    const char* oat_dir = outputPath.c_str();
+
+    bool success = move_ab(apk_path, instruction_set, oat_dir);
+    return success ? ok() : error();
+}
+
+binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
+        const std::string& instructionSet, const std::string& outputPath) {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* apk_path = apkPath.c_str();
+    const char* instruction_set = instructionSet.c_str();
+    const char* oat_dir = outputPath.c_str();
+
+    bool res = delete_odex(apk_path, instruction_set, oat_dir);
+    return res ? ok() : error();
+}
+
+binder::Status InstalldNativeService::invalidateMounts() {
+    ENFORCE_UID(AID_SYSTEM);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    mQuotaDevices.clear();
+
+    std::ifstream in("/proc/mounts");
+    if (!in.is_open()) {
+        return error("Failed to read mounts");
+    }
+
+    std::string source;
+    std::string target;
+    std::string ignored;
+    struct dqblk dq;
+    while (!in.eof()) {
+        std::getline(in, source, ' ');
+        std::getline(in, target, ' ');
+        std::getline(in, ignored);
+
+        if (source.compare(0, 11, "/dev/block/") == 0) {
+            if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
+                    reinterpret_cast<char*>(&dq)) == 0) {
+                LOG(DEBUG) << "Found " << source << " with quota";
+                mQuotaDevices[target] = source;
+            }
+        }
+    }
+    return ok();
+}
+
+std::string InstalldNativeService::findQuotaDeviceForUuid(
+        const std::unique_ptr<std::string>& uuid) {
+    auto path = create_data_path(uuid ? uuid->c_str() : nullptr);
+    return mQuotaDevices[path];
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
new file mode 100644
index 0000000..0208fb1
--- /dev/null
+++ b/cmds/installd/InstalldNativeService.h
@@ -0,0 +1,118 @@
+/*
+**
+** Copyright 2008, 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 COMMANDS_H_
+#define COMMANDS_H_
+
+#include <inttypes.h>
+#include <unistd.h>
+
+#include <vector>
+#include <unordered_map>
+
+#include <binder/BinderService.h>
+#include <cutils/multiuser.h>
+
+#include "android/os/BnInstalld.h"
+#include "installd_constants.h"
+
+namespace android {
+namespace installd {
+
+class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
+public:
+    static status_t start();
+    static char const* getServiceName() { return "installd"; }
+    virtual status_t dump(int fd, const Vector<String16> &args) override;
+
+    binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+            int32_t userSerial, int32_t flags);
+    binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId,
+            int32_t flags);
+
+    binder::Status createAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+            const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+    binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
+            const std::string& seInfo);
+    binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags);
+    binder::Status clearAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+    binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode);
+
+    binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
+            const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
+            int32_t appId, const std::vector<int64_t>& ceDataInodes,
+            const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return);
+    binder::Status getUserSize(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
+            std::vector<int64_t>* _aidl_return);
+    binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid,
+            int32_t userId, int32_t flags, std::vector<int64_t>* _aidl_return);
+
+    binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
+            const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
+            const std::string& dataAppName, int32_t appId, const std::string& seInfo,
+            int32_t targetSdkVersion);
+
+    binder::Status dexopt(const std::string& apkPath, int32_t uid,
+            const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
+            int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
+            const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
+            const std::unique_ptr<std::string>& sharedLibraries);
+
+    binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
+
+    binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return);
+    binder::Status dumpProfiles(int32_t uid, const std::string& packageName,
+            const std::string& codePaths, bool* _aidl_return);
+    binder::Status clearAppProfiles(const std::string& packageName);
+    binder::Status destroyAppProfiles(const std::string& packageName);
+
+    binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
+            int32_t uid);
+    binder::Status rmPackageDir(const std::string& packageDir);
+    binder::Status markBootComplete(const std::string& instructionSet);
+    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t freeStorageSize);
+    binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
+            const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
+    binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
+    binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
+            const std::string& toBase);
+    binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
+            const std::string& outputPath);
+    binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
+            const std::string& outputPath);
+
+    binder::Status invalidateMounts();
+
+private:
+    std::recursive_mutex mLock;
+
+    /* Map from mount point to underlying device node */
+    std::unordered_map<std::string, std::string> mQuotaDevices;
+
+    std::string findQuotaDeviceForUuid(const std::unique_ptr<std::string>& uuid);
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // COMMANDS_H_
diff --git a/cmds/installd/MatchExtensionGen.h b/cmds/installd/MatchExtensionGen.h
new file mode 100644
index 0000000..fded6b7
--- /dev/null
+++ b/cmds/installd/MatchExtensionGen.h
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+
+    switch (ext[0]) {
+    case '3':
+        switch (ext[1]) {
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'p': case 'P':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    case '2':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_VIDEO;
+                        }
+                    }
+                }
+            }
+        }
+    case 'a': case 'A':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case 'c': case 'C':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                case 'c': case 'C':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                case 'f': case 'F':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case 'r': case 'R':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 't': case 'T':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'i': case 'I':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        }
+    case 'b': case 'B':
+        switch (ext[1]) {
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case 'p': case 'P':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'c': case 'C':
+        switch (ext[1]) {
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'd': case 'D':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'f': case 'F':
+        switch (ext[1]) {
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case 'c': case 'C':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            case 'i': case 'I':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'g': case 'G':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        }
+    case 'j': case 'J':
+        switch (ext[1]) {
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'e': case 'E':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'g': case 'G':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'l': case 'L':
+        switch (ext[1]) {
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'm': case 'M':
+        switch (ext[1]) {
+        case '3':
+            switch (ext[2]) {
+            case 'u': case 'U':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case '4':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'k': case 'K':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'o': case 'O':
+            switch (ext[2]) {
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'i': case 'I':
+                    switch (ext[4]) {
+                    case 'e': case 'E':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_VIDEO;
+                        }
+                    }
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case '3':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case '4':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'e': case 'E':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'g': case 'G':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    case 'a': case 'A':
+                        switch (ext[5]) {
+                        case '\0': return AID_MEDIA_AUDIO;
+                        }
+                    }
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                case 'a': case 'A':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_AUDIO;
+                    }
+                }
+            }
+        case 'x': case 'X':
+            switch (ext[2]) {
+            case 'u': case 'U':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'n': case 'N':
+        switch (ext[1]) {
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'o': case 'O':
+        switch (ext[1]) {
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'p': case 'P':
+        switch (ext[1]) {
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'c': case 'C':
+            switch (ext[2]) {
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'l': case 'L':
+            switch (ext[2]) {
+            case 's': case 'S':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 'q': case 'Q':
+        switch (ext[1]) {
+        case 't': case 'T':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'r': case 'R':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_AUDIO;
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 's': case 'S':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'g': case 'G':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_AUDIO;
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    case 's': case 'S':
+        switch (ext[1]) {
+        case 'd': case 'D':
+            switch (ext[2]) {
+            case '2':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'n': case 'N':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'w': case 'W':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'g': case 'G':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'z': case 'Z':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        }
+    case 't': case 'T':
+        switch (ext[1]) {
+        case 'i': case 'I':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                case 'f': case 'F':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 's': case 'S':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            }
+        }
+    case 'v': case 'V':
+        switch (ext[1]) {
+        case 'o': case 'O':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'w': case 'W':
+        switch (ext[1]) {
+        case 'a': case 'A':
+            switch (ext[2]) {
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            }
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 'e': case 'E':
+            switch (ext[2]) {
+            case 'b': case 'B':
+                switch (ext[3]) {
+                case 'm': case 'M':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_VIDEO;
+                    }
+                case 'p': case 'P':
+                    switch (ext[4]) {
+                    case '\0': return AID_MEDIA_IMAGE;
+                    }
+                }
+            }
+        case 'm': case 'M':
+            switch (ext[2]) {
+            case '\0': return AID_MEDIA_VIDEO;
+            case 'a': case 'A':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_AUDIO;
+                }
+            case 'v': case 'V':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'r': case 'R':
+            switch (ext[2]) {
+            case 'f': case 'F':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        case 'v': case 'V':
+            switch (ext[2]) {
+            case 'x': case 'X':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_VIDEO;
+                }
+            }
+        }
+    case 'x': case 'X':
+        switch (ext[1]) {
+        case 'b': case 'B':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'p': case 'P':
+            switch (ext[2]) {
+            case 'm': case 'M':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        case 'w': case 'W':
+            switch (ext[2]) {
+            case 'd': case 'D':
+                switch (ext[3]) {
+                case '\0': return AID_MEDIA_IMAGE;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
new file mode 100644
index 0000000..2f12ea9
--- /dev/null
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+/** {@hide} */
+interface IInstalld {
+    void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags);
+    void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags);
+
+    long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
+            int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion);
+    void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags, int appId, @utf8InCpp String seInfo);
+    void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags);
+    void clearAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags, long ceDataInode);
+    void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
+            int userId, int flags, long ceDataInode);
+
+    long[] getAppSize(@nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+            int userId, int flags, int appId, in long[] ceDataInodes,
+            in @utf8InCpp String[] codePaths);
+    long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+    long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags);
+
+    void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
+            @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
+            @utf8InCpp String seInfo, int targetSdkVersion);
+
+    void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+            @utf8InCpp String instructionSet, int dexoptNeeded,
+            @nullable @utf8InCpp String outputPath, int dexFlags,
+            @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
+            @nullable @utf8InCpp String sharedLibraries);
+
+    void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
+
+    boolean mergeProfiles(int uid, @utf8InCpp String packageName);
+    boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths);
+    void clearAppProfiles(@utf8InCpp String packageName);
+    void destroyAppProfiles(@utf8InCpp String packageName);
+
+    void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
+    void rmPackageDir(@utf8InCpp String packageDir);
+    void markBootComplete(@utf8InCpp String instructionSet);
+    void freeCache(@nullable @utf8InCpp String uuid, long freeStorageSize);
+    void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
+            @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
+    void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
+    void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
+            @utf8InCpp String toBase);
+    void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+            @utf8InCpp String outputPath);
+    void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
+            @utf8InCpp String outputPath);
+
+    void invalidateMounts();
+}
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
deleted file mode 100644
index 4fde1ee..0000000
--- a/cmds/installd/commands.cpp
+++ /dev/null
@@ -1,2284 +0,0 @@
-/*
-** Copyright 2008, 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 "commands.h"
-
-#include <errno.h>
-#include <inttypes.h>
-#include <regex>
-#include <stdlib.h>
-#include <sys/capability.h>
-#include <sys/file.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/xattr.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/fs.h>
-#include <cutils/log.h>               // TODO: Move everything to base/logging.
-#include <cutils/sched_policy.h>
-#include <diskusage/dirsize.h>
-#include <logwrap/logwrap.h>
-#include <private/android_filesystem_config.h>
-#include <selinux/android.h>
-#include <system/thread_defs.h>
-
-#include <globals.h>
-#include <installd_deps.h>
-#include <otapreopt_utils.h>
-#include <utils.h>
-
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-
-using android::base::EndsWith;
-using android::base::StringPrintf;
-
-namespace android {
-namespace installd {
-
-static constexpr const char* kCpPath = "/system/bin/cp";
-static constexpr const char* kXattrDefault = "user.default";
-
-static constexpr const char* PKG_LIB_POSTFIX = "/lib";
-static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
-static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
-
-static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
-static constexpr const char* IDMAP_SUFFIX = "@idmap";
-
-// NOTE: keep in sync with StorageManager
-static constexpr int FLAG_STORAGE_DE = 1 << 0;
-static constexpr int FLAG_STORAGE_CE = 1 << 1;
-
-// NOTE: keep in sync with Installer
-static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
-static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-
-/* dexopt needed flags matching those in dalvik.system.DexFile */
-static constexpr int DEXOPT_DEX2OAT_NEEDED       = 1;
-static constexpr int DEXOPT_PATCHOAT_NEEDED      = 2;
-static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3;
-
-#define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
-
-typedef int fd_t;
-
-static bool property_get_bool(const char* property_name, bool default_value = false) {
-    char tmp_property_value[kPropertyValueMax];
-    bool have_property = get_property(property_name, tmp_property_value, nullptr) > 0;
-    if (!have_property) {
-        return default_value;
-    }
-    return strcmp(tmp_property_value, "true") == 0;
-}
-
-// Keep profile paths in sync with ActivityThread.
-constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
-static std::string create_primary_profile(const std::string& profile_dir) {
-    return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
-}
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        appid_t appid, const char* seinfo, int target_sdk_version) {
-    uid_t uid = multiuser_get_uid(userid, appid);
-    int target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
-        if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
-            PLOG(ERROR) << "Failed to prepare " << path;
-            return -1;
-        }
-        if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) {
-            PLOG(ERROR) << "Failed to setfilecon " << path;
-            return -1;
-        }
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgname);
-        if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) == -1) {
-            PLOG(ERROR) << "Failed to prepare " << path;
-            // TODO: include result once 25796509 is fixed
-            return 0;
-        }
-        if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) {
-            PLOG(ERROR) << "Failed to setfilecon " << path;
-            // TODO: include result once 25796509 is fixed
-            return 0;
-        }
-
-        if (property_get_bool("dalvik.vm.usejitprofiles")) {
-            const std::string profile_path = create_data_user_profile_package_path(userid, pkgname);
-            // read-write-execute only for the app user.
-            if (fs_prepare_dir_strict(profile_path.c_str(), 0700, uid, uid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << profile_path;
-                return -1;
-            }
-            std::string profile_file = create_primary_profile(profile_path);
-            // read-write only for the app user.
-            if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << profile_path;
-                return -1;
-            }
-            const std::string ref_profile_path = create_data_ref_profile_package_path(pkgname);
-            // dex2oat/profman runs under the shared app gid and it needs to read/write reference
-            // profiles.
-            appid_t shared_app_gid = multiuser_get_shared_app_gid(uid);
-            if (fs_prepare_dir_strict(
-                    ref_profile_path.c_str(), 0700, shared_app_gid, shared_app_gid) != 0) {
-                PLOG(ERROR) << "Failed to prepare " << ref_profile_path;
-                return -1;
-            }
-        }
-    }
-    return 0;
-}
-
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags) {
-    // This method only exists to upgrade system apps that have requested
-    // forceDeviceEncrypted, so their default storage always lives in a
-    // consistent location.  This only works on non-FBE devices, since we
-    // never want to risk exposing data on a device with real CE/DE storage.
-
-    auto ce_path = create_data_user_ce_package_path(uuid, userid, pkgname);
-    auto de_path = create_data_user_de_package_path(uuid, userid, pkgname);
-
-    // If neither directory is marked as default, assume CE is default
-    if (getxattr(ce_path.c_str(), kXattrDefault, nullptr, 0) == -1
-            && getxattr(de_path.c_str(), kXattrDefault, nullptr, 0) == -1) {
-        if (setxattr(ce_path.c_str(), kXattrDefault, nullptr, 0, 0) != 0) {
-            PLOG(ERROR) << "Failed to mark default storage " << ce_path;
-            return -1;
-        }
-    }
-
-    // Migrate default data location if needed
-    auto target = (flags & FLAG_STORAGE_DE) ? de_path : ce_path;
-    auto source = (flags & FLAG_STORAGE_DE) ? ce_path : de_path;
-
-    if (getxattr(target.c_str(), kXattrDefault, nullptr, 0) == -1) {
-        LOG(WARNING) << "Requested default storage " << target
-                << " is not active; migrating from " << source;
-        if (delete_dir_contents_and_dir(target) != 0) {
-            PLOG(ERROR) << "Failed to delete";
-            return -1;
-        }
-        if (rename(source.c_str(), target.c_str()) != 0) {
-            PLOG(ERROR) << "Failed to rename";
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static bool clear_profile(const std::string& profile) {
-    base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
-    if (ufd.get() < 0) {
-        if (errno != ENOENT) {
-            PLOG(WARNING) << "Could not open profile " << profile;
-            return false;
-        } else {
-            // Nothing to clear. That's ok.
-            return true;
-        }
-    }
-
-    if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
-        if (errno != EWOULDBLOCK) {
-            PLOG(WARNING) << "Error locking profile " << profile;
-        }
-        // This implies that the app owning this profile is running
-        // (and has acquired the lock).
-        //
-        // If we can't acquire the lock bail out since clearing is useless anyway
-        // (the app will write again to the profile).
-        //
-        // Note:
-        // This does not impact the this is not an issue for the profiling correctness.
-        // In case this is needed because of an app upgrade, profiles will still be
-        // eventually cleared by the app itself due to checksum mismatch.
-        // If this is needed because profman advised, then keeping the data around
-        // until the next run is again not an issue.
-        //
-        // If the app attempts to acquire a lock while we've held one here,
-        // it will simply skip the current write cycle.
-        return false;
-    }
-
-    bool truncated = ftruncate(ufd.get(), 0) == 0;
-    if (!truncated) {
-        PLOG(WARNING) << "Could not truncate " << profile;
-    }
-    if (flock(ufd.get(), LOCK_UN) != 0) {
-        PLOG(WARNING) << "Error unlocking profile " << profile;
-    }
-    return truncated;
-}
-
-static bool clear_reference_profile(const char* pkgname) {
-    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
-    std::string reference_profile = create_primary_profile(reference_profile_dir);
-    return clear_profile(reference_profile);
-}
-
-static bool clear_current_profile(const char* pkgname, userid_t user) {
-    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
-    std::string profile = create_primary_profile(profile_dir);
-    return clear_profile(profile);
-}
-
-static bool clear_current_profiles(const char* pkgname) {
-    bool success = true;
-    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
-    for (auto user : users) {
-        success &= clear_current_profile(pkgname, user);
-    }
-    return success;
-}
-
-int clear_app_profiles(const char* pkgname) {
-    bool success = true;
-    success &= clear_reference_profile(pkgname);
-    success &= clear_current_profiles(pkgname);
-    return success ? 0 : -1;
-}
-
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode) {
-    std::string suffix = "";
-    bool only_cache = false;
-    if (flags & FLAG_CLEAR_CACHE_ONLY) {
-        suffix = CACHE_DIR_POSTFIX;
-        only_cache = true;
-    } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
-        suffix = CODE_CACHE_DIR_POSTFIX;
-        only_cache = true;
-    }
-
-    int res = 0;
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode) + suffix;
-        if (access(path.c_str(), F_OK) == 0) {
-            res |= delete_dir_contents(path);
-        }
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgname) + suffix;
-        if (access(path.c_str(), F_OK) == 0) {
-            // TODO: include result once 25796509 is fixed
-            delete_dir_contents(path);
-        }
-        if (!only_cache) {
-            if (!clear_current_profile(pkgname, userid)) {
-                res |= -1;
-            }
-        }
-    }
-    return res;
-}
-
-static int destroy_app_reference_profile(const char *pkgname) {
-    return delete_dir_contents_and_dir(
-        create_data_ref_profile_package_path(pkgname),
-        /*ignore_if_missing*/ true);
-}
-
-static int destroy_app_current_profiles(const char *pkgname, userid_t userid) {
-    return delete_dir_contents_and_dir(
-        create_data_user_profile_package_path(userid, pkgname),
-        /*ignore_if_missing*/ true);
-}
-
-int destroy_app_profiles(const char *pkgname) {
-    int result = 0;
-    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
-    for (auto user : users) {
-        result |= destroy_app_current_profiles(pkgname, user);
-    }
-    result |= destroy_app_reference_profile(pkgname);
-    return result;
-}
-
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode) {
-    int res = 0;
-    if (flags & FLAG_STORAGE_CE) {
-        res |= delete_dir_contents_and_dir(
-                create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode));
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        res |= delete_dir_contents_and_dir(
-                create_data_user_de_package_path(uuid, userid, pkgname));
-        destroy_app_current_profiles(pkgname, userid);
-        // TODO(calin): If the package is still installed by other users it's probably
-        // beneficial to keep the reference profile around.
-        // Verify if it's ok to do that.
-        destroy_app_reference_profile(pkgname);
-    }
-    return res;
-}
-
-int move_complete_app(const char *from_uuid, const char *to_uuid, const char *package_name,
-        const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version) {
-    std::vector<userid_t> users = get_known_users(from_uuid);
-
-    // Copy app
-    {
-        auto from = create_data_app_package_path(from_uuid, data_app_name);
-        auto to = create_data_app_package_path(to_uuid, data_app_name);
-        auto to_parent = create_data_app_path(to_uuid);
-
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            (char*) from.c_str(),
-            (char*) to_parent.c_str()
-        };
-
-        LOG(DEBUG) << "Copying " << from << " to " << to;
-        int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-
-        if (rc != 0) {
-            LOG(ERROR) << "Failed copying " << from << " to " << to
-                    << ": status " << rc;
-            goto fail;
-        }
-
-        if (selinux_android_restorecon(to.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
-            LOG(ERROR) << "Failed to restorecon " << to;
-            goto fail;
-        }
-    }
-
-    // Copy private data for all known users
-    for (auto user : users) {
-
-        // Data source may not exist for all users; that's okay
-        auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
-        if (access(from_ce.c_str(), F_OK) != 0) {
-            LOG(INFO) << "Missing source " << from_ce;
-            continue;
-        }
-
-        if (create_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
-                appid, seinfo, target_sdk_version) != 0) {
-            LOG(ERROR) << "Failed to create package target on " << to_uuid;
-            goto fail;
-        }
-
-        char *argv[] = {
-            (char*) kCpPath,
-            (char*) "-F", /* delete any existing destination file first (--remove-destination) */
-            (char*) "-p", /* preserve timestamps, ownership, and permissions */
-            (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */
-            (char*) "-P", /* Do not follow symlinks [default] */
-            (char*) "-d", /* don't dereference symlinks */
-            nullptr,
-            nullptr
-        };
-
-        {
-            auto from = create_data_user_de_package_path(from_uuid, user, package_name);
-            auto to = create_data_user_de_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
-
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-            if (rc != 0) {
-                LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
-                goto fail;
-            }
-        }
-        {
-            auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
-            auto to = create_data_user_ce_path(to_uuid, user);
-            argv[6] = (char*) from.c_str();
-            argv[7] = (char*) to.c_str();
-
-            LOG(DEBUG) << "Copying " << from << " to " << to;
-            int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true);
-            if (rc != 0) {
-                LOG(ERROR) << "Failed copying " << from << " to " << to << " with status " << rc;
-                goto fail;
-            }
-        }
-
-        if (restorecon_app_data(to_uuid, package_name, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
-                appid, seinfo) != 0) {
-            LOG(ERROR) << "Failed to restorecon";
-            goto fail;
-        }
-    }
-
-    // We let the framework scan the new location and persist that before
-    // deleting the data in the old location; this ordering ensures that
-    // we can recover from things like battery pulls.
-    return 0;
-
-fail:
-    // Nuke everything we might have already copied
-    {
-        auto to = create_data_app_package_path(to_uuid, data_app_name);
-        if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-            LOG(WARNING) << "Failed to rollback " << to;
-        }
-    }
-    for (auto user : users) {
-        {
-            auto to = create_data_user_de_package_path(to_uuid, user, package_name);
-            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-                LOG(WARNING) << "Failed to rollback " << to;
-            }
-        }
-        {
-            auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
-            if (delete_dir_contents(to.c_str(), 1, NULL) != 0) {
-                LOG(WARNING) << "Failed to rollback " << to;
-            }
-        }
-    }
-    return -1;
-}
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial ATTRIBUTE_UNUSED,
-        int flags) {
-    if (flags & FLAG_STORAGE_DE) {
-        if (uuid == nullptr) {
-            return ensure_config_user_dirs(userid);
-        }
-    }
-    return 0;
-}
-
-int destroy_user_data(const char *uuid, userid_t userid, int flags) {
-    int res = 0;
-    if (flags & FLAG_STORAGE_DE) {
-        res |= delete_dir_contents_and_dir(create_data_user_de_path(uuid, userid), true);
-        if (uuid == nullptr) {
-            res |= delete_dir_contents_and_dir(create_data_misc_legacy_path(userid), true);
-            res |= delete_dir_contents_and_dir(create_data_user_profiles_path(userid), true);
-        }
-    }
-    if (flags & FLAG_STORAGE_CE) {
-        res |= delete_dir_contents_and_dir(create_data_user_ce_path(uuid, userid), true);
-        res |= delete_dir_contents_and_dir(create_data_media_path(uuid, userid), true);
-    }
-    return res;
-}
-
-/* Try to ensure free_size bytes of storage are available.
- * Returns 0 on success.
- * This is rather simple-minded because doing a full LRU would
- * be potentially memory-intensive, and without atime it would
- * also require that apps constantly modify file metadata even
- * when just reading from the cache, which is pretty awful.
- */
-int free_cache(const char *uuid, int64_t free_size) {
-    cache_t* cache;
-    int64_t avail;
-
-    auto data_path = create_data_path(uuid);
-
-    avail = data_disk_free(data_path);
-    if (avail < 0) return -1;
-
-    ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
-    if (avail >= free_size) return 0;
-
-    cache = start_cache_collection();
-
-    auto users = get_known_users(uuid);
-    for (auto user : users) {
-        add_cache_files(cache, create_data_user_ce_path(uuid, user));
-        add_cache_files(cache, create_data_user_de_path(uuid, user));
-        add_cache_files(cache,
-                StringPrintf("%s/Android/data", create_data_media_path(uuid, user).c_str()));
-    }
-
-    clear_cache_files(data_path, cache, free_size);
-    finish_cache_collection(cache);
-
-    return data_disk_free(data_path) >= free_size ? 0 : -1;
-}
-
-int rm_dex(const char *path, const char *instruction_set)
-{
-    char dex_path[PKG_PATH_MAX];
-
-    if (validate_apk_path(path) && validate_system_app_path(path)) {
-        ALOGE("invalid apk path '%s' (bad prefix)\n", path);
-        return -1;
-    }
-
-    if (!create_cache_path(dex_path, path, instruction_set)) return -1;
-
-    ALOGV("unlink %s\n", dex_path);
-    if (unlink(dex_path) < 0) {
-        if (errno != ENOENT) {
-            ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));
-        }
-        return -1;
-    } else {
-        return 0;
-    }
-}
-
-static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *datasize,
-        int64_t *cachesize) {
-    DIR *d;
-    int dfd;
-    struct dirent *de;
-    struct stat s;
-
-    d = opendir(path.c_str());
-    if (d == nullptr) {
-        PLOG(WARNING) << "Failed to open " << path;
-        return;
-    }
-    dfd = dirfd(d);
-    while ((de = readdir(d))) {
-        const char *name = de->d_name;
-
-        int64_t statsize = 0;
-        if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
-            statsize = stat_size(&s);
-        }
-
-        if (de->d_type == DT_DIR) {
-            int subfd;
-            int64_t dirsize = 0;
-            /* always skip "." and ".." */
-            if (name[0] == '.') {
-                if (name[1] == 0) continue;
-                if ((name[1] == '.') && (name[2] == 0)) continue;
-            }
-            subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
-            if (subfd >= 0) {
-                dirsize = calculate_dir_size(subfd);
-                close(subfd);
-            }
-            // TODO: check xattrs!
-            if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) {
-                *datasize += statsize;
-                *cachesize += dirsize;
-            } else {
-                *datasize += dirsize + statsize;
-            }
-        } else if (de->d_type == DT_LNK && !strcmp(name, "lib")) {
-            *codesize += statsize;
-        } else {
-            *datasize += statsize;
-        }
-    }
-    closedir(d);
-}
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
-        const char *code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
-        int64_t* asecsize) {
-    DIR *d;
-    int dfd;
-
-    d = opendir(code_path);
-    if (d != nullptr) {
-        dfd = dirfd(d);
-        *codesize += calculate_dir_size(dfd);
-        closedir(d);
-    }
-
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
-        add_app_data_size(path, codesize, datasize, cachesize);
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgname);
-        add_app_data_size(path, codesize, datasize, cachesize);
-    }
-
-    *asecsize = 0;
-
-    return 0;
-}
-
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) {
-    struct stat buf;
-    memset(&buf, 0, sizeof(buf));
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
-        if (stat(path.c_str(), &buf) == 0) {
-            *inode = buf.st_ino;
-            return 0;
-        }
-    }
-    return -1;
-}
-
-static int split_count(const char *str)
-{
-  char *ctx;
-  int count = 0;
-  char buf[kPropertyValueMax];
-
-  strncpy(buf, str, sizeof(buf));
-  char *pBuf = buf;
-
-  while(strtok_r(pBuf, " ", &ctx) != NULL) {
-    count++;
-    pBuf = NULL;
-  }
-
-  return count;
-}
-
-static int split(char *buf, const char **argv)
-{
-  char *ctx;
-  int count = 0;
-  char *tok;
-  char *pBuf = buf;
-
-  while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) {
-    argv[count++] = tok;
-    pBuf = NULL;
-  }
-
-  return count;
-}
-
-static void run_patchoat(int input_oat_fd, int input_vdex_fd, int out_oat_fd, int out_vdex_fd,
-    const char* input_oat_file_name, const char* input_vdex_file_name,
-    const char* output_oat_file_name, const char* output_vdex_file_name,
-    const char *pkgname ATTRIBUTE_UNUSED, const char *instruction_set)
-{
-    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
-    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
-    static const char* PATCHOAT_BIN = "/system/bin/patchoat";
-    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
-        ALOGE("Instruction set %s longer than max length of %d",
-              instruction_set, MAX_INSTRUCTION_SET_LEN);
-        return;
-    }
-
-    /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/
-    char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];
-    char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN];
-    char input_vdex_fd_arg[strlen("--input-vdex-fd=") + MAX_INT_LEN];
-    char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN];
-    char output_vdex_fd_arg[strlen("--output-vdex-fd=") + MAX_INT_LEN];
-    const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art";
-    // The caller has already gotten all the locks we need.
-    const char* no_lock_arg = "--no-lock-output";
-    sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);
-    sprintf(output_oat_fd_arg, "--output-oat-fd=%d", out_oat_fd);
-    sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_oat_fd);
-    ALOGV("Running %s isa=%s in-oat-fd=%d (%s) in-vdex-fd=%d (%s) "
-          "out-oat-fd=%d (%s) out-vdex-fd=%d (%s)\n",
-          PATCHOAT_BIN, instruction_set,
-          input_oat_fd, input_oat_file_name,
-          input_vdex_fd, input_vdex_file_name,
-          out_oat_fd, output_oat_file_name,
-          out_vdex_fd, output_vdex_file_name);
-
-    /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */
-    char* argv[9];
-    argv[0] = (char*) PATCHOAT_BIN;
-    argv[1] = (char*) patched_image_location_arg;
-    argv[2] = (char*) no_lock_arg;
-    argv[3] = instruction_set_arg;
-    argv[4] = input_oat_fd_arg;
-    argv[5] = input_vdex_fd_arg;
-    argv[6] = output_oat_fd_arg;
-    argv[7] = output_vdex_fd_arg;
-    argv[8] = NULL;
-
-    execv(PATCHOAT_BIN, (char* const *)argv);
-    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,
-        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) {
-    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
-
-    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
-        ALOGE("Instruction set %s longer than max length of %d",
-              instruction_set, MAX_INSTRUCTION_SET_LEN);
-        return;
-    }
-
-    char dex2oat_Xms_flag[kPropertyValueMax];
-    bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
-
-    char dex2oat_Xmx_flag[kPropertyValueMax];
-    bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
-
-    char dex2oat_threads_buf[kPropertyValueMax];
-    bool have_dex2oat_threads_flag = get_property(post_bootcomplete
-                                                      ? "dalvik.vm.dex2oat-threads"
-                                                      : "dalvik.vm.boot-dex2oat-threads",
-                                                  dex2oat_threads_buf,
-                                                  NULL) > 0;
-    char dex2oat_threads_arg[kPropertyValueMax + 2];
-    if (have_dex2oat_threads_flag) {
-        sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
-    }
-
-    char dex2oat_isa_features_key[kPropertyKeyMax];
-    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
-    char dex2oat_isa_features[kPropertyValueMax];
-    bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
-                                                  dex2oat_isa_features, NULL) > 0;
-
-    char dex2oat_isa_variant_key[kPropertyKeyMax];
-    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
-    char dex2oat_isa_variant[kPropertyValueMax];
-    bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
-                                                 dex2oat_isa_variant, NULL) > 0;
-
-    const char *dex2oat_norelocation = "-Xnorelocate";
-    bool have_dex2oat_relocation_skip_flag = false;
-
-    char dex2oat_flags[kPropertyValueMax];
-    int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
-                                 dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
-    ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
-
-    // If we booting without the real /data, don't spend time compiling.
-    char vold_decrypt[kPropertyValueMax];
-    bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
-    bool skip_compilation = (have_vold_decrypt &&
-                             (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
-                             (strcmp(vold_decrypt, "1") == 0)));
-
-    bool generate_debug_info = property_get_bool("debug.generate-debug-info");
-
-    char app_image_format[kPropertyValueMax];
-    char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
-    bool have_app_image_format =
-            image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
-    if (have_app_image_format) {
-        sprintf(image_format_arg, "--image-format=%s", app_image_format);
-    }
-
-    char dex2oat_large_app_threshold[kPropertyValueMax];
-    bool have_dex2oat_large_app_threshold =
-            get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
-    char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
-    if (have_dex2oat_large_app_threshold) {
-        sprintf(dex2oat_large_app_threshold_arg,
-                "--very-large-app-threshold=%s",
-                dex2oat_large_app_threshold);
-    }
-
-    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
-
-    static const char* RUNTIME_ARG = "--runtime-arg";
-
-    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
-
-    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 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];
-    char instruction_set_variant_arg[strlen("--instruction-set-variant=") + kPropertyValueMax];
-    char instruction_set_features_arg[strlen("--instruction-set-features=") + kPropertyValueMax];
-    char dex2oat_Xms_arg[strlen("-Xms") + kPropertyValueMax];
-    char dex2oat_Xmx_arg[strlen("-Xmx") + kPropertyValueMax];
-    char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
-    bool have_dex2oat_swap_fd = false;
-    char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN];
-    bool have_dex2oat_image_fd = false;
-    char dex2oat_image_fd[strlen("--app-image-fd=") + MAX_INT_LEN];
-
-    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(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);
-    sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
-    sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
-    if (swap_fd >= 0) {
-        have_dex2oat_swap_fd = true;
-        sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
-    }
-    if (image_fd >= 0) {
-        have_dex2oat_image_fd = true;
-        sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
-    }
-
-    if (have_dex2oat_Xms_flag) {
-        sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
-    }
-    if (have_dex2oat_Xmx_flag) {
-        sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
-    }
-
-    // Compute compiler filter.
-
-    bool have_dex2oat_compiler_filter_flag;
-    if (skip_compilation) {
-        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
-        have_dex2oat_compiler_filter_flag = true;
-        have_dex2oat_relocation_skip_flag = true;
-    } else if (vm_safe_mode) {
-        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
-        have_dex2oat_compiler_filter_flag = true;
-    } else if (compiler_filter != nullptr &&
-            strlen(compiler_filter) + strlen("--compiler-filter=") <
-                    arraysize(dex2oat_compiler_filter_arg)) {
-        sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
-        have_dex2oat_compiler_filter_flag = true;
-    } else {
-        char dex2oat_compiler_filter_flag[kPropertyValueMax];
-        have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
-                                                         dex2oat_compiler_filter_flag, NULL) > 0;
-        if (have_dex2oat_compiler_filter_flag) {
-            sprintf(dex2oat_compiler_filter_arg,
-                    "--compiler-filter=%s",
-                    dex2oat_compiler_filter_flag);
-        }
-    }
-
-    // Check whether all apps should be compiled debuggable.
-    if (!debuggable) {
-        char prop_buf[kPropertyValueMax];
-        debuggable =
-                (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
-                (prop_buf[0] == '1');
-    }
-    char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
-    if (profile_fd != -1) {
-        sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
-    }
-
-
-    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
-
-    const char* argv[8  // 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)
-                     + (have_dex2oat_Xmx_flag ? 2 : 0)
-                     + (have_dex2oat_compiler_filter_flag ? 1 : 0)
-                     + (have_dex2oat_threads_flag ? 1 : 0)
-                     + (have_dex2oat_swap_fd ? 1 : 0)
-                     + (have_dex2oat_image_fd ? 1 : 0)
-                     + (have_dex2oat_relocation_skip_flag ? 2 : 0)
-                     + (generate_debug_info ? 1 : 0)
-                     + (debuggable ? 1 : 0)
-                     + (have_app_image_format ? 1 : 0)
-                     + dex2oat_flags_count
-                     + (profile_fd == -1 ? 0 : 1)
-                     + (shared_libraries != nullptr ? 4 : 0)
-                     + (have_dex2oat_large_app_threshold ? 1 : 0)];
-    int i = 0;
-    argv[i++] = DEX2OAT_BIN;
-    argv[i++] = zip_fd_arg;
-    argv[i++] = zip_location_arg;
-    argv[i++] = vdex_fd_arg;
-    argv[i++] = oat_fd_arg;
-    argv[i++] = oat_location_arg;
-    argv[i++] = instruction_set_arg;
-    if (have_dex2oat_isa_variant) {
-        argv[i++] = instruction_set_variant_arg;
-    }
-    if (have_dex2oat_isa_features) {
-        argv[i++] = instruction_set_features_arg;
-    }
-    if (have_dex2oat_Xms_flag) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = dex2oat_Xms_arg;
-    }
-    if (have_dex2oat_Xmx_flag) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = dex2oat_Xmx_arg;
-    }
-    if (have_dex2oat_compiler_filter_flag) {
-        argv[i++] = dex2oat_compiler_filter_arg;
-    }
-    if (have_dex2oat_threads_flag) {
-        argv[i++] = dex2oat_threads_arg;
-    }
-    if (have_dex2oat_swap_fd) {
-        argv[i++] = dex2oat_swap_fd;
-    }
-    if (have_dex2oat_image_fd) {
-        argv[i++] = dex2oat_image_fd;
-    }
-    if (generate_debug_info) {
-        argv[i++] = "--generate-debug-info";
-    }
-    if (debuggable) {
-        argv[i++] = "--debuggable";
-    }
-    if (have_app_image_format) {
-        argv[i++] = image_format_arg;
-    }
-    if (have_dex2oat_large_app_threshold) {
-        argv[i++] = dex2oat_large_app_threshold_arg;
-    }
-    if (dex2oat_flags_count) {
-        i += split(dex2oat_flags, argv + i);
-    }
-    if (have_dex2oat_relocation_skip_flag) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = dex2oat_norelocation;
-    }
-    if (profile_fd != -1) {
-        argv[i++] = profile_arg;
-    }
-    if (shared_libraries != nullptr) {
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = "-classpath";
-        argv[i++] = RUNTIME_ARG;
-        argv[i++] = shared_libraries;
-    }
-    // Do not add after dex2oat_flags, they should override others for debugging.
-    argv[i] = NULL;
-
-    execv(DEX2OAT_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
-}
-
-/*
- * Whether dexopt should use a swap file when compiling an APK.
- *
- * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision
- * itself, anyways).
- *
- * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true".
- *
- * Otherwise, return true if this is a low-mem device.
- *
- * Otherwise, return default value.
- */
-static bool kAlwaysProvideSwapFile = false;
-static bool kDefaultProvideSwapFile = true;
-
-static bool ShouldUseSwapFileForDexopt() {
-    if (kAlwaysProvideSwapFile) {
-        return true;
-    }
-
-    // Check the "override" property. If it exists, return value == "true".
-    char dex2oat_prop_buf[kPropertyValueMax];
-    if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) {
-        if (strcmp(dex2oat_prop_buf, "true") == 0) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    // Shortcut for default value. This is an implementation optimization for the process sketched
-    // above. If the default value is true, we can avoid to check whether this is a low-mem device,
-    // as low-mem is never returning false. The compiler will optimize this away if it can.
-    if (kDefaultProvideSwapFile) {
-        return true;
-    }
-
-    bool is_low_mem = property_get_bool("ro.config.low_ram");
-    if (is_low_mem) {
-        return true;
-    }
-
-    // Default value must be false here.
-    return kDefaultProvideSwapFile;
-}
-
-static void SetDex2OatAndPatchOatScheduling(bool set_to_bg) {
-    if (set_to_bg) {
-        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-            ALOGE("set_sched_policy failed: %s\n", strerror(errno));
-            exit(70);
-        }
-        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
-            ALOGE("setpriority failed: %s\n", strerror(errno));
-            exit(71);
-        }
-    }
-}
-
-static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
-    for (size_t i = 0; i < fds.size(); i++) {
-        if (close(fds[i]) != 0) {
-            PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
-        }
-    }
-}
-
-static fd_t open_profile_dir(const std::string& profile_dir) {
-    fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
-            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
-    if (profile_dir_fd < 0) {
-        // In a multi-user environment, these directories can be created at
-        // different points and it's possible we'll attempt to open a profile
-        // dir before it exists.
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir;
-        }
-    }
-    return profile_dir_fd;
-}
-
-static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
-    fd_t profile_dir_fd  = open_profile_dir(profile_dir);
-    if (profile_dir_fd < 0) {
-        return -1;
-    }
-
-    fd_t profile_fd = -1;
-    std::string profile_file = create_primary_profile(profile_dir);
-
-    profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW));
-    if (profile_fd == -1) {
-        // It's not an error if the profile file does not exist.
-        if (errno != ENOENT) {
-            PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
-        }
-    }
-    // TODO(calin): use AutoCloseFD instead of closing the fd manually.
-    if (close(profile_dir_fd) != 0) {
-        PLOG(WARNING) << "Could not close profile dir " << profile_dir;
-    }
-    return profile_fd;
-}
-
-static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
-    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
-    return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
-}
-
-static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
-    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
-    int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
-    fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
-    if (fd < 0) {
-        return -1;
-    }
-    if (read_write) {
-        // Fix the owner.
-        if (fchown(fd, uid, uid) < 0) {
-            close(fd);
-            return -1;
-        }
-    }
-    return fd;
-}
-
-static void open_profile_files(uid_t uid, const char* pkgname,
-            /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
-    // Open the reference profile in read-write mode as profman might need to save the merge.
-    *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
-    if (*reference_profile_fd < 0) {
-        // We can't access the reference profile file.
-        return;
-    }
-
-    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
-    for (auto user : users) {
-        fd_t profile_fd = open_primary_profile_file(user, pkgname);
-        // Add to the lists only if both fds are valid.
-        if (profile_fd >= 0) {
-            profiles_fd->push_back(profile_fd);
-        }
-    }
-}
-
-static void drop_capabilities(uid_t uid) {
-    if (setgid(uid) != 0) {
-        ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
-        exit(64);
-    }
-    if (setuid(uid) != 0) {
-        ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
-        exit(65);
-    }
-    // drop capabilities
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    if (capset(&capheader, &capdata[0]) < 0) {
-        ALOGE("capset failed: %s\n", strerror(errno));
-        exit(66);
-    }
-}
-
-static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
-static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
-
-static void run_profman_merge(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
-    static const size_t MAX_INT_LEN = 32;
-    static const char* PROFMAN_BIN = "/system/bin/profman";
-
-    std::vector<std::string> profile_args(profiles_fd.size());
-    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
-    for (size_t k = 0; k < profiles_fd.size(); k++) {
-        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
-        profile_args[k].assign(profile_buf);
-    }
-    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
-    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
-
-    // program name, reference profile fd, the final NULL and the profile fds
-    const char* argv[3 + profiles_fd.size()];
-    int i = 0;
-    argv[i++] = PROFMAN_BIN;
-    argv[i++] = reference_profile_arg;
-    for (size_t k = 0; k < profile_args.size(); k++) {
-        argv[i++] = profile_args[k].c_str();
-    }
-    // Do not add after dex2oat_flags, they should override others for debugging.
-    argv[i] = NULL;
-
-    execv(PROFMAN_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
-    exit(68);   /* only get here on exec failure */
-}
-
-// Decides if profile guided compilation is needed or not based on existing profiles.
-// Returns true if there is enough information in the current profiles that worth
-// a re-compilation of the package.
-// If the return value is true all the current profiles would have been merged into
-// the reference profiles accessible with open_reference_profile().
-static bool analyse_profiles(uid_t uid, const char* pkgname) {
-    std::vector<fd_t> profiles_fd;
-    fd_t reference_profile_fd = -1;
-    open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
-    if (profiles_fd.empty() || (reference_profile_fd == -1)) {
-        // Skip profile guided compilation because no profiles were found.
-        // Or if the reference profile info couldn't be opened.
-        close_all_fds(profiles_fd, "profiles_fd");
-        if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
-            PLOG(WARNING) << "Failed to close fd for reference profile";
-        }
-        return false;
-    }
-
-    ALOGV("PROFMAN (MERGE): --- BEGIN '%s' ---\n", pkgname);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
-        run_profman_merge(profiles_fd, reference_profile_fd);
-        exit(68);   /* only get here on exec failure */
-    }
-    /* parent */
-    int return_code = wait_child(pid);
-    bool need_to_compile = false;
-    bool should_clear_current_profiles = false;
-    bool should_clear_reference_profile = false;
-    if (!WIFEXITED(return_code)) {
-        LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
-    } else {
-        return_code = WEXITSTATUS(return_code);
-        switch (return_code) {
-            case PROFMAN_BIN_RETURN_CODE_COMPILE:
-                need_to_compile = true;
-                should_clear_current_profiles = true;
-                should_clear_reference_profile = false;
-                break;
-            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
-                need_to_compile = false;
-                should_clear_current_profiles = false;
-                should_clear_reference_profile = false;
-                break;
-            case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
-                LOG(WARNING) << "Bad profiles for package " << pkgname;
-                need_to_compile = false;
-                should_clear_current_profiles = true;
-                should_clear_reference_profile = true;
-                break;
-            case PROFMAN_BIN_RETURN_CODE_ERROR_IO:  // fall-through
-            case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
-                // Temporary IO problem (e.g. locking). Ignore but log a warning.
-                LOG(WARNING) << "IO error while reading profiles for package " << pkgname;
-                need_to_compile = false;
-                should_clear_current_profiles = false;
-                should_clear_reference_profile = false;
-                break;
-           default:
-                // Unknown return code or error. Unlink profiles.
-                LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname
-                        << ": " << return_code;
-                need_to_compile = false;
-                should_clear_current_profiles = true;
-                should_clear_reference_profile = true;
-                break;
-        }
-    }
-    close_all_fds(profiles_fd, "profiles_fd");
-    if (close(reference_profile_fd) != 0) {
-        PLOG(WARNING) << "Failed to close fd for reference profile";
-    }
-    if (should_clear_current_profiles) {
-        clear_current_profiles(pkgname);
-    }
-    if (should_clear_reference_profile) {
-        clear_reference_profile(pkgname);
-    }
-    return need_to_compile;
-}
-
-static void run_profman_dump(const std::vector<fd_t>& profile_fds,
-                             fd_t reference_profile_fd,
-                             const std::vector<std::string>& dex_locations,
-                             const std::vector<fd_t>& apk_fds,
-                             fd_t output_fd) {
-    std::vector<std::string> profman_args;
-    static const char* PROFMAN_BIN = "/system/bin/profman";
-    profman_args.push_back(PROFMAN_BIN);
-    profman_args.push_back("--dump-only");
-    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
-    if (reference_profile_fd != -1) {
-        profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
-                                            reference_profile_fd));
-    }
-    for (fd_t profile_fd : profile_fds) {
-        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
-    }
-    for (const std::string& dex_location : dex_locations) {
-        profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
-    }
-    for (fd_t apk_fd : apk_fds) {
-        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
-    }
-    const char **argv = new const char*[profman_args.size() + 1];
-    size_t i = 0;
-    for (const std::string& profman_arg : profman_args) {
-        argv[i++] = profman_arg.c_str();
-    }
-    argv[i] = NULL;
-
-    execv(PROFMAN_BIN, (char * const *)argv);
-    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
-    exit(68);   /* only get here on exec failure */
-}
-
-static const char* get_location_from_path(const char* path) {
-    static constexpr char kLocationSeparator = '/';
-    const char *location = strrchr(path, kLocationSeparator);
-    if (location == NULL) {
-        return path;
-    } else {
-        // Skip the separator character.
-        return location + 1;
-    }
-}
-
-// Dumps the contents of a profile file, using pkgname's dex files for pretty
-// printing the result.
-bool dump_profile(uid_t uid, const char* pkgname, const char* code_path_string) {
-    std::vector<fd_t> profile_fds;
-    fd_t reference_profile_fd = -1;
-    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
-
-    ALOGV("PROFMAN (DUMP): --- BEGIN '%s' ---\n", pkgname);
-
-    open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
-
-    const bool has_reference_profile = (reference_profile_fd != -1);
-    const bool has_profiles = !profile_fds.empty();
-
-    if (!has_reference_profile && !has_profiles) {
-        ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
-        return false;
-    }
-
-    fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
-    if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
-        ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
-        return false;
-    }
-    std::vector<std::string> code_full_paths = base::Split(code_path_string, ";");
-    std::vector<std::string> dex_locations;
-    std::vector<fd_t> apk_fds;
-    for (const std::string& code_full_path : code_full_paths) {
-        const char* full_path = code_full_path.c_str();
-        fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
-        if (apk_fd == -1) {
-            ALOGE("installd cannot open '%s'\n", full_path);
-            return false;
-        }
-        dex_locations.push_back(get_location_from_path(full_path));
-        apk_fds.push_back(apk_fd);
-    }
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
-        run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
-                         apk_fds, output_fd);
-        exit(68);   /* only get here on exec failure */
-    }
-    /* parent */
-    close_all_fds(apk_fds, "apk_fds");
-    close_all_fds(profile_fds, "profile_fds");
-    if (close(reference_profile_fd) != 0) {
-        PLOG(WARNING) << "Failed to close fd for reference profile";
-    }
-    int return_code = wait_child(pid);
-    if (!WIFEXITED(return_code)) {
-        LOG(WARNING) << "profman failed for package " << pkgname << ": "
-                << return_code;
-        return false;
-    }
-    return true;
-}
-
-static std::string replace_file_extension(const std::string& oat_path, const std::string& new_ext) {
-  // A standard dalvik-cache entry. Replace ".dex" with `new_ext`.
-  if (EndsWith(oat_path, ".dex")) {
-    std::string new_path = oat_path;
-    new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext);
-    CHECK(EndsWith(new_path, new_ext.c_str()));
-    return new_path;
-  }
-
-  // An odex entry. Not that this may not be an extension, e.g., in the OTA
-  // case (where the base name will have an extension for the B artifact).
-  size_t odex_pos = oat_path.rfind(".odex");
-  if (odex_pos != std::string::npos) {
-    std::string new_path = oat_path;
-    new_path.replace(odex_pos, strlen(".odex"), new_ext);
-    CHECK_NE(new_path.find(new_ext), std::string::npos);
-    return new_path;
-  }
-
-  // Don't know how to handle this.
-  return "";
-}
-
-// Translate the given oat path to an art (app image) path. An empty string
-// denotes an error.
-static std::string create_image_filename(const std::string& oat_path) {
-    return replace_file_extension(oat_path, ".art");
-}
-
-// Translate the given oat path to a vdex path. An empty string denotes an error.
-static std::string create_vdex_filename(const std::string& oat_path) {
-    return replace_file_extension(oat_path, ".vdex");
-}
-
-static bool add_extension_to_file_name(char* file_name, const char* extension) {
-    if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
-        return false;
-    }
-    strcat(file_name, extension);
-    return true;
-}
-
-static int open_output_file(const char* file_name, bool recreate, int permissions) {
-    int flags = O_RDWR | O_CREAT;
-    if (recreate) {
-        if (unlink(file_name) < 0) {
-            if (errno != ENOENT) {
-                PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
-            }
-        }
-        flags |= O_EXCL;
-    }
-    return open(file_name, flags, permissions);
-}
-
-static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) {
-    if (fchmod(fd,
-               S_IRUSR|S_IWUSR|S_IRGRP |
-               (is_public ? S_IROTH : 0)) < 0) {
-        ALOGE("installd cannot chmod '%s' during dexopt\n", path);
-        return false;
-    } else if (fchown(fd, AID_SYSTEM, uid) < 0) {
-        ALOGE("installd cannot chown '%s' during dexopt\n", path);
-        return false;
-    }
-    return true;
-}
-
-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.
-    // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
-    // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
-    // extension to the cache path (5 bytes).
-    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
-        ALOGE("apk_path too long '%s'\n", apk_path);
-        return false;
-    }
-
-    if (oat_dir != NULL && oat_dir[0] != '!') {
-        if (validate_apk_path(oat_dir)) {
-            ALOGE("invalid oat_dir '%s'\n", oat_dir);
-            return false;
-        }
-        if (!calculate_oat_file_path(out_oat_path, oat_dir, apk_path, instruction_set)) {
-            return false;
-        }
-    } else {
-        if (!create_cache_path(out_oat_path, apk_path, instruction_set)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-// TODO: Consider returning error codes.
-bool merge_profiles(uid_t uid, const char *pkgname) {
-    return analyse_profiles(uid, pkgname);
-}
-
-static const char* parse_null(const char* arg) {
-    if (strcmp(arg, "!") == 0) {
-        return nullptr;
-    } else {
-        return arg;
-    }
-}
-
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
-    return dexopt(params[0],                    // apk_path
-                  atoi(params[1]),              // uid
-                  params[2],                    // pkgname
-                  params[3],                    // instruction_set
-                  atoi(params[4]),              // dexopt_needed
-                  params[5],                    // oat_dir
-                  atoi(params[6]),              // dexopt_flags
-                  params[7],                    // compiler_filter
-                  parse_null(params[8]),        // volume_uuid
-                  parse_null(params[9]));       // shared_libraries
-    static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
-}
-
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-//   Dex2oatFileWrapper<std::function<void ()>> file(open(...),
-//                                                   [name]() {
-//                                                       unlink(name.c_str());
-//                                                   });
-//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-//            wrapper if captured as a reference.
-//
-//   if (file.get() == -1) {
-//       // Error opening...
-//   }
-//
-//   ...
-//   if (error) {
-//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-//       // and delete the file (after the fd is closed).
-//       return -1;
-//   }
-//
-//   (Success case)
-//   file.SetCleanup(false);
-//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
-//   // (leaving the file around; after the fd is closed).
-//
-template <typename Cleanup>
-class Dex2oatFileWrapper {
- public:
-    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) {
-    }
-
-    Dex2oatFileWrapper(int value, Cleanup cleanup)
-            : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
-
-    ~Dex2oatFileWrapper() {
-        reset(-1);
-    }
-
-    int get() {
-        return value_;
-    }
-
-    void SetCleanup(bool cleanup) {
-        do_cleanup_ = cleanup;
-    }
-
-    void reset(int new_value) {
-        if (value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-    }
-
-    void reset(int new_value, Cleanup new_cleanup) {
-        if (value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-        cleanup_ = new_cleanup;
-    }
-
- private:
-    int value_;
-    Cleanup cleanup_;
-    bool do_cleanup_;
-};
-
-int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
-           int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
-           const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries)
-{
-    bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
-    bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
-    bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
-    bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
-    bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
-
-    CHECK(pkgname != nullptr);
-    CHECK(pkgname[0] != 0);
-
-    // Public apps should not be compiled with profile information ever. Same goes for the special
-    // package '*' used for the system server.
-    Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
-    if (!is_public && pkgname[0] != '*') {
-        // Open reference profile in read only mode as dex2oat does not get write permissions.
-        const std::string pkgname_str(pkgname);
-        reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
-                                   [pkgname_str]() {
-                                       clear_reference_profile(pkgname_str.c_str());
-                                   });
-        // Note: it's OK to not find a profile here.
-    }
-
-    if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
-        LOG_FATAL("dexopt flags contains unknown fields\n");
-    }
-
-    char out_oat_path[PKG_PATH_MAX];
-    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_oat_path)) {
-        return false;
-    }
-
-    const char *input_file;
-    char in_odex_path[PKG_PATH_MAX];
-    switch (dexopt_needed) {
-        case DEXOPT_DEX2OAT_NEEDED:
-            input_file = apk_path;
-            break;
-
-        case DEXOPT_PATCHOAT_NEEDED:
-            if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
-                return -1;
-            }
-            input_file = in_odex_path;
-            break;
-
-        case DEXOPT_SELF_PATCHOAT_NEEDED:
-            input_file = out_oat_path;
-            break;
-
-        default:
-            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            return 72;
-    }
-
-    struct stat input_stat;
-    memset(&input_stat, 0, sizeof(input_stat));
-    stat(input_file, &input_stat);
-
-    // Open the input file. If running dex2oat, `input_file` is the APK. If running
-    // patchoat, it is the OAT file to be relocated.
-    base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
-    if (input_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
-        return -1;
-    }
-
-    // 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(
-            open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
-            [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
-    if (out_oat_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for output during dexopt\n", out_oat_path);
-        return -1;
-    }
-    if (!set_permissions_and_ownership(out_oat_fd.get(), is_public, uid, out_oat_path)) {
-        return -1;
-    }
-
-    // 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()) {
-        return -1;
-    }
-    Dex2oatFileWrapper<std::function<void ()>> out_vdex_fd(
-            open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
-            [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
-    if (out_vdex_fd.get() < 0) {
-        ALOGE("installd cannot open '%s' for output during dexopt\n", out_vdex_path_str.c_str());
-        return -1;
-    }
-    if (!set_permissions_and_ownership(out_vdex_fd.get(), is_public,
-                uid, out_vdex_path_str.c_str())) {
-        return -1;
-    }
-
-    // Create a swap file if necessary.
-    base::unique_fd swap_fd;
-    if (ShouldUseSwapFileForDexopt()) {
-        // Make sure there really is enough space.
-        char swap_file_name[PKG_PATH_MAX];
-        strcpy(swap_file_name, out_oat_path);
-        if (add_extension_to_file_name(swap_file_name, ".swap")) {
-            swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
-        }
-        if (swap_fd.get() < 0) {
-            // Could not create swap file. Optimistically go on and hope that we can compile
-            // without it.
-            ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
-        } else {
-            // Immediately unlink. We don't really want to hit flash.
-            if (unlink(swap_file_name) < 0) {
-                PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
-            }
-        }
-    }
-
-    // Avoid generating an app image for extract only since it will not contain any classes.
-    Dex2oatFileWrapper<std::function<void ()>> image_fd;
-    const std::string image_path = create_image_filename(out_oat_path);
-    if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED && !image_path.empty()) {
-        char app_image_format[kPropertyValueMax];
-        bool have_app_image_format =
-                get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
-        // Use app images only if it is enabled (by a set image format) and we are compiling
-        // profile-guided (so the app image doesn't conservatively contain all classes).
-        if (profile_guided && have_app_image_format) {
-            // Recreate is true since we do not want to modify a mapped image. If the app is
-            // already running and we modify the image file, it can cause crashes (b/27493510).
-            image_fd.reset(open_output_file(image_path.c_str(),
-                                            true /*recreate*/,
-                                            0600 /*permissions*/),
-                           [image_path]() { unlink(image_path.c_str()); }
-                           );
-            if (image_fd.get() < 0) {
-                // Could not create application image file. Go on since we can compile without
-                // it.
-                LOG(ERROR) << "installd could not create '"
-                        << image_path
-                        << "' for image file during dexopt";
-            } else if (!set_permissions_and_ownership(image_fd.get(),
-                                                      is_public,
-                                                      uid,
-                                                      image_path.c_str())) {
-                image_fd.reset(-1);
-            }
-        }
-        // If we have a valid image file path but no image fd, explicitly erase the image file.
-        if (image_fd.get() < 0) {
-            if (unlink(image_path.c_str()) < 0) {
-                if (errno != ENOENT) {
-                    PLOG(ERROR) << "Couldn't unlink image file " << image_path;
-                }
-            }
-        }
-    }
-
-    ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
-
-        SetDex2OatAndPatchOatScheduling(boot_complete);
-        if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
-            ALOGE("flock(%s) failed: %s\n", out_oat_path, strerror(errno));
-            _exit(67);
-        }
-
-        if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED
-            || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {
-            run_patchoat(input_fd.get(),
-                         input_vdex_fd.get(),
-                         out_oat_fd.get(),
-                         out_vdex_fd.get(),
-                         input_file,
-                         in_vdex_path_str.c_str(),
-                         out_oat_path,
-                         out_vdex_path_str.c_str(),
-                         pkgname,
-                         instruction_set);
-        } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {
-            // Pass dex2oat the relative path to the input file.
-            const char *input_file_name = get_location_from_path(input_file);
-            run_dex2oat(input_fd.get(),
-                        out_oat_fd.get(),
-                        out_vdex_fd.get(),
-                        image_fd.get(),
-                        input_file_name,
-                        out_oat_path,
-                        swap_fd.get(),
-                        instruction_set,
-                        compiler_filter,
-                        vm_safe_mode,
-                        debuggable,
-                        boot_complete,
-                        reference_profile_fd.get(),
-                        shared_libraries);
-        } else {
-            ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
-            _exit(73);
-        }
-        _exit(68);   /* only get here on exec failure */
-    } else {
-        int res = wait_child(pid);
-        if (res == 0) {
-            ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
-        } else {
-            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
-            return -1;
-        }
-    }
-
-    struct utimbuf ut;
-    ut.actime = input_stat.st_atime;
-    ut.modtime = input_stat.st_mtime;
-    utime(out_oat_path, &ut);
-
-    // We've been successful, don't delete output.
-    out_oat_fd.SetCleanup(false);
-    out_vdex_fd.SetCleanup(false);
-    image_fd.SetCleanup(false);
-    reference_profile_fd.SetCleanup(false);
-
-    return 0;
-}
-
-int mark_boot_complete(const char* instruction_set)
-{
-  char boot_marker_path[PKG_PATH_MAX];
-  sprintf(boot_marker_path,
-          "%s/%s/%s/.booting",
-          android_data_dir.path,
-          DALVIK_CACHE,
-          instruction_set);
-
-  ALOGV("mark_boot_complete : %s", boot_marker_path);
-  if (unlink(boot_marker_path) != 0) {
-      ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path,
-            strerror(errno));
-      return -1;
-  }
-
-  return 0;
-}
-
-void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
-        struct stat* statbuf)
-{
-    while (path[basepos] != 0) {
-        if (path[basepos] == '/') {
-            path[basepos] = 0;
-            if (lstat(path, statbuf) < 0) {
-                ALOGV("Making directory: %s\n", path);
-                if (mkdir(path, mode) == 0) {
-                    chown(path, uid, gid);
-                } else {
-                    ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
-                }
-            }
-            path[basepos] = '/';
-            basepos++;
-        }
-        basepos++;
-    }
-}
-
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId)
-{
-    struct stat s, libStat;
-    int rc = 0;
-
-    std::string _pkgdir(create_data_user_ce_package_path(uuid, userId, pkgname));
-    std::string _libsymlink(_pkgdir + PKG_LIB_POSTFIX);
-
-    const char* pkgdir = _pkgdir.c_str();
-    const char* libsymlink = _libsymlink.c_str();
-
-    if (stat(pkgdir, &s) < 0) return -1;
-
-    if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
-        ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));
-        return -1;
-    }
-
-    if (chmod(pkgdir, 0700) < 0) {
-        ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
-        rc = -1;
-        goto out;
-    }
-
-    if (lstat(libsymlink, &libStat) < 0) {
-        if (errno != ENOENT) {
-            ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
-            rc = -1;
-            goto out;
-        }
-    } else {
-        if (S_ISDIR(libStat.st_mode)) {
-            if (delete_dir_contents(libsymlink, 1, NULL) < 0) {
-                rc = -1;
-                goto out;
-            }
-        } else if (S_ISLNK(libStat.st_mode)) {
-            if (unlink(libsymlink) < 0) {
-                ALOGE("couldn't unlink lib dir: %s\n", strerror(errno));
-                rc = -1;
-                goto out;
-            }
-        }
-    }
-
-    if (symlink(asecLibDir, libsymlink) < 0) {
-        ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
-                strerror(errno));
-        rc = -errno;
-        goto out;
-    }
-
-out:
-    if (chmod(pkgdir, s.st_mode) < 0) {
-        ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
-        rc = -errno;
-    }
-
-    if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
-        ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno));
-        return -errno;
-    }
-
-    return rc;
-}
-
-static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
-    static const char *IDMAP_BIN = "/system/bin/idmap";
-    static const size_t MAX_INT_LEN = 32;
-    char idmap_str[MAX_INT_LEN];
-
-    snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd);
-
-    execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL);
-    ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno));
-}
-
-// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
-// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
-static int flatten_path(const char *prefix, const char *suffix,
-        const char *overlay_path, char *idmap_path, size_t N)
-{
-    if (overlay_path == NULL || idmap_path == NULL) {
-        return -1;
-    }
-    const size_t len_overlay_path = strlen(overlay_path);
-    // will access overlay_path + 1 further below; requires absolute path
-    if (len_overlay_path < 2 || *overlay_path != '/') {
-        return -1;
-    }
-    const size_t len_idmap_root = strlen(prefix);
-    const size_t len_suffix = strlen(suffix);
-    if (SIZE_MAX - len_idmap_root < len_overlay_path ||
-            SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
-        // additions below would cause overflow
-        return -1;
-    }
-    if (N < len_idmap_root + len_overlay_path + len_suffix) {
-        return -1;
-    }
-    memset(idmap_path, 0, N);
-    snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
-    char *ch = idmap_path + len_idmap_root;
-    while (*ch != '\0') {
-        if (*ch == '/') {
-            *ch = '@';
-        }
-        ++ch;
-    }
-    return 0;
-}
-
-int idmap(const char *target_apk, const char *overlay_apk, uid_t uid)
-{
-    ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
-
-    int idmap_fd = -1;
-    char idmap_path[PATH_MAX];
-
-    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
-                idmap_path, sizeof(idmap_path)) == -1) {
-        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
-        goto fail;
-    }
-
-    unlink(idmap_path);
-    idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
-    if (idmap_fd < 0) {
-        ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
-        goto fail;
-    }
-    if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
-        ALOGE("idmap cannot chown '%s'\n", idmap_path);
-        goto fail;
-    }
-    if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
-        ALOGE("idmap cannot chmod '%s'\n", idmap_path);
-        goto fail;
-    }
-
-    pid_t pid;
-    pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        if (setgid(uid) != 0) {
-            ALOGE("setgid(%d) failed during idmap\n", uid);
-            exit(1);
-        }
-        if (setuid(uid) != 0) {
-            ALOGE("setuid(%d) failed during idmap\n", uid);
-            exit(1);
-        }
-        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
-            ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
-            exit(1);
-        }
-
-        run_idmap(target_apk, overlay_apk, idmap_fd);
-        exit(1); /* only if exec call to idmap failed */
-    } else {
-        int status = wait_child(pid);
-        if (status != 0) {
-            ALOGE("idmap failed, status=0x%04x\n", status);
-            goto fail;
-        }
-    }
-
-    close(idmap_fd);
-    return 0;
-fail:
-    if (idmap_fd >= 0) {
-        close(idmap_fd);
-        unlink(idmap_path);
-    }
-    return -1;
-}
-
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
-        appid_t appid, const char* seinfo) {
-    int res = 0;
-
-    // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here.
-    unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE;
-
-    if (!pkgName || !seinfo) {
-        ALOGE("Package name or seinfo tag is null when trying to restorecon.");
-        return -1;
-    }
-
-    uid_t uid = multiuser_get_uid(userid, appid);
-    if (flags & FLAG_STORAGE_CE) {
-        auto path = create_data_user_ce_package_path(uuid, userid, pkgName);
-        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
-            PLOG(ERROR) << "restorecon failed for " << path;
-            res = -1;
-        }
-    }
-    if (flags & FLAG_STORAGE_DE) {
-        auto path = create_data_user_de_package_path(uuid, userid, pkgName);
-        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, seflags) < 0) {
-            PLOG(ERROR) << "restorecon failed for " << path;
-            // TODO: include result once 25796509 is fixed
-        }
-    }
-
-    return res;
-}
-
-int create_oat_dir(const char* oat_dir, const char* instruction_set)
-{
-    char oat_instr_dir[PKG_PATH_MAX];
-
-    if (validate_apk_path(oat_dir)) {
-        ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir);
-        return -1;
-    }
-    if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
-        return -1;
-    }
-    if (selinux_android_restorecon(oat_dir, 0)) {
-        ALOGE("cannot restorecon dir '%s': %s\n", oat_dir, strerror(errno));
-        return -1;
-    }
-    snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set);
-    if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) {
-        return -1;
-    }
-    return 0;
-}
-
-int rm_package_dir(const char* apk_path)
-{
-    if (validate_apk_path(apk_path)) {
-        ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path);
-        return -1;
-    }
-    return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
-}
-
-int link_file(const char* relative_path, const char* from_base, const char* to_base) {
-    char from_path[PKG_PATH_MAX];
-    char to_path[PKG_PATH_MAX];
-    snprintf(from_path, PKG_PATH_MAX, "%s/%s", from_base, relative_path);
-    snprintf(to_path, PKG_PATH_MAX, "%s/%s", to_base, relative_path);
-
-    if (validate_apk_path_subdirs(from_path)) {
-        ALOGE("invalid app data sub-path '%s' (bad prefix)\n", from_path);
-        return -1;
-    }
-
-    if (validate_apk_path_subdirs(to_path)) {
-        ALOGE("invalid app data sub-path '%s' (bad prefix)\n", to_path);
-        return -1;
-    }
-
-    const int ret = link(from_path, to_path);
-    if (ret < 0) {
-        ALOGE("link(%s, %s) failed : %s", from_path, to_path, strerror(errno));
-        return -1;
-    }
-
-    return 0;
-}
-
-// Helper for move_ab, so that we can have common failure-case cleanup.
-static bool unlink_and_rename(const char* from, const char* to) {
-    // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
-    // return a failure.
-    struct stat s;
-    if (stat(to, &s) == 0) {
-        if (!S_ISREG(s.st_mode)) {
-            LOG(ERROR) << from << " is not a regular file to replace for A/B.";
-            return false;
-        }
-        if (unlink(to) != 0) {
-            LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
-            return false;
-        }
-    } else {
-        // This may be a permission problem. We could investigate the error code, but we'll just
-        // let the rename failure do the work for us.
-    }
-
-    // Try to rename "to" to "from."
-    if (rename(from, to) != 0) {
-        PLOG(ERROR) << "Could not rename " << from << " to " << to;
-        return false;
-    }
-    return true;
-}
-
-// Move/rename a B artifact (from) to an A artifact (to).
-static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
-    // Check whether B exists.
-    {
-        struct stat s;
-        if (stat(b_path.c_str(), &s) != 0) {
-            // Silently ignore for now. The service calling this isn't smart enough to understand
-            // lack of artifacts at the moment.
-            return false;
-        }
-        if (!S_ISREG(s.st_mode)) {
-            LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
-            // Try to unlink, but swallow errors.
-            unlink(b_path.c_str());
-            return false;
-        }
-    }
-
-    // Rename B to A.
-    if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
-        // Delete the b_path so we don't try again (or fail earlier).
-        if (unlink(b_path.c_str()) != 0) {
-            PLOG(ERROR) << "Could not unlink " << b_path;
-        }
-
-        return false;
-    }
-
-    return true;
-}
-
-int move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
-    if (apk_path == nullptr || instruction_set == nullptr || oat_dir == nullptr) {
-        LOG(ERROR) << "Cannot move_ab with null input";
-        return -1;
-    }
-
-    // Get the current slot suffix. No suffix, no A/B.
-    std::string slot_suffix;
-    {
-        char buf[kPropertyValueMax];
-        if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
-            return -1;
-        }
-        slot_suffix = buf;
-
-        if (!ValidateTargetSlotSuffix(slot_suffix)) {
-            LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
-            return -1;
-        }
-    }
-
-    // Validate other inputs.
-    if (validate_apk_path(apk_path) != 0) {
-        LOG(ERROR) << "invalid apk_path " << apk_path;
-        return -1;
-    }
-    if (validate_apk_path(oat_dir) != 0) {
-        LOG(ERROR) << "invalid oat_dir " << oat_dir;
-        return -1;
-    }
-
-    char a_path[PKG_PATH_MAX];
-    if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
-        return -1;
-    }
-    const std::string a_vdex_path = create_vdex_filename(a_path);
-    const std::string a_image_path = create_image_filename(a_path);
-
-    // B path = A path + slot suffix.
-    const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
-    const std::string b_vdex_path = StringPrintf("%s.%s", a_vdex_path.c_str(), slot_suffix.c_str());
-    const std::string b_image_path = StringPrintf("%s.%s",
-                                                  a_image_path.c_str(),
-                                                  slot_suffix.c_str());
-
-    bool success = true;
-    if (move_ab_path(b_path, a_path)) {
-        if (move_ab_path(b_vdex_path, a_vdex_path)) {
-            // Note: we can live without an app image. As such, ignore failure to move the image file.
-            //       If we decide to require the app image, or the app image being moved correctly,
-            //       then change accordingly.
-            constexpr bool kIgnoreAppImageFailure = true;
-
-            if (!a_image_path.empty()) {
-                if (!move_ab_path(b_image_path, a_image_path)) {
-                    if (!kIgnoreAppImageFailure) {
-                        success = false;
-                    }
-                }
-            }
-        } else {
-            // Cleanup: delete B image, ignore errors.
-            unlink(b_image_path.c_str());
-            success = false;
-        }
-    } else {
-        // Cleanup: delete B image, ignore errors.
-        unlink(b_vdex_path.c_str());
-        unlink(b_image_path.c_str());
-        success = false;
-    }
-
-    return success ? 0 : -1;
-}
-
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir) {
-    // Delete the oat/odex file.
-    char out_path[PKG_PATH_MAX];
-    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
-        return false;
-    }
-
-    // In case of a permission failure report the issue. Otherwise just print a warning.
-    auto unlink_and_check = [](const char* path) -> bool {
-        int result = unlink(path);
-        if (result != 0) {
-            if (errno == EACCES || errno == EPERM) {
-                PLOG(ERROR) << "Could not unlink " << path;
-                return false;
-            }
-            PLOG(WARNING) << "Could not unlink " << path;
-        }
-        return true;
-    };
-
-    // Delete the oat/odex file.
-    bool return_value_oat = unlink_and_check(out_path);
-
-    // Derive and delete the app image.
-    bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
-
-    // Report success.
-    return return_value_oat && return_value_art;
-}
-
-}  // namespace installd
-}  // namespace android
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
deleted file mode 100644
index ba27517..0000000
--- a/cmds/installd/commands.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-**
-** Copyright 2008, 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 COMMANDS_H_
-#define COMMANDS_H_
-
-#include <inttypes.h>
-#include <unistd.h>
-
-#include <cutils/multiuser.h>
-
-#include <installd_constants.h>
-
-namespace android {
-namespace installd {
-
-static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
-
-int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        appid_t appid, const char* seinfo, int target_sdk_version);
-int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
-        appid_t appid, const char* seinfo);
-int migrate_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags);
-int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode);
-int destroy_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
-        ino_t ce_data_inode);
-
-int move_complete_app(const char* from_uuid, const char *to_uuid, const char *package_name,
-        const char *data_app_name, appid_t appid, const char* seinfo, int target_sdk_version);
-
-int get_app_size(const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
-        const char* code_path, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
-        int64_t *asecsize);
-int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode);
-
-int create_user_data(const char *uuid, userid_t userid, int user_serial, int flags);
-int destroy_user_data(const char *uuid, userid_t userid, int flags);
-
-int rm_dex(const char *path, const char *instruction_set);
-int free_cache(const char *uuid, int64_t free_size);
-
-bool merge_profiles(uid_t uid, const char *pkgname);
-
-bool dump_profile(uid_t uid, const char *pkgname, const char *dex_files);
-
-int dexopt(const char *apk_path,
-           uid_t uid,
-           const char *pkgName,
-           const char *instruction_set,
-           int dexopt_needed,
-           const char* oat_dir,
-           int dexopt_flags,
-           const char* compiler_filter,
-           const char* volume_uuid,
-           const char* shared_libraries);
-static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
-
-// Helper for the above, converting arguments.
-int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
-
-int mark_boot_complete(const char *instruction_set);
-int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
-int idmap(const char *target_path, const char *overlay_path, uid_t uid);
-int create_oat_dir(const char* oat_dir, const char *instruction_set);
-int rm_package_dir(const char* apk_path);
-int clear_app_profiles(const char* pkgname);
-int destroy_app_profiles(const char* pkgname);
-int link_file(const char *relative_path, const char *from_base, const char *to_base);
-
-// Move a B version over to the A location. Only works for oat_dir != nullptr.
-int move_ab(const char *apk_path, const char *instruction_set, const char* oat_dir);
-
-// Delete odex files generated by dexopt.
-bool delete_odex(const char *apk_path, const char *instruction_set, const char *oat_dir);
-
-}  // namespace installd
-}  // namespace android
-
-#endif  // COMMANDS_H_
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
new file mode 100644
index 0000000..76d5695
--- /dev/null
+++ b/cmds/installd/dexopt.cpp
@@ -0,0 +1,1391 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "installed"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/properties.h>
+#include <cutils/sched_policy.h>
+#include <log/log.h>               // TODO: Move everything to base/logging.
+#include <private/android_filesystem_config.h>
+#include <system/thread_defs.h>
+
+#include "dexopt.h"
+#include "installd_deps.h"
+#include "otapreopt_utils.h"
+#include "utils.h"
+
+using android::base::StringPrintf;
+using android::base::EndsWith;
+
+namespace android {
+namespace installd {
+
+static const char* parse_null(const char* arg) {
+    if (strcmp(arg, "!") == 0) {
+        return nullptr;
+    } else {
+        return arg;
+    }
+}
+
+static bool clear_profile(const std::string& profile) {
+    base::unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
+    if (ufd.get() < 0) {
+        if (errno != ENOENT) {
+            PLOG(WARNING) << "Could not open profile " << profile;
+            return false;
+        } else {
+            // Nothing to clear. That's ok.
+            return true;
+        }
+    }
+
+    if (flock(ufd.get(), LOCK_EX | LOCK_NB) != 0) {
+        if (errno != EWOULDBLOCK) {
+            PLOG(WARNING) << "Error locking profile " << profile;
+        }
+        // This implies that the app owning this profile is running
+        // (and has acquired the lock).
+        //
+        // If we can't acquire the lock bail out since clearing is useless anyway
+        // (the app will write again to the profile).
+        //
+        // Note:
+        // This does not impact the this is not an issue for the profiling correctness.
+        // In case this is needed because of an app upgrade, profiles will still be
+        // eventually cleared by the app itself due to checksum mismatch.
+        // If this is needed because profman advised, then keeping the data around
+        // until the next run is again not an issue.
+        //
+        // If the app attempts to acquire a lock while we've held one here,
+        // it will simply skip the current write cycle.
+        return false;
+    }
+
+    bool truncated = ftruncate(ufd.get(), 0) == 0;
+    if (!truncated) {
+        PLOG(WARNING) << "Could not truncate " << profile;
+    }
+    if (flock(ufd.get(), LOCK_UN) != 0) {
+        PLOG(WARNING) << "Error unlocking profile " << profile;
+    }
+    return truncated;
+}
+
+bool clear_reference_profile(const char* pkgname) {
+    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
+    std::string reference_profile = create_primary_profile(reference_profile_dir);
+    return clear_profile(reference_profile);
+}
+
+bool clear_current_profile(const char* pkgname, userid_t user) {
+    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
+    std::string profile = create_primary_profile(profile_dir);
+    return clear_profile(profile);
+}
+
+bool clear_current_profiles(const char* pkgname) {
+    bool success = true;
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        success &= clear_current_profile(pkgname, user);
+    }
+    return success;
+}
+
+static int split_count(const char *str)
+{
+  char *ctx;
+  int count = 0;
+  char buf[kPropertyValueMax];
+
+  strncpy(buf, str, sizeof(buf));
+  char *pBuf = buf;
+
+  while(strtok_r(pBuf, " ", &ctx) != NULL) {
+    count++;
+    pBuf = NULL;
+  }
+
+  return count;
+}
+
+static int split(char *buf, const char **argv)
+{
+  char *ctx;
+  int count = 0;
+  char *tok;
+  char *pBuf = buf;
+
+  while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) {
+    argv[count++] = tok;
+    pBuf = NULL;
+  }
+
+  return count;
+}
+
+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) {
+    static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
+
+    if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
+        ALOGE("Instruction set %s longer than max length of %d",
+              instruction_set, MAX_INSTRUCTION_SET_LEN);
+        return;
+    }
+
+    char dex2oat_Xms_flag[kPropertyValueMax];
+    bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;
+
+    char dex2oat_Xmx_flag[kPropertyValueMax];
+    bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
+
+    char dex2oat_threads_buf[kPropertyValueMax];
+    bool have_dex2oat_threads_flag = get_property(post_bootcomplete
+                                                      ? "dalvik.vm.dex2oat-threads"
+                                                      : "dalvik.vm.boot-dex2oat-threads",
+                                                  dex2oat_threads_buf,
+                                                  NULL) > 0;
+    char dex2oat_threads_arg[kPropertyValueMax + 2];
+    if (have_dex2oat_threads_flag) {
+        sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);
+    }
+
+    char dex2oat_isa_features_key[kPropertyKeyMax];
+    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
+    char dex2oat_isa_features[kPropertyValueMax];
+    bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,
+                                                  dex2oat_isa_features, NULL) > 0;
+
+    char dex2oat_isa_variant_key[kPropertyKeyMax];
+    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
+    char dex2oat_isa_variant[kPropertyValueMax];
+    bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,
+                                                 dex2oat_isa_variant, NULL) > 0;
+
+    const char *dex2oat_norelocation = "-Xnorelocate";
+    bool have_dex2oat_relocation_skip_flag = false;
+
+    char dex2oat_flags[kPropertyValueMax];
+    int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",
+                                 dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);
+    ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);
+
+    // If we booting without the real /data, don't spend time compiling.
+    char vold_decrypt[kPropertyValueMax];
+    bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;
+    bool skip_compilation = (have_vold_decrypt &&
+                             (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
+                             (strcmp(vold_decrypt, "1") == 0)));
+
+    bool generate_debug_info = property_get_bool("debug.generate-debug-info", false);
+
+    char app_image_format[kPropertyValueMax];
+    char image_format_arg[strlen("--image-format=") + kPropertyValueMax];
+    bool have_app_image_format =
+            image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+    if (have_app_image_format) {
+        sprintf(image_format_arg, "--image-format=%s", app_image_format);
+    }
+
+    char dex2oat_large_app_threshold[kPropertyValueMax];
+    bool have_dex2oat_large_app_threshold =
+            get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;
+    char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];
+    if (have_dex2oat_large_app_threshold) {
+        sprintf(dex2oat_large_app_threshold_arg,
+                "--very-large-app-threshold=%s",
+                dex2oat_large_app_threshold);
+    }
+
+    static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
+
+    static const char* RUNTIME_ARG = "--runtime-arg";
+
+    static const int MAX_INT_LEN = 12;      // '-'+10dig+'\0' -OR- 0x+8dig
+
+    char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN];
+    char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
+    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];
+    char instruction_set_variant_arg[strlen("--instruction-set-variant=") + kPropertyValueMax];
+    char instruction_set_features_arg[strlen("--instruction-set-features=") + kPropertyValueMax];
+    char dex2oat_Xms_arg[strlen("-Xms") + kPropertyValueMax];
+    char dex2oat_Xmx_arg[strlen("-Xmx") + kPropertyValueMax];
+    char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + kPropertyValueMax];
+    bool have_dex2oat_swap_fd = false;
+    char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN];
+    bool have_dex2oat_image_fd = false;
+    char dex2oat_image_fd[strlen("--app-image-fd=") + MAX_INT_LEN];
+
+    sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
+    sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
+    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);
+    sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);
+    sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);
+    if (swap_fd >= 0) {
+        have_dex2oat_swap_fd = true;
+        sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);
+    }
+    if (image_fd >= 0) {
+        have_dex2oat_image_fd = true;
+        sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);
+    }
+
+    if (have_dex2oat_Xms_flag) {
+        sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);
+    }
+    if (have_dex2oat_Xmx_flag) {
+        sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
+    }
+
+    // Compute compiler filter.
+
+    bool have_dex2oat_compiler_filter_flag;
+    if (skip_compilation) {
+        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
+        have_dex2oat_compiler_filter_flag = true;
+        have_dex2oat_relocation_skip_flag = true;
+    } else if (vm_safe_mode) {
+        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
+        have_dex2oat_compiler_filter_flag = true;
+    } else if (compiler_filter != nullptr &&
+            strlen(compiler_filter) + strlen("--compiler-filter=") <
+                    arraysize(dex2oat_compiler_filter_arg)) {
+        sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
+        have_dex2oat_compiler_filter_flag = true;
+    } else {
+        char dex2oat_compiler_filter_flag[kPropertyValueMax];
+        have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
+                                                         dex2oat_compiler_filter_flag, NULL) > 0;
+        if (have_dex2oat_compiler_filter_flag) {
+            sprintf(dex2oat_compiler_filter_arg,
+                    "--compiler-filter=%s",
+                    dex2oat_compiler_filter_flag);
+        }
+    }
+
+    // Check whether all apps should be compiled debuggable.
+    if (!debuggable) {
+        char prop_buf[kPropertyValueMax];
+        debuggable =
+                (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&
+                (prop_buf[0] == '1');
+    }
+    char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];
+    if (profile_fd != -1) {
+        sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);
+    }
+
+
+    ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
+
+    const char* argv[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)
+                     + (have_dex2oat_Xmx_flag ? 2 : 0)
+                     + (have_dex2oat_compiler_filter_flag ? 1 : 0)
+                     + (have_dex2oat_threads_flag ? 1 : 0)
+                     + (have_dex2oat_swap_fd ? 1 : 0)
+                     + (have_dex2oat_image_fd ? 1 : 0)
+                     + (have_dex2oat_relocation_skip_flag ? 2 : 0)
+                     + (generate_debug_info ? 1 : 0)
+                     + (debuggable ? 1 : 0)
+                     + (have_app_image_format ? 1 : 0)
+                     + dex2oat_flags_count
+                     + (profile_fd == -1 ? 0 : 1)
+                     + (shared_libraries != nullptr ? 4 : 0)
+                     + (have_dex2oat_large_app_threshold ? 1 : 0)];
+    int i = 0;
+    argv[i++] = DEX2OAT_BIN;
+    argv[i++] = zip_fd_arg;
+    argv[i++] = zip_location_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;
+    if (have_dex2oat_isa_variant) {
+        argv[i++] = instruction_set_variant_arg;
+    }
+    if (have_dex2oat_isa_features) {
+        argv[i++] = instruction_set_features_arg;
+    }
+    if (have_dex2oat_Xms_flag) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = dex2oat_Xms_arg;
+    }
+    if (have_dex2oat_Xmx_flag) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = dex2oat_Xmx_arg;
+    }
+    if (have_dex2oat_compiler_filter_flag) {
+        argv[i++] = dex2oat_compiler_filter_arg;
+    }
+    if (have_dex2oat_threads_flag) {
+        argv[i++] = dex2oat_threads_arg;
+    }
+    if (have_dex2oat_swap_fd) {
+        argv[i++] = dex2oat_swap_fd;
+    }
+    if (have_dex2oat_image_fd) {
+        argv[i++] = dex2oat_image_fd;
+    }
+    if (generate_debug_info) {
+        argv[i++] = "--generate-debug-info";
+    }
+    if (debuggable) {
+        argv[i++] = "--debuggable";
+    }
+    if (have_app_image_format) {
+        argv[i++] = image_format_arg;
+    }
+    if (have_dex2oat_large_app_threshold) {
+        argv[i++] = dex2oat_large_app_threshold_arg;
+    }
+    if (dex2oat_flags_count) {
+        i += split(dex2oat_flags, argv + i);
+    }
+    if (have_dex2oat_relocation_skip_flag) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = dex2oat_norelocation;
+    }
+    if (profile_fd != -1) {
+        argv[i++] = profile_arg;
+    }
+    if (shared_libraries != nullptr) {
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = "-classpath";
+        argv[i++] = RUNTIME_ARG;
+        argv[i++] = shared_libraries;
+    }
+    // Do not add after dex2oat_flags, they should override others for debugging.
+    argv[i] = NULL;
+
+    execv(DEX2OAT_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
+}
+
+/*
+ * Whether dexopt should use a swap file when compiling an APK.
+ *
+ * If kAlwaysProvideSwapFile, do this on all devices (dex2oat will make a more informed decision
+ * itself, anyways).
+ *
+ * Otherwise, read "dalvik.vm.dex2oat-swap". If the property exists, return whether it is "true".
+ *
+ * Otherwise, return true if this is a low-mem device.
+ *
+ * Otherwise, return default value.
+ */
+static bool kAlwaysProvideSwapFile = false;
+static bool kDefaultProvideSwapFile = true;
+
+static bool ShouldUseSwapFileForDexopt() {
+    if (kAlwaysProvideSwapFile) {
+        return true;
+    }
+
+    // Check the "override" property. If it exists, return value == "true".
+    char dex2oat_prop_buf[kPropertyValueMax];
+    if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) {
+        if (strcmp(dex2oat_prop_buf, "true") == 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // Shortcut for default value. This is an implementation optimization for the process sketched
+    // above. If the default value is true, we can avoid to check whether this is a low-mem device,
+    // as low-mem is never returning false. The compiler will optimize this away if it can.
+    if (kDefaultProvideSwapFile) {
+        return true;
+    }
+
+    bool is_low_mem = property_get_bool("ro.config.low_ram", false);
+    if (is_low_mem) {
+        return true;
+    }
+
+    // Default value must be false here.
+    return kDefaultProvideSwapFile;
+}
+
+static void SetDex2OatScheduling(bool set_to_bg) {
+    if (set_to_bg) {
+        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
+            ALOGE("set_sched_policy failed: %s\n", strerror(errno));
+            exit(70);
+        }
+        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+            ALOGE("setpriority failed: %s\n", strerror(errno));
+            exit(71);
+        }
+    }
+}
+
+static void close_all_fds(const std::vector<fd_t>& fds, const char* description) {
+    for (size_t i = 0; i < fds.size(); i++) {
+        if (close(fds[i]) != 0) {
+            PLOG(WARNING) << "Failed to close fd for " << description << " at index " << i;
+        }
+    }
+}
+
+static fd_t open_profile_dir(const std::string& profile_dir) {
+    fd_t profile_dir_fd = TEMP_FAILURE_RETRY(open(profile_dir.c_str(),
+            O_PATH | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
+    if (profile_dir_fd < 0) {
+        // In a multi-user environment, these directories can be created at
+        // different points and it's possible we'll attempt to open a profile
+        // dir before it exists.
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Failed to open profile_dir: " << profile_dir;
+        }
+    }
+    return profile_dir_fd;
+}
+
+static fd_t open_primary_profile_file_from_dir(const std::string& profile_dir, mode_t open_mode) {
+    fd_t profile_dir_fd  = open_profile_dir(profile_dir);
+    if (profile_dir_fd < 0) {
+        return -1;
+    }
+
+    fd_t profile_fd = -1;
+    std::string profile_file = create_primary_profile(profile_dir);
+
+    profile_fd = TEMP_FAILURE_RETRY(open(profile_file.c_str(), open_mode | O_NOFOLLOW));
+    if (profile_fd == -1) {
+        // It's not an error if the profile file does not exist.
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Failed to lstat profile_dir: " << profile_dir;
+        }
+    }
+    // TODO(calin): use AutoCloseFD instead of closing the fd manually.
+    if (close(profile_dir_fd) != 0) {
+        PLOG(WARNING) << "Could not close profile dir " << profile_dir;
+    }
+    return profile_fd;
+}
+
+static fd_t open_primary_profile_file(userid_t user, const char* pkgname) {
+    std::string profile_dir = create_data_user_profile_package_path(user, pkgname);
+    return open_primary_profile_file_from_dir(profile_dir, O_RDONLY);
+}
+
+static fd_t open_reference_profile(uid_t uid, const char* pkgname, bool read_write) {
+    std::string reference_profile_dir = create_data_ref_profile_package_path(pkgname);
+    int flags = read_write ? O_RDWR | O_CREAT : O_RDONLY;
+    fd_t fd = open_primary_profile_file_from_dir(reference_profile_dir, flags);
+    if (fd < 0) {
+        return -1;
+    }
+    if (read_write) {
+        // Fix the owner.
+        if (fchown(fd, uid, uid) < 0) {
+            close(fd);
+            return -1;
+        }
+    }
+    return fd;
+}
+
+static void open_profile_files(uid_t uid, const char* pkgname,
+            /*out*/ std::vector<fd_t>* profiles_fd, /*out*/ fd_t* reference_profile_fd) {
+    // Open the reference profile in read-write mode as profman might need to save the merge.
+    *reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ true);
+    if (*reference_profile_fd < 0) {
+        // We can't access the reference profile file.
+        return;
+    }
+
+    std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
+    for (auto user : users) {
+        fd_t profile_fd = open_primary_profile_file(user, pkgname);
+        // Add to the lists only if both fds are valid.
+        if (profile_fd >= 0) {
+            profiles_fd->push_back(profile_fd);
+        }
+    }
+}
+
+static void drop_capabilities(uid_t uid) {
+    if (setgid(uid) != 0) {
+        ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
+        exit(64);
+    }
+    if (setuid(uid) != 0) {
+        ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
+        exit(65);
+    }
+    // drop capabilities
+    struct __user_cap_header_struct capheader;
+    struct __user_cap_data_struct capdata[2];
+    memset(&capheader, 0, sizeof(capheader));
+    memset(&capdata, 0, sizeof(capdata));
+    capheader.version = _LINUX_CAPABILITY_VERSION_3;
+    if (capset(&capheader, &capdata[0]) < 0) {
+        ALOGE("capset failed: %s\n", strerror(errno));
+        exit(66);
+    }
+}
+
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+
+static void run_profman_merge(const std::vector<fd_t>& profiles_fd, fd_t reference_profile_fd) {
+    static const size_t MAX_INT_LEN = 32;
+    static const char* PROFMAN_BIN = "/system/bin/profman";
+
+    std::vector<std::string> profile_args(profiles_fd.size());
+    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
+    for (size_t k = 0; k < profiles_fd.size(); k++) {
+        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k]);
+        profile_args[k].assign(profile_buf);
+    }
+    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
+    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd);
+
+    // program name, reference profile fd, the final NULL and the profile fds
+    const char* argv[3 + profiles_fd.size()];
+    int i = 0;
+    argv[i++] = PROFMAN_BIN;
+    argv[i++] = reference_profile_arg;
+    for (size_t k = 0; k < profile_args.size(); k++) {
+        argv[i++] = profile_args[k].c_str();
+    }
+    // Do not add after dex2oat_flags, they should override others for debugging.
+    argv[i] = NULL;
+
+    execv(PROFMAN_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+    exit(68);   /* only get here on exec failure */
+}
+
+// Decides if profile guided compilation is needed or not based on existing profiles.
+// Returns true if there is enough information in the current profiles that worth
+// a re-compilation of the package.
+// If the return value is true all the current profiles would have been merged into
+// the reference profiles accessible with open_reference_profile().
+bool analyse_profiles(uid_t uid, const char* pkgname) {
+    std::vector<fd_t> profiles_fd;
+    fd_t reference_profile_fd = -1;
+    open_profile_files(uid, pkgname, &profiles_fd, &reference_profile_fd);
+    if (profiles_fd.empty() || (reference_profile_fd == -1)) {
+        // Skip profile guided compilation because no profiles were found.
+        // Or if the reference profile info couldn't be opened.
+        close_all_fds(profiles_fd, "profiles_fd");
+        if ((reference_profile_fd != - 1) && (close(reference_profile_fd) != 0)) {
+            PLOG(WARNING) << "Failed to close fd for reference profile";
+        }
+        return false;
+    }
+
+    ALOGV("PROFMAN (MERGE): --- BEGIN '%s' ---\n", pkgname);
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+        run_profman_merge(profiles_fd, reference_profile_fd);
+        exit(68);   /* only get here on exec failure */
+    }
+    /* parent */
+    int return_code = wait_child(pid);
+    bool need_to_compile = false;
+    bool should_clear_current_profiles = false;
+    bool should_clear_reference_profile = false;
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "profman failed for package " << pkgname << ": " << return_code;
+    } else {
+        return_code = WEXITSTATUS(return_code);
+        switch (return_code) {
+            case PROFMAN_BIN_RETURN_CODE_COMPILE:
+                need_to_compile = true;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = false;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION:
+                need_to_compile = false;
+                should_clear_current_profiles = false;
+                should_clear_reference_profile = false;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_BAD_PROFILES:
+                LOG(WARNING) << "Bad profiles for package " << pkgname;
+                need_to_compile = false;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = true;
+                break;
+            case PROFMAN_BIN_RETURN_CODE_ERROR_IO:  // fall-through
+            case PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING:
+                // Temporary IO problem (e.g. locking). Ignore but log a warning.
+                LOG(WARNING) << "IO error while reading profiles for package " << pkgname;
+                need_to_compile = false;
+                should_clear_current_profiles = false;
+                should_clear_reference_profile = false;
+                break;
+           default:
+                // Unknown return code or error. Unlink profiles.
+                LOG(WARNING) << "Unknown error code while processing profiles for package " << pkgname
+                        << ": " << return_code;
+                need_to_compile = false;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = true;
+                break;
+        }
+    }
+    close_all_fds(profiles_fd, "profiles_fd");
+    if (close(reference_profile_fd) != 0) {
+        PLOG(WARNING) << "Failed to close fd for reference profile";
+    }
+    if (should_clear_current_profiles) {
+        clear_current_profiles(pkgname);
+    }
+    if (should_clear_reference_profile) {
+        clear_reference_profile(pkgname);
+    }
+    return need_to_compile;
+}
+
+static void run_profman_dump(const std::vector<fd_t>& profile_fds,
+                             fd_t reference_profile_fd,
+                             const std::vector<std::string>& dex_locations,
+                             const std::vector<fd_t>& apk_fds,
+                             fd_t output_fd) {
+    std::vector<std::string> profman_args;
+    static const char* PROFMAN_BIN = "/system/bin/profman";
+    profman_args.push_back(PROFMAN_BIN);
+    profman_args.push_back("--dump-only");
+    profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd));
+    if (reference_profile_fd != -1) {
+        profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d",
+                                            reference_profile_fd));
+    }
+    for (fd_t profile_fd : profile_fds) {
+        profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fd));
+    }
+    for (const std::string& dex_location : dex_locations) {
+        profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str()));
+    }
+    for (fd_t apk_fd : apk_fds) {
+        profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fd));
+    }
+    const char **argv = new const char*[profman_args.size() + 1];
+    size_t i = 0;
+    for (const std::string& profman_arg : profman_args) {
+        argv[i++] = profman_arg.c_str();
+    }
+    argv[i] = NULL;
+
+    execv(PROFMAN_BIN, (char * const *)argv);
+    ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno));
+    exit(68);   /* only get here on exec failure */
+}
+
+static const char* get_location_from_path(const char* path) {
+    static constexpr char kLocationSeparator = '/';
+    const char *location = strrchr(path, kLocationSeparator);
+    if (location == NULL) {
+        return path;
+    } else {
+        // Skip the separator character.
+        return location + 1;
+    }
+}
+
+bool dump_profiles(int32_t uid, const char* pkgname, const char* code_paths) {
+    std::vector<fd_t> profile_fds;
+    fd_t reference_profile_fd = -1;
+    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname);
+
+    ALOGV("PROFMAN (DUMP): --- BEGIN '%s' ---\n", pkgname);
+
+    open_profile_files(uid, pkgname, &profile_fds, &reference_profile_fd);
+
+    const bool has_reference_profile = (reference_profile_fd != -1);
+    const bool has_profiles = !profile_fds.empty();
+
+    if (!has_reference_profile && !has_profiles) {
+        ALOGE("profman dump: no profiles to dump for '%s'", pkgname);
+        return false;
+    }
+
+    fd_t output_fd = open(out_file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW);
+    if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+        ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
+        return false;
+    }
+    std::vector<std::string> code_full_paths = base::Split(code_paths, ";");
+    std::vector<std::string> dex_locations;
+    std::vector<fd_t> apk_fds;
+    for (const std::string& code_full_path : code_full_paths) {
+        const char* full_path = code_full_path.c_str();
+        fd_t apk_fd = open(full_path, O_RDONLY | O_NOFOLLOW);
+        if (apk_fd == -1) {
+            ALOGE("installd cannot open '%s'\n", full_path);
+            return false;
+        }
+        dex_locations.push_back(get_location_from_path(full_path));
+        apk_fds.push_back(apk_fd);
+    }
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+        run_profman_dump(profile_fds, reference_profile_fd, dex_locations,
+                         apk_fds, output_fd);
+        exit(68);   /* only get here on exec failure */
+    }
+    /* parent */
+    close_all_fds(apk_fds, "apk_fds");
+    close_all_fds(profile_fds, "profile_fds");
+    if (close(reference_profile_fd) != 0) {
+        PLOG(WARNING) << "Failed to close fd for reference profile";
+    }
+    int return_code = wait_child(pid);
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "profman failed for package " << pkgname << ": "
+                << return_code;
+        return false;
+    }
+    return true;
+}
+
+static std::string replace_file_extension(const std::string& oat_path, const std::string& new_ext) {
+  // A standard dalvik-cache entry. Replace ".dex" with `new_ext`.
+  if (EndsWith(oat_path, ".dex")) {
+    std::string new_path = oat_path;
+    new_path.replace(new_path.length() - strlen(".dex"), strlen(".dex"), new_ext);
+    CHECK(EndsWith(new_path, new_ext.c_str()));
+    return new_path;
+  }
+
+  // An odex entry. Not that this may not be an extension, e.g., in the OTA
+  // case (where the base name will have an extension for the B artifact).
+  size_t odex_pos = oat_path.rfind(".odex");
+  if (odex_pos != std::string::npos) {
+    std::string new_path = oat_path;
+    new_path.replace(odex_pos, strlen(".odex"), new_ext);
+    CHECK_NE(new_path.find(new_ext), std::string::npos);
+    return new_path;
+  }
+
+  // Don't know how to handle this.
+  return "";
+}
+
+// Translate the given oat path to an art (app image) path. An empty string
+// denotes an error.
+static std::string create_image_filename(const std::string& oat_path) {
+    return replace_file_extension(oat_path, ".art");
+}
+
+// Translate the given oat path to a vdex path. An empty string denotes an error.
+static std::string create_vdex_filename(const std::string& oat_path) {
+    return replace_file_extension(oat_path, ".vdex");
+}
+
+static bool add_extension_to_file_name(char* file_name, const char* extension) {
+    if (strlen(file_name) + strlen(extension) + 1 > PKG_PATH_MAX) {
+        return false;
+    }
+    strcat(file_name, extension);
+    return true;
+}
+
+static int open_output_file(const char* file_name, bool recreate, int permissions) {
+    int flags = O_RDWR | O_CREAT;
+    if (recreate) {
+        if (unlink(file_name) < 0) {
+            if (errno != ENOENT) {
+                PLOG(ERROR) << "open_output_file: Couldn't unlink " << file_name;
+            }
+        }
+        flags |= O_EXCL;
+    }
+    return open(file_name, flags, permissions);
+}
+
+static bool set_permissions_and_ownership(int fd, bool is_public, int uid, const char* path) {
+    if (fchmod(fd,
+               S_IRUSR|S_IWUSR|S_IRGRP |
+               (is_public ? S_IROTH : 0)) < 0) {
+        ALOGE("installd cannot chmod '%s' during dexopt\n", path);
+        return false;
+    } else if (fchown(fd, AID_SYSTEM, uid) < 0) {
+        ALOGE("installd cannot chown '%s' during dexopt\n", path);
+        return false;
+    }
+    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.
+    // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run
+    // without a swap file, if necessary. Reference profiles file also add an extra ".prof"
+    // extension to the cache path (5 bytes).
+    if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
+        ALOGE("apk_path too long '%s'\n", apk_path);
+        return false;
+    }
+
+    if (!IsOutputDalvikCache(oat_dir)) {
+        if (validate_apk_path(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)) {
+            return false;
+        }
+    } else {
+        if (!create_cache_path(out_oat_path, apk_path, instruction_set)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
+// on destruction. It will also run the given cleanup (unless told not to) after closing.
+//
+// Usage example:
+//
+//   Dex2oatFileWrapper<std::function<void ()>> file(open(...),
+//                                                   [name]() {
+//                                                       unlink(name.c_str());
+//                                                   });
+//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
+//            wrapper if captured as a reference.
+//
+//   if (file.get() == -1) {
+//       // Error opening...
+//   }
+//
+//   ...
+//   if (error) {
+//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
+//       // and delete the file (after the fd is closed).
+//       return -1;
+//   }
+//
+//   (Success case)
+//   file.SetCleanup(false);
+//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
+//   // (leaving the file around; after the fd is closed).
+//
+template <typename Cleanup>
+class Dex2oatFileWrapper {
+ public:
+    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true) {
+    }
+
+    Dex2oatFileWrapper(int value, Cleanup cleanup)
+            : value_(value), cleanup_(cleanup), do_cleanup_(true) {}
+
+    ~Dex2oatFileWrapper() {
+        reset(-1);
+    }
+
+    int get() {
+        return value_;
+    }
+
+    void SetCleanup(bool cleanup) {
+        do_cleanup_ = cleanup;
+    }
+
+    void reset(int new_value) {
+        if (value_ >= 0) {
+            close(value_);
+        }
+        if (do_cleanup_ && cleanup_ != nullptr) {
+            cleanup_();
+        }
+
+        value_ = new_value;
+    }
+
+    void reset(int new_value, Cleanup new_cleanup) {
+        if (value_ >= 0) {
+            close(value_);
+        }
+        if (do_cleanup_ && cleanup_ != nullptr) {
+            cleanup_();
+        }
+
+        value_ = new_value;
+        cleanup_ = new_cleanup;
+    }
+
+ private:
+    int value_;
+    Cleanup cleanup_;
+    bool do_cleanup_;
+};
+
+int dexopt(const char* apk_path, uid_t uid, const char* pkgname, const char* instruction_set,
+        int dexopt_needed, const char* oat_dir, int dexopt_flags,const char* compiler_filter,
+        const char* volume_uuid ATTRIBUTE_UNUSED, const char* shared_libraries) {
+    bool is_public = ((dexopt_flags & DEXOPT_PUBLIC) != 0);
+    bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
+    bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
+    bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
+    bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+
+    CHECK(pkgname != nullptr);
+    CHECK(pkgname[0] != 0);
+
+    // Public apps should not be compiled with profile information ever. Same goes for the special
+    // package '*' used for the system server.
+    Dex2oatFileWrapper<std::function<void ()>> reference_profile_fd;
+    if (!is_public && pkgname[0] != '*') {
+        // Open reference profile in read only mode as dex2oat does not get write permissions.
+        const std::string pkgname_str(pkgname);
+        reference_profile_fd.reset(open_reference_profile(uid, pkgname, /*read_write*/ false),
+                                   [pkgname_str]() {
+                                       clear_reference_profile(pkgname_str.c_str());
+                                   });
+        // Note: it's OK to not find a profile here.
+    }
+
+    if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
+        LOG_FATAL("dexopt flags contains unknown fields\n");
+    }
+
+    char out_oat_path[PKG_PATH_MAX];
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_oat_path)) {
+        return false;
+    }
+
+    const char *input_file = apk_path;
+    struct stat input_stat;
+    memset(&input_stat, 0, sizeof(input_stat));
+    stat(input_file, &input_stat);
+
+    // Open the input file.
+    base::unique_fd input_fd(open(input_file, O_RDONLY, 0));
+    if (input_fd.get() < 0) {
+        ALOGE("installd cannot open '%s' for input during dexopt\n", input_file);
+        return -1;
+    }
+
+    // Create the output OAT file.
+    const std::string out_oat_path_str(out_oat_path);
+    Dex2oatFileWrapper<std::function<void ()>> out_oat_fd(
+            open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
+            [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
+    if (out_oat_fd.get() < 0) {
+        ALOGE("installd cannot open '%s' for output during dexopt\n", out_oat_path);
+        return -1;
+    }
+    if (!set_permissions_and_ownership(out_oat_fd.get(), is_public, uid, out_oat_path)) {
+        return -1;
+    }
+
+    // Open the existing VDEX. We do this before creating the new output VDEX, which will
+    // unlink the old one.
+    char in_odex_path[PKG_PATH_MAX];
+    int dexopt_action = abs(dexopt_needed);
+    bool is_odex_location = dexopt_needed < 0;
+    base::unique_fd in_vdex_fd;
+    std::string in_vdex_path_str;
+    if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
+        // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
+        const char* path = nullptr;
+        if (is_odex_location) {
+            if (calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) {
+                path = in_odex_path;
+            } else {
+                ALOGE("installd cannot compute input vdex location for '%s'\n", apk_path);
+                return -1;
+            }
+        } else {
+            path = out_oat_path;
+        }
+        in_vdex_path_str = create_vdex_filename(path);
+        if (in_vdex_path_str.empty()) {
+            ALOGE("installd cannot compute input vdex location for '%s'\n", path);
+            return -1;
+        }
+        if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) {
+            // When we dex2oat because iof boot image change, we are going to update
+            // in-place the vdex file.
+            in_vdex_fd.reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
+        } else {
+            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()) {
+        return -1;
+    }
+    Dex2oatFileWrapper<std::function<void ()>> out_vdex_wrapper_fd;
+    int out_vdex_fd = -1;
+
+    // If we are compiling because the boot image is out of date, we do not
+    // need to recreate a vdex, and can use the same existing one.
+    if (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE &&
+            in_vdex_fd != -1 &&
+            in_vdex_path_str == out_vdex_path_str) {
+        out_vdex_fd = in_vdex_fd;
+    } else {
+        out_vdex_wrapper_fd.reset(
+              open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
+              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        out_vdex_fd = out_vdex_wrapper_fd.get();
+        if (out_vdex_fd < 0) {
+            ALOGE("installd cannot open '%s' for output during dexopt\n", out_vdex_path_str.c_str());
+            return -1;
+        }
+    }
+    if (!set_permissions_and_ownership(out_vdex_fd, is_public,
+                uid, out_vdex_path_str.c_str())) {
+        return -1;
+    }
+
+    // Create a swap file if necessary.
+    base::unique_fd swap_fd;
+    if (ShouldUseSwapFileForDexopt()) {
+        // Make sure there really is enough space.
+        char swap_file_name[PKG_PATH_MAX];
+        strcpy(swap_file_name, out_oat_path);
+        if (add_extension_to_file_name(swap_file_name, ".swap")) {
+            swap_fd.reset(open_output_file(swap_file_name, /*recreate*/true, /*permissions*/0600));
+        }
+        if (swap_fd.get() < 0) {
+            // Could not create swap file. Optimistically go on and hope that we can compile
+            // without it.
+            ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name);
+        } else {
+            // Immediately unlink. We don't really want to hit flash.
+            if (unlink(swap_file_name) < 0) {
+                PLOG(ERROR) << "Couldn't unlink swap file " << swap_file_name;
+            }
+        }
+    }
+
+    // Avoid generating an app image for extract only since it will not contain any classes.
+    Dex2oatFileWrapper<std::function<void ()>> image_fd;
+    const std::string image_path = create_image_filename(out_oat_path);
+    if (!image_path.empty()) {
+        char app_image_format[kPropertyValueMax];
+        bool have_app_image_format =
+                get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
+        // Use app images only if it is enabled (by a set image format) and we are compiling
+        // profile-guided (so the app image doesn't conservatively contain all classes).
+        if (profile_guided && have_app_image_format) {
+            // Recreate is true since we do not want to modify a mapped image. If the app is
+            // already running and we modify the image file, it can cause crashes (b/27493510).
+            image_fd.reset(open_output_file(image_path.c_str(),
+                                            true /*recreate*/,
+                                            0600 /*permissions*/),
+                           [image_path]() { unlink(image_path.c_str()); }
+                           );
+            if (image_fd.get() < 0) {
+                // Could not create application image file. Go on since we can compile without
+                // it.
+                LOG(ERROR) << "installd could not create '"
+                        << image_path
+                        << "' for image file during dexopt";
+            } else if (!set_permissions_and_ownership(image_fd.get(),
+                                                      is_public,
+                                                      uid,
+                                                      image_path.c_str())) {
+                image_fd.reset(-1);
+            }
+        }
+        // If we have a valid image file path but no image fd, explicitly erase the image file.
+        if (image_fd.get() < 0) {
+            if (unlink(image_path.c_str()) < 0) {
+                if (errno != ENOENT) {
+                    PLOG(ERROR) << "Couldn't unlink image file " << image_path;
+                }
+            }
+        }
+    }
+
+    ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file);
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+
+        SetDex2OatScheduling(boot_complete);
+        if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
+            ALOGE("flock(%s) failed: %s\n", out_oat_path, strerror(errno));
+            _exit(67);
+        }
+
+        // Pass dex2oat the relative path to the input file.
+        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,
+                    image_fd.get(),
+                    input_file_name,
+                    out_oat_path,
+                    swap_fd.get(),
+                    instruction_set,
+                    compiler_filter,
+                    vm_safe_mode,
+                    debuggable,
+                    boot_complete,
+                    reference_profile_fd.get(),
+                    shared_libraries);
+        _exit(68);   /* only get here on exec failure */
+    } else {
+        int res = wait_child(pid);
+        if (res == 0) {
+            ALOGV("DexInv: --- END '%s' (success) ---\n", input_file);
+        } else {
+            ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res);
+            return -1;
+        }
+    }
+
+    struct utimbuf ut;
+    ut.actime = input_stat.st_atime;
+    ut.modtime = input_stat.st_mtime;
+    utime(out_oat_path, &ut);
+
+    // We've been successful, don't delete output.
+    out_oat_fd.SetCleanup(false);
+    out_vdex_wrapper_fd.SetCleanup(false);
+    image_fd.SetCleanup(false);
+    reference_profile_fd.SetCleanup(false);
+
+    return 0;
+}
+
+// Helper for move_ab, so that we can have common failure-case cleanup.
+static bool unlink_and_rename(const char* from, const char* to) {
+    // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
+    // return a failure.
+    struct stat s;
+    if (stat(to, &s) == 0) {
+        if (!S_ISREG(s.st_mode)) {
+            LOG(ERROR) << from << " is not a regular file to replace for A/B.";
+            return false;
+        }
+        if (unlink(to) != 0) {
+            LOG(ERROR) << "Could not unlink " << to << " to move A/B.";
+            return false;
+        }
+    } else {
+        // This may be a permission problem. We could investigate the error code, but we'll just
+        // let the rename failure do the work for us.
+    }
+
+    // Try to rename "to" to "from."
+    if (rename(from, to) != 0) {
+        PLOG(ERROR) << "Could not rename " << from << " to " << to;
+        return false;
+    }
+    return true;
+}
+
+// Move/rename a B artifact (from) to an A artifact (to).
+static bool move_ab_path(const std::string& b_path, const std::string& a_path) {
+    // Check whether B exists.
+    {
+        struct stat s;
+        if (stat(b_path.c_str(), &s) != 0) {
+            // Silently ignore for now. The service calling this isn't smart enough to understand
+            // lack of artifacts at the moment.
+            return false;
+        }
+        if (!S_ISREG(s.st_mode)) {
+            LOG(ERROR) << "A/B artifact " << b_path << " is not a regular file.";
+            // Try to unlink, but swallow errors.
+            unlink(b_path.c_str());
+            return false;
+        }
+    }
+
+    // Rename B to A.
+    if (!unlink_and_rename(b_path.c_str(), a_path.c_str())) {
+        // Delete the b_path so we don't try again (or fail earlier).
+        if (unlink(b_path.c_str()) != 0) {
+            PLOG(ERROR) << "Could not unlink " << b_path;
+        }
+
+        return false;
+    }
+
+    return true;
+}
+
+bool move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+    // Get the current slot suffix. No suffix, no A/B.
+    std::string slot_suffix;
+    {
+        char buf[kPropertyValueMax];
+        if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
+            return false;
+        }
+        slot_suffix = buf;
+
+        if (!ValidateTargetSlotSuffix(slot_suffix)) {
+            LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
+            return false;
+        }
+    }
+
+    // Validate other inputs.
+    if (validate_apk_path(apk_path) != 0) {
+        LOG(ERROR) << "Invalid apk_path: " << apk_path;
+        return false;
+    }
+    if (validate_apk_path(oat_dir) != 0) {
+        LOG(ERROR) << "Invalid oat_dir: " << oat_dir;
+        return false;
+    }
+
+    char a_path[PKG_PATH_MAX];
+    if (!calculate_oat_file_path(a_path, oat_dir, apk_path, instruction_set)) {
+        return false;
+    }
+    const std::string a_vdex_path = create_vdex_filename(a_path);
+    const std::string a_image_path = create_image_filename(a_path);
+
+    // B path = A path + slot suffix.
+    const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
+    const std::string b_vdex_path = StringPrintf("%s.%s", a_vdex_path.c_str(), slot_suffix.c_str());
+    const std::string b_image_path = StringPrintf("%s.%s",
+                                                  a_image_path.c_str(),
+                                                  slot_suffix.c_str());
+
+    bool success = true;
+    if (move_ab_path(b_path, a_path)) {
+        if (move_ab_path(b_vdex_path, a_vdex_path)) {
+            // Note: we can live without an app image. As such, ignore failure to move the image file.
+            //       If we decide to require the app image, or the app image being moved correctly,
+            //       then change accordingly.
+            constexpr bool kIgnoreAppImageFailure = true;
+
+            if (!a_image_path.empty()) {
+                if (!move_ab_path(b_image_path, a_image_path)) {
+                    unlink(a_image_path.c_str());
+                    if (!kIgnoreAppImageFailure) {
+                        success = false;
+                    }
+                }
+            }
+        } else {
+            // Cleanup: delete B image, ignore errors.
+            unlink(b_image_path.c_str());
+            success = false;
+        }
+    } else {
+        // Cleanup: delete B image, ignore errors.
+        unlink(b_vdex_path.c_str());
+        unlink(b_image_path.c_str());
+        success = false;
+    }
+    return success;
+}
+
+bool delete_odex(const char* apk_path, const char* instruction_set, const char* oat_dir) {
+    // Delete the oat/odex file.
+    char out_path[PKG_PATH_MAX];
+    if (!create_oat_out_path(apk_path, instruction_set, oat_dir, out_path)) {
+        return false;
+    }
+
+    // In case of a permission failure report the issue. Otherwise just print a warning.
+    auto unlink_and_check = [](const char* path) -> bool {
+        int result = unlink(path);
+        if (result != 0) {
+            if (errno == EACCES || errno == EPERM) {
+                PLOG(ERROR) << "Could not unlink " << path;
+                return false;
+            }
+            PLOG(WARNING) << "Could not unlink " << path;
+        }
+        return true;
+    };
+
+    // Delete the oat/odex file.
+    bool return_value_oat = unlink_and_check(out_path);
+
+    // Derive and delete the app image.
+    bool return_value_art = unlink_and_check(create_image_filename(out_path).c_str());
+
+    // Report success.
+    return return_value_oat && return_value_art;
+}
+
+int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
+    return dexopt(params[0],                    // apk_path
+                  atoi(params[1]),              // uid
+                  params[2],                    // pkgname
+                  params[3],                    // instruction_set
+                  atoi(params[4]),              // dexopt_needed
+                  params[5],                    // oat_dir
+                  atoi(params[6]),              // dexopt_flags
+                  params[7],                    // compiler_filter
+                  parse_null(params[8]),        // volume_uuid
+                  parse_null(params[9]));       // shared_libraries
+    static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param count");
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
new file mode 100644
index 0000000..1115c78
--- /dev/null
+++ b/cmds/installd/dexopt.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef DEXOPT_H_
+#define DEXOPT_H_
+
+#include <sys/types.h>
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace installd {
+
+/* dexopt needed flags matching those in dalvik.system.DexFile */
+static constexpr int DEX2OAT_FROM_SCRATCH        = 1;
+static constexpr int DEX2OAT_FOR_BOOT_IMAGE      = 2;
+static constexpr int DEX2OAT_FOR_FILTER          = 3;
+static constexpr int DEX2OAT_FOR_RELOCATION      = 4;
+static constexpr int PATCHOAT_FOR_RELOCATION     = 5;
+
+typedef int fd_t;
+
+bool clear_reference_profile(const char* pkgname);
+bool clear_current_profile(const char* pkgname, userid_t user);
+bool clear_current_profiles(const char* pkgname);
+
+bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
+
+bool analyse_profiles(uid_t uid, const char* pkgname);
+bool dump_profiles(int32_t uid, const char* pkgname, const char* code_paths);
+
+bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
+
+int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
+        int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+        const char* volume_uuid, const char* shared_libraries);
+
+static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
+static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
+
+// Helper for the above, converting arguments.
+int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // DEXOPT_H_
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index 93e1ce5..edcdb6a 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -14,19 +14,17 @@
 ** limitations under the License.
 */
 
+#define LOG_TAG "installd"
+
 #include <stdlib.h>
 #include <string.h>
 
-#include <cutils/log.h>               // TODO: Move everything to base::logging.
+#include <log/log.h>              // TODO: Move everything to base::logging.
 
 #include <globals.h>
 #include <installd_constants.h>
 #include <utils.h>
 
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-
 namespace android {
 namespace installd {
 
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index c90beec..8242eec 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -20,6 +20,8 @@
 
 #include <inttypes.h>
 
+#include "InstalldNativeService.h"
+
 namespace android {
 namespace installd {
 
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index c81a339..35936a2 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -13,6 +13,7 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
+#define LOG_TAG "installd"
 
 #include <fcntl.h>
 #include <selinux/android.h>
@@ -20,30 +21,19 @@
 #include <sys/capability.h>
 #include <sys/fsuid.h>
 #include <sys/prctl.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 
 #include <android-base/logging.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>               // TODO: Move everything to base::logging.
 #include <cutils/properties.h>
-#include <cutils/sockets.h>
+#include <log/log.h>              // TODO: Move everything to base::logging.
 #include <private/android_filesystem_config.h>
 
-#include <commands.h>
-#include <globals.h>
-#include <installd_constants.h>
-#include <installd_deps.h>  // Need to fill in requirements of commands.
-#include <utils.h>
-
-#ifndef LOG_TAG
-#define LOG_TAG "installd"
-#endif
-#define SOCKET_PATH "installd"
-
-#define BUFFER_MAX    1024  /* input buffer for commands */
-#define TOKEN_MAX     16    /* max number of arguments in buffer */
-#define REPLY_MAX     256   /* largest reply allowed */
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "installd_constants.h"
+#include "installd_deps.h"  // Need to fill in requirements of commands.
+#include "utils.h"
 
 namespace android {
 namespace installd {
@@ -70,12 +60,12 @@
 
     file_name_start = strrchr(apk_path, '/');
     if (file_name_start == NULL) {
-        ALOGE("apk_path '%s' has no '/'s in it\n", apk_path);
+        SLOGE("apk_path '%s' has no '/'s in it\n", apk_path);
         return false;
     }
     file_name_end = strrchr(apk_path, '.');
     if (file_name_end < file_name_start) {
-        ALOGE("apk_path '%s' has no extension\n", apk_path);
+        SLOGE("apk_path '%s' has no extension\n", apk_path);
         return false;
     }
 
@@ -101,14 +91,14 @@
                               const char *instruction_set) {
     if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set)
             + strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) {
-        ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
+        SLOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
         return false;
     }
 
     strcpy(path, apk_path);
     char *end = strrchr(path, '/');
     if (end == NULL) {
-        ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
+        SLOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
         return false;
     }
     const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/');
@@ -118,7 +108,7 @@
     strcat(path, apk_end);         // path = /system/framework/oat/<isa>/whatever.jar\0
     end = strrchr(path, '.');
     if (end == NULL) {
-        ALOGE("apk_path '%s' has no extension.\n", apk_path);
+        SLOGE("apk_path '%s' has no extension.\n", apk_path);
         return false;
     }
     strcpy(end + 1, "odex");
@@ -174,404 +164,15 @@
     return true;
 }
 
-
-static char* parse_null(char* arg) {
-    if (strcmp(arg, "!") == 0) {
-        return nullptr;
-    } else {
-        return arg;
-    }
-}
-
-static int do_ping(char **arg ATTRIBUTE_UNUSED, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return 0;
-}
-
-static int do_create_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags,
-            appid_t appid, const char* seinfo, int target_sdk_version */
-    return create_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]),
-                           atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_restorecon_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char* uuid, const char* pkgName, userid_t userid, int flags,
-            appid_t appid, const char* seinfo */
-    return restorecon_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atoi(arg[4]), arg[5]);
-}
-
-static int do_migrate_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags */
-    return migrate_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_clear_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
-    return clear_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-static int do_destroy_app_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char *uuid, const char *pkgname, userid_t userid, int flags, ino_t ce_data_inode */
-    return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]));
-}
-
-// We use otapreopt_chroot to get into the chroot.
-static constexpr const char* kOtaPreopt = "/system/bin/otapreopt_chroot";
-
-static int do_ota_dexopt(const char* args[DEXOPT_PARAM_COUNT],
-                         char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    // Time to fork and run otapreopt.
-
-    // Check that the tool exists.
-    struct stat s;
-    if (stat(kOtaPreopt, &s) != 0) {
-        LOG(ERROR) << "Otapreopt chroot tool not found.";
-        return -1;
-    }
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
-        argv[0] = kOtaPreopt;
-
-        for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
-            argv[i + 1] = args[i];
-        }
-
-        argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
-
-        execv(argv[0], (char * const *)argv);
-        PLOG(ERROR) << "execv(OTAPREOPT_CHROOT) failed";
-        exit(99);
-    } else {
-        int res = wait_child(pid);
-        if (res == 0) {
-            ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
-        } else {
-            ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
-        }
-        return res;
-    }
-}
-
-static int do_regular_dexopt(const char* args[DEXOPT_PARAM_COUNT],
-                             char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    return dexopt(args);
-}
-
-using DexoptFn = int (*)(const char* args[DEXOPT_PARAM_COUNT],
-                         char reply[REPLY_MAX]);
-
-static int do_dexopt(char **arg, char reply[REPLY_MAX])
-{
-    const char* args[DEXOPT_PARAM_COUNT];
-    for (size_t i = 0; i < DEXOPT_PARAM_COUNT; ++i) {
-        CHECK(arg[i] != nullptr);
-        args[i] = arg[i];
-    }
-
-    int dexopt_flags = atoi(arg[6]);
-    DexoptFn dexopt_fn;
-    if ((dexopt_flags & DEXOPT_OTA) != 0) {
-        dexopt_fn = do_ota_dexopt;
-    } else {
-        dexopt_fn = do_regular_dexopt;
-    }
-    return dexopt_fn(args, reply);
-}
-
-static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
-{
-    uid_t uid = static_cast<uid_t>(atoi(arg[0]));
-    const char* pkgname = arg[1];
-    if (merge_profiles(uid, pkgname)) {
-        strncpy(reply, "true", REPLY_MAX);
-    } else {
-        strncpy(reply, "false", REPLY_MAX);
-    }
-    return 0;
-}
-
-static int do_dump_profiles(char **arg, char reply[REPLY_MAX])
-{
-    uid_t uid = static_cast<uid_t>(atoi(arg[0]));
-    const char* pkgname = arg[1];
-    const char* dex_files = arg[2];
-    if (dump_profile(uid, pkgname, dex_files)) {
-        strncpy(reply, "true", REPLY_MAX);
-    } else {
-        strncpy(reply, "false", REPLY_MAX);
-    }
-    return 0;
-}
-
-static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return mark_boot_complete(arg[0] /* instruction set */);
-}
-
-static int do_rm_dex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return rm_dex(arg[0], arg[1]); /* pkgname, instruction_set */
-}
-
-static int do_free_cache(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) /* TODO int:free_size */
-{
-    return free_cache(parse_null(arg[0]), (int64_t)atoll(arg[1])); /* uuid, free_size */
-}
-
-static int do_get_app_size(char **arg, char reply[REPLY_MAX]) {
-    int64_t codesize = 0;
-    int64_t datasize = 0;
-    int64_t cachesize = 0;
-    int64_t asecsize = 0;
-    int res = 0;
-
-    /* const char *uuid, const char *pkgname, int userid, int flags, ino_t ce_data_inode,
-            const char* code_path */
-    res = get_app_size(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), atol(arg[4]),
-            arg[5], &codesize, &datasize, &cachesize, &asecsize);
-
-    /*
-     * Each int64_t can take up 22 characters printed out. Make sure it
-     * doesn't go over REPLY_MAX in the future.
-     */
-    snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64,
-            codesize, datasize, cachesize, asecsize);
-    return res;
-}
-
-static int do_get_app_data_inode(char **arg, char reply[REPLY_MAX]) {
-    ino_t inode = 0;
-    int res = 0;
-
-    /* const char *uuid, const char *pkgname, int userid, int flags */
-    res = get_app_data_inode(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), &inode);
-
-    snprintf(reply, REPLY_MAX, "%" PRId64, (int64_t) inode);
-    return res;
-}
-
-static int do_move_complete_app(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    /* const char* from_uuid, const char *to_uuid, const char *package_name,
-            const char *data_app_name, appid_t appid, const char* seinfo,
-            int target_sdk_version */
-    return move_complete_app(parse_null(arg[0]), parse_null(arg[1]), arg[2], arg[3],
-                             atoi(arg[4]), arg[5], atoi(arg[6]));
-}
-
-static int do_create_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* const char *uuid, userid_t userid, int user_serial, int flags */
-    return create_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]), atoi(arg[3]));
-}
-
-static int do_destroy_user_data(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* const char *uuid, userid_t userid, int flags */
-    return destroy_user_data(parse_null(arg[0]), atoi(arg[1]), atoi(arg[2]));
-}
-
-static int do_linklib(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return linklib(parse_null(arg[0]), arg[1], arg[2], atoi(arg[3]));
-}
-
-static int do_idmap(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    return idmap(arg[0], arg[1], atoi(arg[2]));
-}
-
-static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* oat_dir, instruction_set */
-    return create_oat_dir(arg[0], arg[1]);
-}
-
-static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* oat_dir */
-    return rm_package_dir(arg[0]);
-}
-
-static int do_clear_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* package_name */
-    return clear_app_profiles(arg[0]);
-}
-
-static int do_destroy_app_profiles(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* package_name */
-    return destroy_app_profiles(arg[0]);
-}
-
-static int do_link_file(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
-{
-    /* relative_path, from_base, to_base */
-    return link_file(arg[0], arg[1], arg[2]);
-}
-
-static int do_move_ab(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    // apk_path, instruction_set, oat_dir
-    return move_ab(arg[0], arg[1], arg[2]);
-}
-
-static int do_delete_odex(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
-    // apk_path, instruction_set, oat_dir
-    return delete_odex(arg[0], arg[1], arg[2]) ? 0 : -1;
-}
-
-struct cmdinfo {
-    const char *name;
-    unsigned numargs;
-    int (*func)(char **arg, char reply[REPLY_MAX]);
-};
-
-struct cmdinfo cmds[] = {
-    { "ping",                 0, do_ping },
-
-    { "create_app_data",      7, do_create_app_data },
-    { "restorecon_app_data",  6, do_restorecon_app_data },
-    { "migrate_app_data",     4, do_migrate_app_data },
-    { "clear_app_data",       5, do_clear_app_data },
-    { "destroy_app_data",     5, do_destroy_app_data },
-    { "move_complete_app",    7, do_move_complete_app },
-    { "get_app_size",         6, do_get_app_size },
-    { "get_app_data_inode",   4, do_get_app_data_inode },
-
-    { "create_user_data",     4, do_create_user_data },
-    { "destroy_user_data",    3, do_destroy_user_data },
-
-    { "dexopt",              10, do_dexopt },
-    { "markbootcomplete",     1, do_mark_boot_complete },
-    { "rmdex",                2, do_rm_dex },
-    { "freecache",            2, do_free_cache },
-    { "linklib",              4, do_linklib },
-    { "idmap",                3, do_idmap },
-    { "createoatdir",         2, do_create_oat_dir },
-    { "rmpackagedir",         1, do_rm_package_dir },
-    { "clear_app_profiles",   1, do_clear_app_profiles },
-    { "destroy_app_profiles", 1, do_destroy_app_profiles },
-    { "linkfile",             3, do_link_file },
-    { "move_ab",              3, do_move_ab },
-    { "merge_profiles",       2, do_merge_profiles },
-    { "dump_profiles",        3, do_dump_profiles },
-    { "delete_odex",          3, do_delete_odex },
-};
-
-static int readx(int s, void *_buf, int count)
-{
-    char *buf = (char *) _buf;
-    int n = 0, r;
-    if (count < 0) return -1;
-    while (n < count) {
-        r = read(s, buf + n, count - n);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            ALOGE("read error: %s\n", strerror(errno));
-            return -1;
-        }
-        if (r == 0) {
-            ALOGE("eof\n");
-            return -1; /* EOF */
-        }
-        n += r;
-    }
-    return 0;
-}
-
-static int writex(int s, const void *_buf, int count)
-{
-    const char *buf = (const char *) _buf;
-    int n = 0, r;
-    if (count < 0) return -1;
-    while (n < count) {
-        r = write(s, buf + n, count - n);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            ALOGE("write error: %s\n", strerror(errno));
-            return -1;
-        }
-        n += r;
-    }
-    return 0;
-}
-
-
-/* Tokenize the command buffer, locate a matching command,
- * ensure that the required number of arguments are provided,
- * call the function(), return the result.
- */
-static int execute(int s, char cmd[BUFFER_MAX])
-{
-    char reply[REPLY_MAX];
-    char *arg[TOKEN_MAX+1];
-    unsigned i;
-    unsigned n = 0;
-    unsigned short count;
-    int ret = -1;
-
-    // ALOGI("execute('%s')\n", cmd);
-
-        /* default reply is "" */
-    reply[0] = 0;
-
-        /* n is number of args (not counting arg[0]) */
-    arg[0] = cmd;
-    while (*cmd) {
-        if (isspace(*cmd)) {
-            *cmd++ = 0;
-            n++;
-            arg[n] = cmd;
-            if (n == TOKEN_MAX) {
-                ALOGE("too many arguments\n");
-                goto done;
-            }
-        }
-        if (*cmd) {
-          cmd++;
-        }
-    }
-
-    for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
-        if (!strcmp(cmds[i].name,arg[0])) {
-            if (n != cmds[i].numargs) {
-                ALOGE("%s requires %d arguments (%d given)\n",
-                     cmds[i].name, cmds[i].numargs, n);
-            } else {
-                ret = cmds[i].func(arg + 1, reply);
-            }
-            goto done;
-        }
-    }
-    ALOGE("unsupported command '%s'\n", arg[0]);
-
-done:
-    if (reply[0]) {
-        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
-    } else {
-        n = snprintf(cmd, BUFFER_MAX, "%d", ret);
-    }
-    if (n > BUFFER_MAX) n = BUFFER_MAX;
-    count = n;
-
-    // ALOGI("reply: '%s'\n", cmd);
-    if (writex(s, &count, sizeof(count))) return -1;
-    if (writex(s, cmd, count)) return -1;
-    return 0;
-}
-
 static bool initialize_globals() {
     const char* data_path = getenv("ANDROID_DATA");
     if (data_path == nullptr) {
-        ALOGE("Could not find ANDROID_DATA");
+        SLOGE("Could not find ANDROID_DATA");
         return false;
     }
     const char* root_path = getenv("ANDROID_ROOT");
     if (root_path == nullptr) {
-        ALOGE("Could not find ANDROID_ROOT");
+        SLOGE("Could not find ANDROID_ROOT");
         return false;
     }
 
@@ -597,12 +198,12 @@
     }
 
     if (ensure_config_user_dirs(0) == -1) {
-        ALOGE("Failed to setup misc for user 0");
+        SLOGE("Failed to setup misc for user 0");
         goto fail;
     }
 
     if (version == 2) {
-        ALOGD("Upgrading to /data/misc/user directories");
+        SLOGD("Upgrading to /data/misc/user directories");
 
         char misc_dir[PATH_MAX];
         snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path);
@@ -643,12 +244,12 @@
                 gid_t gid = uid;
                 if (access(keychain_added_dir, F_OK) == 0) {
                     if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) {
-                        ALOGE("Some files failed to copy");
+                        SLOGE("Some files failed to copy");
                     }
                 }
                 if (access(keychain_removed_dir, F_OK) == 0) {
                     if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) {
-                        ALOGE("Some files failed to copy");
+                        SLOGE("Some files failed to copy");
                     }
                 }
             }
@@ -668,7 +269,7 @@
     // Persist layout version if changed
     if (version != oldVersion) {
         if (fs_write_atomic_int(version_path, version) == -1) {
-            ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+            SLOGE("Failed to save version to %s: %s", version_path, strerror(errno));
             goto fail;
         }
     }
@@ -702,80 +303,41 @@
 }
 
 static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
-    char buf[BUFFER_MAX];
-    struct sockaddr addr;
-    socklen_t alen;
-    int lsocket, s;
+    int ret;
     int selinux_enabled = (is_selinux_enabled() > 0);
 
     setenv("ANDROID_LOG_TAGS", "*:v", 1);
     android::base::InitLogging(argv);
 
-    ALOGI("installd firing up\n");
+    SLOGI("installd firing up");
 
     union selinux_callback cb;
     cb.func_log = log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
     if (!initialize_globals()) {
-        ALOGE("Could not initialize globals; exiting.\n");
+        SLOGE("Could not initialize globals; exiting.\n");
         exit(1);
     }
 
     if (initialize_directories() < 0) {
-        ALOGE("Could not create directories; exiting.\n");
+        SLOGE("Could not create directories; exiting.\n");
         exit(1);
     }
 
     if (selinux_enabled && selinux_status_open(true) < 0) {
-        ALOGE("Could not open selinux status; exiting.\n");
+        SLOGE("Could not open selinux status; exiting.\n");
         exit(1);
     }
 
-    lsocket = android_get_control_socket(SOCKET_PATH);
-    if (lsocket < 0) {
-        ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
+    if ((ret = InstalldNativeService::start()) != android::OK) {
+        SLOGE("Unable to start InstalldNativeService: %d", ret);
         exit(1);
     }
-    if (listen(lsocket, 5)) {
-        ALOGE("Listen on socket failed: %s\n", strerror(errno));
-        exit(1);
-    }
-    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
 
-    for (;;) {
-        alen = sizeof(addr);
-        s = accept(lsocket, &addr, &alen);
-        if (s < 0) {
-            ALOGE("Accept failed: %s\n", strerror(errno));
-            continue;
-        }
-        fcntl(s, F_SETFD, FD_CLOEXEC);
+    IPCThreadState::self()->joinThreadPool();
 
-        ALOGI("new connection\n");
-        for (;;) {
-            unsigned short count;
-            if (readx(s, &count, sizeof(count))) {
-                ALOGE("failed to read size\n");
-                break;
-            }
-            if ((count < 1) || (count >= BUFFER_MAX)) {
-                ALOGE("invalid size %d\n", count);
-                break;
-            }
-            if (readx(s, buf, count)) {
-                ALOGE("failed to read command\n");
-                break;
-            }
-            buf[count] = 0;
-            if (selinux_enabled && selinux_status_updated() > 0) {
-                selinux_android_seapp_context_reload();
-            }
-            if (execute(s, buf)) break;
-        }
-        ALOGI("closing connection\n");
-        close(s);
-    }
+    LOG(INFO) << "installd shutting down";
 
     return 0;
 }
diff --git a/cmds/installd/installd.rc b/cmds/installd/installd.rc
index 5e4c925..d5d5236 100644
--- a/cmds/installd/installd.rc
+++ b/cmds/installd/installd.rc
@@ -1,3 +1,2 @@
 service installd /system/bin/installd
     class main
-    socket installd stream 600 system system
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index b0bcce9..401e581 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -42,7 +42,6 @@
 constexpr int DEXOPT_DEBUGGABLE     = 1 << 3;
 constexpr int DEXOPT_BOOTCOMPLETE   = 1 << 4;
 constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
-constexpr int DEXOPT_OTA            = 1 << 6;
 
 /* all known values for dexopt flags */
 constexpr int DEXOPT_MASK =
@@ -50,8 +49,7 @@
     | DEXOPT_SAFEMODE
     | DEXOPT_DEBUGGABLE
     | DEXOPT_BOOTCOMPLETE
-    | DEXOPT_PROFILE_GUIDED
-    | DEXOPT_OTA;
+    | DEXOPT_PROFILE_GUIDED;
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py
new file mode 100644
index 0000000..b37352b
--- /dev/null
+++ b/cmds/installd/matchgen.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 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.
+
+import collections
+
+TYPES = {
+    "AID_MEDIA_AUDIO": ["aac","aac","amr","awb","snd","flac","flac","mp3","mpga","mpega","mp2","m4a","aif","aiff","aifc","gsm","mka","m3u","wma","wax","ra","rm","ram","ra","pls","sd2","wav","ogg","oga"],
+    "AID_MEDIA_VIDEO": ["3gpp","3gp","3gpp2","3g2","avi","dl","dif","dv","fli","m4v","ts","mpeg","mpg","mpe","mp4","vob","qt","mov","mxu","webm","lsf","lsx","mkv","mng","asf","asx","wm","wmv","wmx","wvx","movie","wrf"],
+    "AID_MEDIA_IMAGE": ["bmp","gif","jpg","jpeg","jpe","pcx","png","svg","svgz","tiff","tif","wbmp","webp","dng","cr2","ras","art","jng","nef","nrw","orf","rw2","pef","psd","pnm","pbm","pgm","ppm","srw","arw","rgb","xbm","xpm","xwd"]
+}
+
+print """/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/******************************************************************
+ * THIS CODE WAS GENERATED BY matchgen.py, DO NOT MODIFY DIRECTLY *
+ ******************************************************************/
+
+#include <private/android_filesystem_config.h>
+
+int MatchExtension(const char* ext) {
+"""
+
+trie = collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(lambda: ""))))))
+
+for t in TYPES:
+    for v in TYPES[t]:
+        v = v.lower()
+        target = trie
+        for c in v:
+            target = target[c]
+        target["\0"] = t
+
+def dump(target, index):
+    prefix = "    " * (index + 1)
+    print "%sswitch (ext[%d]) {" % (prefix, index)
+    for k in sorted(target.keys()):
+        if k == "\0":
+            print "%scase '\\0': return %s;" % (prefix, target[k])
+        else:
+            upper = k.upper()
+            if k != upper:
+                print "%scase '%s': case '%s':" % (prefix, k, upper)
+            else:
+                print "%scase '%s':" % (prefix, k)
+            dump(target[k], index + 1)
+    print "%s}" % (prefix)
+
+dump(trie, 0)
+
+print """
+    return 0;
+}
+"""
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 7e02b6b..c74c65b 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -32,17 +32,18 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
-#include <commands.h>
-#include <file_parsing.h>
-#include <globals.h>
-#include <installd_deps.h>  // Need to fill in requirements of commands.
-#include <otapreopt_utils.h>
-#include <system_properties.h>
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "dexopt.h"
+#include "file_parsing.h"
+#include "globals.h"
+#include "installd_deps.h"  // Need to fill in requirements of commands.
+#include "otapreopt_utils.h"
+#include "system_properties.h"
+#include "utils.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 5ea89e6..cec8f68 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -25,8 +25,9 @@
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
-#include <commands.h>
-#include <otapreopt_utils.h>
+#include "installd_constants.h"
+#include "otapreopt_utils.h"
+#include "dexopt.h"
 
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 447c8bd..a32df22 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -5,8 +5,8 @@
     srcs: ["installd_utils_test.cpp"],
     shared_libs: [
         "libbase",
-        "libutils",
         "liblog",
+        "libutils",
         "libcutils",
     ],
     static_libs: [
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 9b2de88..947cc0d 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -19,9 +19,9 @@
 
 #include <gtest/gtest.h>
 
-#include <commands.h>
-#include <globals.h>
-#include <utils.h>
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "utils.h"
 
 #undef LOG_TAG
 #define LOG_TAG "utils_test"
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c838993..0b1cd7e 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -18,9 +18,11 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <fts.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <sys/xattr.h>
 
 #if defined(__APPLE__)
 #include <sys/mount.h>
@@ -31,7 +33,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
 #include "globals.h"  // extern variables.
@@ -39,7 +41,9 @@
 #ifndef LOG_TAG
 #define LOG_TAG "installd"
 #endif
+
 #define CACHE_NOISY(x) //x
+#define DEBUG_XATTRS 0
 
 using android::base::StringPrintf;
 
@@ -50,7 +54,7 @@
  * Check that given string is valid filename, and that it attempts no
  * parent or child directory traversal.
  */
-static bool is_valid_filename(const std::string& name) {
+bool is_valid_filename(const std::string& name) {
     if (name.empty() || (name == ".") || (name == "..")
             || (name.find('/') != std::string::npos)) {
         return false;
@@ -61,7 +65,7 @@
 
 static void check_package_name(const char* package_name) {
     CHECK(is_valid_filename(package_name));
-    CHECK(is_valid_package_name(package_name) == 0);
+    CHECK(is_valid_package_name(package_name));
 }
 
 /**
@@ -105,10 +109,12 @@
         while ((ent = readdir(dir))) {
             if (ent->d_ino == ce_data_inode) {
                 auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
+#if DEBUG_XATTRS
                 if (resolved != fallback) {
                     LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
                             << " instead of " << fallback;
                 }
+#endif
                 closedir(dir);
                 return resolved;
             }
@@ -130,7 +136,7 @@
 
 int create_pkg_path(char path[PKG_PATH_MAX], const char *pkgname,
         const char *postfix, userid_t userid) {
-    if (is_valid_package_name(pkgname) != 0) {
+    if (!is_valid_package_name(pkgname)) {
         path[0] = '\0';
         return -1;
     }
@@ -193,17 +199,31 @@
     return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid);
 }
 
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name) {
+    return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name);
+}
+
+std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
+        const char* data_type, const char* package_name) {
+    return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(),
+            data_type, package_name);
+}
+
 std::string create_data_misc_legacy_path(userid_t userid) {
     return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid);
 }
 
-std::string create_data_user_profiles_path(userid_t userid) {
+std::string create_data_user_profile_path(userid_t userid) {
     return StringPrintf("%s/cur/%u", android_profiles_dir.path, userid);
 }
 
 std::string create_data_user_profile_package_path(userid_t user, const char* package_name) {
     check_package_name(package_name);
-    return StringPrintf("%s/%s",create_data_user_profiles_path(user).c_str(), package_name);
+    return StringPrintf("%s/%s",create_data_user_profile_path(user).c_str(), package_name);
+}
+
+std::string create_data_ref_profile_path() {
+    return StringPrintf("%s/ref", android_profiles_dir.path);
 }
 
 std::string create_data_ref_profile_package_path(const char* package_name) {
@@ -211,6 +231,21 @@
     return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name);
 }
 
+std::string create_data_dalvik_cache_path() {
+    return "/data/dalvik-cache";
+}
+
+std::string create_data_misc_foreign_dex_path(userid_t userid) {
+    return StringPrintf("/data/misc/profiles/cur/%d/foreign-dex", userid);
+}
+
+// Keep profile paths in sync with ActivityThread.
+constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof";
+
+std::string create_primary_profile(const std::string& profile_dir) {
+    return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
+}
+
 std::vector<userid_t> get_known_users(const char* volume_uuid) {
     std::vector<userid_t> users;
 
@@ -243,6 +278,59 @@
     return users;
 }
 
+int calculate_tree_size(const std::string& path, int64_t* size,
+        int32_t include_gid, int32_t exclude_gid, bool exclude_apps) {
+    FTS *fts;
+    FTSENT *p;
+    int64_t matchedSize = 0;
+    char *argv[] = { (char*) path.c_str(), nullptr };
+    if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) {
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Failed to fts_open " << path;
+        }
+        return -1;
+    }
+    while ((p = fts_read(fts)) != NULL) {
+        switch (p->fts_info) {
+        case FTS_D:
+        case FTS_DEFAULT:
+        case FTS_F:
+        case FTS_SL:
+        case FTS_SLNONE:
+            int32_t uid = p->fts_statp->st_uid;
+            int32_t gid = p->fts_statp->st_gid;
+            int32_t user_uid = multiuser_get_app_id(uid);
+            int32_t user_gid = multiuser_get_app_id(gid);
+            if (exclude_apps && ((user_uid >= AID_APP_START && user_uid <= AID_APP_END)
+                    || (user_gid >= AID_CACHE_GID_START && user_gid <= AID_CACHE_GID_END)
+                    || (user_gid >= AID_SHARED_GID_START && user_gid <= AID_SHARED_GID_END))) {
+                // Don't traverse inside or measure
+                fts_set(fts, p, FTS_SKIP);
+                break;
+            }
+            if (include_gid != -1 && gid != include_gid) {
+                break;
+            }
+            if (exclude_gid != -1 && gid == exclude_gid) {
+                break;
+            }
+            matchedSize += (p->fts_statp->st_blocks * 512);
+            break;
+        }
+    }
+    fts_close(fts);
+#if MEASURE_DEBUG
+    if ((include_gid == -1) && (exclude_gid == -1)) {
+        LOG(DEBUG) << "Measured " << path << " size " << matchedSize;
+    } else {
+        LOG(DEBUG) << "Measured " << path << " size " << matchedSize << "; include " << include_gid
+                << " exclude " << exclude_gid;
+    }
+#endif
+    *size += matchedSize;
+    return 0;
+}
+
 int create_move_path(char path[PKG_PATH_MAX],
     const char* pkgname,
     const char* leaf,
@@ -261,12 +349,13 @@
  * Checks whether the package name is valid. Returns -1 on error and
  * 0 on success.
  */
-int is_valid_package_name(const char* pkgname) {
+bool is_valid_package_name(const std::string& packageName) {
+    const char* pkgname = packageName.c_str();
     const char *x = pkgname;
     int alpha = -1;
 
     if (strlen(pkgname) > PKG_NAME_MAX) {
-        return -1;
+        return false;
     }
 
     while (*x) {
@@ -276,7 +365,7 @@
             if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) {
                     /* periods must not be first, last, or doubled */
                 ALOGE("invalid package name '%s'\n", pkgname);
-                return -1;
+                return false;
             }
         } else if (*x == '-') {
             /* Suffix -X is fine to let versioning of packages.
@@ -285,7 +374,7 @@
         } else {
                 /* anything not A-Z, a-z, 0-9, _, or . is invalid */
             ALOGE("invalid package name '%s'\n", pkgname);
-            return -1;
+            return false;
         }
 
         x++;
@@ -297,13 +386,13 @@
         while (*x) {
             if (!isalnum(*x)) {
                 ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
-                return -1;
+                return false;
             }
             x++;
         }
     }
 
-    return 0;
+    return true;
 }
 
 static int _delete_dir_contents(DIR *d,
@@ -551,7 +640,7 @@
         if (res == NULL) {
             return NULL;
         }
-        CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %d", res, len));
+        CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %zu", res, len));
         // Link it into our list of blocks, not disrupting the current one.
         if (cache->memBlocks == NULL) {
             *(void**)res = NULL;
@@ -576,7 +665,7 @@
         cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE;
         nextPos = res + len;
     }
-    CACHE_NOISY(ALOGI("cache_malloc: ret %p size %d, block=%p, nextPos=%p",
+    CACHE_NOISY(ALOGI("cache_malloc: ret %p size %zu, block=%p, nextPos=%p",
             res, len, cache->memBlocks, nextPos));
     cache->curMemBlockAvail = nextPos;
     return res;
@@ -654,7 +743,7 @@
             cache->availFiles = newAvail;
             cache->files = newFiles;
         }
-        CACHE_NOISY(ALOGI("Setting file %p at position %d in array %p", file,
+        CACHE_NOISY(ALOGI("Setting file %p at position %zd in array %p", file,
                 cache->numFiles, cache->files));
         cache->files[cache->numFiles] = file;
         cache->numFiles++;
@@ -779,6 +868,99 @@
     return 0;
 }
 
+int get_path_inode(const std::string& path, ino_t *inode) {
+    struct stat buf;
+    memset(&buf, 0, sizeof(buf));
+    if (stat(path.c_str(), &buf) != 0) {
+        PLOG(WARNING) << "Failed to stat " << path;
+        return -1;
+    } else {
+        *inode = buf.st_ino;
+        return 0;
+    }
+}
+
+/**
+ * Write the inode of a specific child file into the given xattr on the
+ * parent directory. This allows you to find the child later, even if its
+ * name is encrypted.
+ */
+int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr) {
+    ino_t inode = 0;
+    uint64_t inode_raw = 0;
+    auto path = StringPrintf("%s/%s", parent.c_str(), name);
+
+    if (get_path_inode(path, &inode) != 0) {
+        // Path probably doesn't exist yet; ignore
+        return 0;
+    }
+
+    // Check to see if already set correctly
+    if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) {
+        if (inode_raw == inode) {
+            // Already set correctly; skip writing
+            return 0;
+        } else {
+            PLOG(WARNING) << "Mismatched inode value; found " << inode
+                    << " on disk but marked value was " << inode_raw << "; overwriting";
+        }
+    }
+
+    inode_raw = inode;
+    if (setxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw), 0) != 0 && errno != EOPNOTSUPP) {
+        PLOG(ERROR) << "Failed to write xattr " << inode_xattr << " at " << parent;
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+/**
+ * Read the inode of a specific child file from the given xattr on the
+ * parent directory. Returns a currently valid path for that child, which
+ * might have an encrypted name.
+ */
+std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr) {
+    ino_t inode = 0;
+    uint64_t inode_raw = 0;
+    auto fallback = StringPrintf("%s/%s", parent.c_str(), name);
+
+    // Lookup the inode value written earlier
+    if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) {
+        inode = inode_raw;
+    }
+
+    // For testing purposes, rely on the inode when defined; this could be
+    // optimized to use access() in the future.
+    if (inode != 0) {
+        DIR* dir = opendir(parent.c_str());
+        if (dir == nullptr) {
+            PLOG(ERROR) << "Failed to opendir " << parent;
+            return fallback;
+        }
+
+        struct dirent* ent;
+        while ((ent = readdir(dir))) {
+            if (ent->d_ino == inode) {
+                auto resolved = StringPrintf("%s/%s", parent.c_str(), ent->d_name);
+#if DEBUG_XATTRS
+                if (resolved != fallback) {
+                    LOG(DEBUG) << "Resolved path " << resolved << " for inode " << inode
+                            << " instead of " << fallback;
+                }
+#endif
+                closedir(dir);
+                return resolved;
+            }
+        }
+        LOG(WARNING) << "Failed to resolve inode " << inode << "; using " << fallback;
+        closedir(dir);
+        return fallback;
+    } else {
+        return fallback;
+    }
+}
+
 void add_cache_files(cache_t* cache, const std::string& data_path) {
     DIR *d;
     struct dirent *de;
@@ -796,7 +978,6 @@
         if (de->d_type == DT_DIR) {
             DIR* subdir;
             const char *name = de->d_name;
-            char* pathpos;
 
                 /* always skip "." and ".." */
             if (name[0] == '.') {
@@ -804,16 +985,9 @@
                 if ((name[1] == '.') && (name[2] == 0)) continue;
             }
 
-            strcpy(dirname, basepath);
-            pathpos = dirname + strlen(dirname);
-            if ((*(pathpos-1)) != '/') {
-                *pathpos = '/';
-                pathpos++;
-                *pathpos = 0;
-            }
-
-            // TODO: also try searching using xattr when CE is locked
-            snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/cache", name);
+            auto parent = StringPrintf("%s/%s", basepath, name);
+            auto resolved = read_path_inode(parent, "cache", kXattrInodeCache);
+            strcpy(dirname, resolved.c_str());
             CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname));
 
             subdir = opendir(dirname);
@@ -931,16 +1105,16 @@
 {
     CACHE_NOISY(size_t i;)
 
-    CACHE_NOISY(ALOGI("clear_cache_files: %d dirs, %d files\n", cache->numDirs, cache->numFiles));
+    CACHE_NOISY(ALOGI("clear_cache_files: %zu dirs, %zu files\n", cache->numDirs, cache->numFiles));
     CACHE_NOISY(
         for (i=0; i<cache->numDirs; i++) {
             cache_dir_t* dir = cache->dirs[i];
-            ALOGI("dir #%d: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
+            ALOGI("dir #%zu: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
         })
     CACHE_NOISY(
         for (i=0; i<cache->numFiles; i++) {
             cache_file_t* file = cache->files[i];
-            ALOGI("file #%d: %p %s time=%d dir=%p\n", i, file, file->name,
+            ALOGI("file #%zu: %p %s time=%d dir=%p\n", i, file, file->name,
                     (int)file->modTime, file->dir);
         })
     void* block = cache->memBlocks;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 60df356..f2f0cbb 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -30,6 +30,9 @@
 
 #include <installd_constants.h>
 
+#define MEASURE_DEBUG 0
+#define MEASURE_EXTERNAL 0
+
 namespace android {
 namespace installd {
 
@@ -62,6 +65,9 @@
     int8_t* curMemBlockEnd;
 } cache_t;
 
+constexpr const char* kXattrInodeCache = "user.inode_cache";
+constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache";
+
 int create_pkg_path(char path[PKG_PATH_MAX],
                     const char *pkgname,
                     const char *postfix,
@@ -70,7 +76,6 @@
 std::string create_data_path(const char* volume_uuid);
 
 std::string create_data_app_path(const char* volume_uuid);
-
 std::string create_data_app_package_path(const char* volume_uuid, const char* package_name);
 
 std::string create_data_user_ce_path(const char* volume_uuid, userid_t userid);
@@ -84,15 +89,28 @@
         userid_t user, const char* package_name);
 
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
+std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
+std::string create_data_media_package_path(const char* volume_uuid, userid_t userid,
+        const char* data_type, const char* package_name);
 
 std::string create_data_misc_legacy_path(userid_t userid);
 
-std::string create_data_user_profiles_path(userid_t userid);
+std::string create_data_user_profile_path(userid_t userid);
 std::string create_data_user_profile_package_path(userid_t user, const char* package_name);
+
+std::string create_data_ref_profile_path();
 std::string create_data_ref_profile_package_path(const char* package_name);
 
+std::string create_data_dalvik_cache_path();
+std::string create_data_misc_foreign_dex_path(userid_t userid);
+
+std::string create_primary_profile(const std::string& profile_dir);
+
 std::vector<userid_t> get_known_users(const char* volume_uuid);
 
+int calculate_tree_size(const std::string& path, int64_t* size,
+        int32_t include_gid = -1, int32_t exclude_gid = -1, bool exclude_apps = false);
+
 int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
 
 int create_move_path(char path[PKG_PATH_MAX],
@@ -100,7 +118,8 @@
                      const char* leaf,
                      userid_t userid);
 
-int is_valid_package_name(const char* pkgname);
+bool is_valid_filename(const std::string& name);
+bool is_valid_package_name(const std::string& packageName);
 
 int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false);
 int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false);
@@ -118,6 +137,11 @@
 
 cache_t* start_cache_collection();
 
+int get_path_inode(const std::string& path, ino_t *inode);
+
+int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
+std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
+
 void add_cache_files(cache_t* cache, const std::string& data_path);
 
 void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size);
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index 75b907c..3b8955b 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -14,22 +14,22 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "ip-up-vpn"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <linux/route.h>
+#include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-
-#include <arpa/inet.h>
-#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <linux/if.h>
-#include <linux/route.h>
 
-#define LOG_TAG "ip-up-vpn"
-#include <cutils/log.h>
+#include <log/log.h>
 
 #define DIR "/data/misc/vpn/"
 
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 27c461a..753aeb5 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -1,14 +1,18 @@
 /* Copyright 2008 The Android Open Source Project
  */
 
+#define LOG_TAG "Binder"
+
+#include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <sys/mman.h>
+#include <unistd.h>
+
+#include <log/log.h>
 
 #include "binder.h"
 
@@ -16,9 +20,6 @@
 
 #define TRACE 0
 
-#define LOG_TAG "Binder"
-#include <cutils/log.h>
-
 void bio_init_from_txn(struct binder_io *io, struct binder_transaction_data *txn);
 
 #if TRACE
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 68e3ceb..43c4c8b 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -22,7 +22,7 @@
 #define ALOGE(x...) fprintf(stderr, "svcmgr: " x)
 #else
 #define LOG_TAG "ServiceManager"
-#include <cutils/log.h>
+#include <log/log.h>
 #endif
 
 struct audit_data {
@@ -60,7 +60,6 @@
     return 1;
 }
 
-static int selinux_enabled;
 static char *service_manager_context;
 static struct selabel_handle* sehandle;
 
@@ -89,10 +88,6 @@
 
 static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm)
 {
-    if (selinux_enabled <= 0) {
-        return true;
-    }
-
     return check_mac_perms(spid, uid, service_manager_context, perm, NULL);
 }
 
@@ -101,10 +96,6 @@
     bool allowed;
     char *tctx = NULL;
 
-    if (selinux_enabled <= 0) {
-        return true;
-    }
-
     if (!sehandle) {
         ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
         abort();
@@ -372,6 +363,7 @@
 int main()
 {
     struct binder_state *bs;
+    union selinux_callback cb;
 
     bs = binder_open(128*1024);
     if (!bs) {
@@ -384,28 +376,25 @@
         return -1;
     }
 
-    selinux_enabled = is_selinux_enabled();
-    sehandle = selinux_android_service_context_handle();
-    selinux_status_open(true);
-
-    if (selinux_enabled > 0) {
-        if (sehandle == NULL) {
-            ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
-            abort();
-        }
-
-        if (getcon(&service_manager_context) != 0) {
-            ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
-            abort();
-        }
-    }
-
-    union selinux_callback cb;
     cb.func_audit = audit_callback;
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
     cb.func_log = selinux_log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
+    sehandle = selinux_android_service_context_handle();
+    selinux_status_open(true);
+
+    if (sehandle == NULL) {
+        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
+        abort();
+    }
+
+    if (getcon(&service_manager_context) != 0) {
+        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
+        abort();
+    }
+
+
     binder_loop(bs, svcmgr_handler);
 
     return 0;
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 4b7a706..84230da 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -24,6 +24,7 @@
     <feature name="android.hardware.location" />
     <!-- devices supporting compass/magnitometer sensor must include
 	 android.hardware.sensor.compass.xml -->
+    <feature name="android.hardware.sensor.gyroscope" />
     <feature name="android.hardware.sensor.accelerometer" />
     <feature name="android.hardware.bluetooth" />
     <feature name="android.hardware.touchscreen" />
@@ -34,7 +35,6 @@
 
     <!-- device administration -->
     <feature name="android.software.device_admin" />
-    <feature name="android.software.managed_users" />
 
     <!-- devices with GPS must include device/google/clockwork/gps.xml -->
     <!-- devices with an autofocus camera and/or flash must include either
diff --git a/docs/Makefile b/docs/Makefile
index 5104d81..c655e0c 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,13 +1,12 @@
 HEADERS := $(wildcard ../include/android/*.h)
 
-all: html jd
+all: html website
 
 html: $(HEADERS) Doxyfile
 	mkdir -p html
 	doxygen
 
-jd: $(HEADERS) Doxyfile header.jd
-	mkdir -p jd
-	HTML_HEADER=header.jd HTML_FOOTER=footer.jd HTML_OUTPUT=jd doxygen
-	for file in jd/*.html; do mv "$${file}" "$${file/.html/.jd}"; done
-	rm -f jd/index.jd
+website: $(HEADERS) Doxyfile header.html
+	mkdir -p website
+	HTML_HEADER=header.html HTML_FOOTER=footer.html HTML_OUTPUT=website doxygen
+	rm -f website/index.html
diff --git a/docs/footer.html b/docs/footer.html
new file mode 100644
index 0000000..308b1d0
--- /dev/null
+++ b/docs/footer.html
@@ -0,0 +1,2 @@
+</body>
+</html>
diff --git a/docs/footer.jd b/docs/footer.jd
deleted file mode 100644
index e69de29..0000000
--- a/docs/footer.jd
+++ /dev/null
diff --git a/docs/header.html b/docs/header.html
new file mode 100644
index 0000000..04727b3
--- /dev/null
+++ b/docs/header.html
@@ -0,0 +1,10 @@
+<html devsite>
+<head>
+  <meta name="top_category" value="ndk" />
+  <meta name="subcategory" value="reference" />
+  <meta name="book_path" value="/ndk/reference/_book.yaml" />
+  <title>$title</title>
+  <link rel="stylesheet" type="text/css" href="doxygen-dac.css">
+</head>
+<body>
+<div id="top"><!-- we must have this tag, it's closed by doxygen. ¯\_(ツ)_/¯ -->
diff --git a/docs/header.jd b/docs/header.jd
deleted file mode 100644
index e50f41b..0000000
--- a/docs/header.jd
+++ /dev/null
@@ -1,3 +0,0 @@
-page.title=$title
-page.customHeadTag=<link rel="stylesheet" type="text/css" href="doxygen-dac.css">
-@jd:body
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 67e28da..e202060 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -757,7 +757,15 @@
     /** Copy key. */
     AKEYCODE_COPY = 278,
     /** Paste key. */
-    AKEYCODE_PASTE = 279
+    AKEYCODE_PASTE = 279,
+    /** fingerprint navigation key, up. */
+    AKEYCODE_SYSTEM_NAVIGATION_UP = 280,
+    /** fingerprint navigation key, down. */
+    AKEYCODE_SYSTEM_NAVIGATION_DOWN = 281,
+    /** fingerprint navigation key, left. */
+    AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282,
+    /** fingerprint navigation key, right. */
+    AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/sensor.h b/include/android/sensor.h
index b6a42ae..4a00818 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -397,6 +397,7 @@
 /**
  * Enable the selected sensor with a specified sampling period and max batch report latency.
  * Returns a negative error code on failure.
+ * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before.
  */
 int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
         int32_t samplingPeriodUs, int maxBatchReportLatencyUs);
diff --git a/include/gui/BitTube.h b/include/gui/BitTube.h
index 3ecac52..9d65fad 100644
--- a/include/gui/BitTube.h
+++ b/include/gui/BitTube.h
@@ -20,10 +20,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <android/log.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
-#include <cutils/log.h>
-
 
 namespace android {
 // ----------------------------------------------------------------------------
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index a23ed57..266f0aa 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -66,6 +66,8 @@
         virtual void onFrameReplaced(const BufferItem& item) override;
         virtual void onBuffersReleased() override;
         virtual void onSidebandStreamChanged() override;
+        virtual bool getFrameTimestamps(uint64_t frameNumber,
+                FrameTimestamps* outTimestamps) const override;
     private:
         // mConsumerListener is a weak reference to the IConsumerListener.  This is
         // the raison d'etre of ProxyConsumerListener.
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
index c655f2a..e2bafec 100644
--- a/include/gui/BufferQueueConsumer.h
+++ b/include/gui/BufferQueueConsumer.h
@@ -136,6 +136,13 @@
     // Retrieve the sideband buffer stream, if any.
     virtual sp<NativeHandle> getSidebandStream() const;
 
+    // See IGraphicBufferConsumer::getOccupancyHistory
+    virtual status_t getOccupancyHistory(bool forceFlush,
+            std::vector<OccupancyTracker::Segment>* outHistory) override;
+
+    // See IGraphicBufferConsumer::discardFreeBuffers
+    virtual status_t discardFreeBuffers() override;
+
     // dump our state in a String
     virtual void dumpState(String8& result, const char* prefix) const;
 
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 4f1e09c..b1c730a 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -20,6 +20,7 @@
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueDefs.h>
 #include <gui/BufferSlot.h>
+#include <gui/OccupancyTracker.h>
 
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
@@ -124,6 +125,11 @@
     // all slots, even if they're currently dequeued, queued, or acquired.
     void freeAllBuffersLocked();
 
+    // discardFreeBuffersLocked releases all currently-free buffers held by the
+    // queue, in order to reduce the memory consumption of the queue to the
+    // minimum possible without discarding data.
+    void discardFreeBuffersLocked();
+
     // If delta is positive, makes more slots available. If negative, takes
     // away slots. Returns false if the request can't be met.
     bool adjustAvailableSlotsLocked(int delta);
@@ -176,9 +182,15 @@
     // to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated
     // by the connect and disconnect methods.
     int mConnectedApi;
+    // PID of the process which last successfully called connect(...)
+    pid_t mConnectedPid;
 
-    // mConnectedProducerToken is used to set a binder death notification on
+    // mLinkedToDeath is used to set a binder death notification on
     // the producer.
+    sp<IProducerListener> mLinkedToDeath;
+
+    // mConnectedProducerListener is used to handle the onBufferReleased
+    // notification.
     sp<IProducerListener> mConnectedProducerListener;
 
     // mSlots is an array of buffer slots that must be mirrored on the producer
@@ -322,6 +334,10 @@
     // The slot of the last queued buffer
     int mLastQueuedSlot;
 
+    OccupancyTracker mOccupancyTracker;
+
+    const uint64_t mUniqueId;
+
 }; // class BufferQueueCore
 
 } // namespace android
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index a48f45e..79e7af2 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -135,15 +135,8 @@
     virtual status_t connect(const sp<IProducerListener>& listener,
             int api, bool producerControlledByApp, QueueBufferOutput* output);
 
-    // disconnect attempts to disconnect a producer API from the BufferQueue.
-    // Calling this method will cause any subsequent calls to other
-    // IGraphicBufferProducer methods to fail except for getAllocator and connect.
-    // Successfully calling connect after this will allow the other methods to
-    // succeed again.
-    //
-    // This method will fail if the the BufferQueue is not currently
-    // connected to the specified producer API.
-    virtual status_t disconnect(int api);
+    // See IGraphicBufferProducer::disconnect
+    virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api);
 
     // Attaches a sideband buffer stream to the IGraphicBufferProducer.
     //
@@ -170,9 +163,6 @@
     // See IGraphicBufferProducer::getConsumerName
     virtual String8 getConsumerName() const override;
 
-    // See IGraphicBufferProducer::getNextFrameNumber
-    virtual uint64_t getNextFrameNumber() const override;
-
     // See IGraphicBufferProducer::setSharedBufferMode
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
 
@@ -186,6 +176,13 @@
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
 
+    // See IGraphicBufferProducer::getFrameTimestamps
+    virtual bool getFrameTimestamps(uint64_t frameNumber,
+            FrameTimestamps* outTimestamps) const override;
+
+    // See IGraphicBufferProducer::getUniqueId
+    virtual status_t getUniqueId(uint64_t* outId) const override;
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 19fdcc3..9f8b638 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -85,6 +85,13 @@
     // See IGraphicBufferConsumer::setDefaultBufferDataSpace
     status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace);
 
+    // See IGraphicBufferConsumer::getOccupancyHistory
+    status_t getOccupancyHistory(bool forceFlush,
+            std::vector<OccupancyTracker::Segment>* outHistory);
+
+    // See IGraphicBufferConsumer::discardFreeBuffers
+    status_t discardFreeBuffers();
+
 private:
     ConsumerBase(const ConsumerBase&);
     void operator=(const ConsumerBase&);
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h
new file mode 100644
index 0000000..4dc7467
--- /dev/null
+++ b/include/gui/FrameTimestamps.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_FRAMETIMESTAMPS_H
+#define ANDROID_GUI_FRAMETIMESTAMPS_H
+
+#include <utils/Timers.h>
+#include <utils/Flattenable.h>
+
+namespace android {
+
+struct FrameTimestamps : public LightFlattenablePod<FrameTimestamps> {
+    FrameTimestamps() :
+        frameNumber(0),
+        postedTime(0),
+        acquireTime(0),
+        refreshStartTime(0),
+        glCompositionDoneTime(0),
+        displayRetireTime(0),
+        releaseTime(0) {}
+
+    uint64_t frameNumber;
+    nsecs_t postedTime;
+    nsecs_t acquireTime;
+    nsecs_t refreshStartTime;
+    nsecs_t glCompositionDoneTime;
+    nsecs_t displayRetireTime;
+    nsecs_t releaseTime;
+};
+
+} // namespace android
+#endif
diff --git a/include/gui/IConsumerListener.h b/include/gui/IConsumerListener.h
index aef076c..460a03d 100644
--- a/include/gui/IConsumerListener.h
+++ b/include/gui/IConsumerListener.h
@@ -25,6 +25,8 @@
 
 #include <binder/IInterface.h>
 
+#include <gui/FrameTimestamps.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -78,6 +80,11 @@
     // stream is first attached and when it is either detached or replaced by a
     // different stream.
     virtual void onSidebandStreamChanged() = 0; /* Asynchronous */
+
+    // See IGraphicBufferProducer::getFrameTimestamps
+    // This queries the consumer for the timestamps
+    virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
+            FrameTimestamps* /*outTimestamps*/) const { return false; }
 };
 
 
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index 210fbc8..dcddca4 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -27,6 +27,7 @@
 #include <binder/IInterface.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
+#include <gui/OccupancyTracker.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -265,6 +266,17 @@
     // Retrieve the sideband buffer stream, if any.
     virtual sp<NativeHandle> getSidebandStream() const = 0;
 
+    // Retrieves any stored segments of the occupancy history of this
+    // BufferQueue and clears them. Optionally closes out the pending segment if
+    // forceFlush is true.
+    virtual status_t getOccupancyHistory(bool forceFlush,
+            std::vector<OccupancyTracker::Segment>* outHistory) = 0;
+
+    // discardFreeBuffers releases all currently-free buffers held by the queue,
+    // in order to reduce the memory consumption of the queue to the minimum
+    // possible without discarding data.
+    virtual status_t discardFreeBuffers() = 0;
+
     // dump state into a string
     virtual void dumpState(String8& result, const char* prefix) const = 0;
 
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index d74f1f6..c2dba50 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -30,6 +30,8 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include <gui/FrameTimestamps.h>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -360,24 +362,29 @@
         inline void deflate(uint32_t* outWidth,
                 uint32_t* outHeight,
                 uint32_t* outTransformHint,
-                uint32_t* outNumPendingBuffers) const {
+                uint32_t* outNumPendingBuffers,
+                uint64_t* outNextFrameNumber) const {
             *outWidth = width;
             *outHeight = height;
             *outTransformHint = transformHint;
             *outNumPendingBuffers = numPendingBuffers;
+            *outNextFrameNumber = nextFrameNumber;
         }
         inline void inflate(uint32_t inWidth, uint32_t inHeight,
-                uint32_t inTransformHint, uint32_t inNumPendingBuffers) {
+                uint32_t inTransformHint, uint32_t inNumPendingBuffers,
+                uint64_t inNextFrameNumber) {
             width = inWidth;
             height = inHeight;
             transformHint = inTransformHint;
             numPendingBuffers = inNumPendingBuffers;
+            nextFrameNumber = inNextFrameNumber;
         }
     private:
         uint32_t width;
         uint32_t height;
         uint32_t transformHint;
         uint32_t numPendingBuffers;
+        uint64_t nextFrameNumber{0};
     };
 
     virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
@@ -452,17 +459,24 @@
     virtual status_t connect(const sp<IProducerListener>& listener,
             int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;
 
+    enum class DisconnectMode {
+        // Disconnect only the specified API.
+        Api,
+        // Disconnect any API originally connected from the process calling disconnect.
+        AllLocal
+    };
+
     // disconnect attempts to disconnect a client API from the
     // IGraphicBufferProducer.  Calling this method will cause any subsequent
     // calls to other IGraphicBufferProducer methods to fail except for
     // getAllocator and connect.  Successfully calling connect after this will
     // allow the other methods to succeed again.
     //
-    // This method will fail if the the IGraphicBufferProducer is not currently
-    // connected to the specified client API.
-    //
     // The api should be one of the NATIVE_WINDOW_API_* values in <window.h>
     //
+    // Alternatively if mode is AllLocal, then the API value is ignored, and any API
+    // connected from the same PID calling disconnect will be disconnected.
+    //
     // Disconnecting from an abandoned IGraphicBufferProducer is legal and
     // is considered a no-op.
     //
@@ -471,7 +485,7 @@
     //             * the api specified does not match the one that was connected
     //             * api was out of range (see above).
     // * DEAD_OBJECT - the token is hosted by an already-dead process
-    virtual status_t disconnect(int api) = 0;
+    virtual status_t disconnect(int api, DisconnectMode mode = DisconnectMode::Api) = 0;
 
     // Attaches a sideband buffer stream to the IGraphicBufferProducer.
     //
@@ -522,9 +536,6 @@
     // Returns the name of the connected consumer.
     virtual String8 getConsumerName() const = 0;
 
-    // Returns the number of the next frame which will be dequeued.
-    virtual uint64_t getNextFrameNumber() const = 0;
-
     // Used to enable/disable shared buffer mode.
     //
     // When shared buffer mode is enabled the first buffer that is queued or
@@ -569,6 +580,17 @@
     // Returns NO_ERROR or the status of the Binder transaction
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
+
+    // Attempts to retrieve timestamp information for the given frame number.
+    // If information for the given frame number is not found, returns false.
+    // Returns true otherwise.
+    //
+    // If a fence has not yet signaled the timestamp returned will be 0;
+    virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
+            FrameTimestamps* /*outTimestamps*/) const { return false; }
+
+    // Returns a unique id for this BufferQueue
+    virtual status_t getUniqueId(uint64_t* outId) const = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/IProducerListener.h b/include/gui/IProducerListener.h
index 4a5ed46..e808bd3 100644
--- a/include/gui/IProducerListener.h
+++ b/include/gui/IProducerListener.h
@@ -41,6 +41,7 @@
     // This is called without any lock held and can be called concurrently by
     // multiple threads.
     virtual void onBufferReleased() = 0; // Asynchronous
+    virtual bool needsReleaseNotify() = 0;
 };
 
 class IProducerListener : public ProducerListener, public IInterface
@@ -54,6 +55,7 @@
 public:
     virtual status_t onTransact(uint32_t code, const Parcel& data,
             Parcel* reply, uint32_t flags = 0);
+    virtual bool needsReleaseNotify();
 };
 
 class DummyProducerListener : public BnProducerListener
@@ -61,6 +63,7 @@
 public:
     virtual ~DummyProducerListener();
     virtual void onBufferReleased() {}
+    virtual bool needsReleaseNotify() { return false; }
 };
 
 } // namespace android
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index dbaa5fe..555a0cc 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -137,6 +137,12 @@
      * should be used */
     virtual status_t setActiveConfig(const sp<IBinder>& display, int id) = 0;
 
+    virtual status_t getDisplayColorModes(const sp<IBinder>& display,
+            Vector<android_color_mode_t>* outColorModes) = 0;
+    virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display) = 0;
+    virtual status_t setActiveColorMode(const sp<IBinder>& display,
+            android_color_mode_t colorMode) = 0;
+
     /* Capture the specified screen. requires READ_FRAME_BUFFER permission
      * This function will fail if there is a secure window on screen.
      */
@@ -193,6 +199,9 @@
         SET_POWER_MODE,
         GET_DISPLAY_STATS,
         GET_HDR_CAPABILITIES,
+        GET_DISPLAY_COLOR_MODES,
+        GET_ACTIVE_COLOR_MODE,
+        SET_ACTIVE_COLOR_MODE,
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/gui/ISurfaceComposerClient.h b/include/gui/ISurfaceComposerClient.h
index 48bd088..4a4efb6 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -77,6 +77,9 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
+
+    virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
+            bool* outTransformToDisplayInverse) const = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/OccupancyTracker.h b/include/gui/OccupancyTracker.h
new file mode 100644
index 0000000..d4de8f2
--- /dev/null
+++ b/include/gui/OccupancyTracker.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_GUI_OCCUPANCYTRACKER_H
+#define ANDROID_GUI_OCCUPANCYTRACKER_H
+
+#include <binder/Parcelable.h>
+
+#include <utils/Timers.h>
+
+#include <deque>
+#include <unordered_map>
+
+namespace android {
+
+class String8;
+
+class OccupancyTracker
+{
+public:
+    OccupancyTracker()
+      : mPendingSegment(),
+        mSegmentHistory(),
+        mLastOccupancy(0),
+        mLastOccupancyChangeTime(0) {}
+
+    struct Segment : public Parcelable {
+        Segment()
+          : totalTime(0),
+            numFrames(0),
+            occupancyAverage(0.0f),
+            usedThirdBuffer(false) {}
+
+        Segment(nsecs_t _totalTime, size_t _numFrames, float _occupancyAverage,
+                bool _usedThirdBuffer)
+          : totalTime(_totalTime),
+            numFrames(_numFrames),
+            occupancyAverage(_occupancyAverage),
+            usedThirdBuffer(_usedThirdBuffer) {}
+
+        // Parcelable interface
+        virtual status_t writeToParcel(Parcel* parcel) const override;
+        virtual status_t readFromParcel(const Parcel* parcel) override;
+
+        nsecs_t totalTime;
+        size_t numFrames;
+
+        // Average occupancy of the queue over this segment. (0.0, 1.0) implies
+        // double-buffered, (1.0, 2.0) implies triple-buffered.
+        float occupancyAverage;
+
+        // Whether a third buffer was used at all during this segment (since a
+        // segment could read as double-buffered on average, but still require a
+        // third buffer to avoid jank for some smaller portion)
+        bool usedThirdBuffer;
+    };
+
+    void registerOccupancyChange(size_t occupancy);
+    std::vector<Segment> getSegmentHistory(bool forceFlush);
+
+private:
+    static constexpr size_t MAX_HISTORY_SIZE = 10;
+    static constexpr nsecs_t NEW_SEGMENT_DELAY = ms2ns(100);
+    static constexpr size_t LONG_SEGMENT_THRESHOLD = 3;
+
+    struct PendingSegment {
+        void clear() {
+            totalTime = 0;
+            numFrames = 0;
+            mOccupancyTimes.clear();
+        }
+
+        nsecs_t totalTime;
+        size_t numFrames;
+        std::unordered_map<size_t, nsecs_t> mOccupancyTimes;
+    };
+
+    void recordPendingSegment();
+
+    PendingSegment mPendingSegment;
+    std::deque<Segment> mSegmentHistory;
+
+    size_t mLastOccupancy;
+    nsecs_t mLastOccupancyChangeTime;
+
+}; // class OccupancyTracker
+
+} // namespace android
+
+#endif
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 592391f..489d5ea 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -134,6 +134,14 @@
     status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]);
 
+    // See IGraphicBufferProducer::getFrameTimestamps
+    bool getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+            nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+            nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+            nsecs_t* outReleaseTime);
+
+    status_t getUniqueId(uint64_t* outId) const;
+
 protected:
     virtual ~Surface();
 
@@ -183,19 +191,18 @@
     int dispatchSetSurfaceDamage(va_list args);
     int dispatchSetSharedBufferMode(va_list args);
     int dispatchSetAutoRefresh(va_list args);
+    int dispatchGetFrameTimestamps(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
     virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
     virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
     virtual int perform(int operation, va_list args);
-    virtual int query(int what, int* value) const;
     virtual int setSwapInterval(int interval);
 
     virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer);
 
     virtual int connect(int api);
-    virtual int disconnect(int api);
     virtual int setBufferCount(int bufferCount);
     virtual int setBuffersDimensions(uint32_t width, uint32_t height);
     virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
@@ -209,12 +216,17 @@
     virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
 
 public:
+    virtual int disconnect(int api,
+            IGraphicBufferProducer::DisconnectMode mode =
+                    IGraphicBufferProducer::DisconnectMode::Api);
+
     virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual int setAsyncMode(bool async);
     virtual int setSharedBufferMode(bool sharedBufferMode);
     virtual int setAutoRefresh(bool autoRefresh);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
+    virtual int query(int what, int* value) const;
 
     virtual int connect(int api, const sp<IProducerListener>& listener);
     virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
@@ -361,7 +373,13 @@
     // used to prevent a mismatch between the number of queue/dequeue calls.
     bool mSharedBufferHasBeenQueued;
 
+    // These are used to satisfy the NATIVE_WINDOW_LAST_*_DURATION queries
+    nsecs_t mLastDequeueDuration = 0;
+    nsecs_t mLastQueueDuration = 0;
+
     Condition mQueueBufferCondition;
+
+    uint64_t mNextFrameNumber;
 };
 
 namespace view {
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index 6c621ee..f2932f2 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -83,6 +83,16 @@
     // returned by getDisplayInfo
     static status_t setActiveConfig(const sp<IBinder>& display, int id);
 
+    // Gets the list of supported color modes for the given display
+    static status_t getDisplayColorModes(const sp<IBinder>& display,
+            Vector<android_color_mode_t>* outColorModes);
+
+    // Gets the active color mode for the given display
+    static android_color_mode_t getActiveColorMode(const sp<IBinder>& display);
+
+    // Sets the active color mode for the given display
+    static status_t setActiveColorMode(const sp<IBinder>& display, android_color_mode_t colorMode);
+
     /* Triggers screen on/off or low power mode and waits for it to complete */
     static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
 
@@ -140,21 +150,24 @@
             const sp<IBinder>& handle, uint64_t frameNumber);
     status_t    setOverrideScalingMode(const sp<IBinder>& id,
             int32_t overrideScalingMode);
-    status_t    setPositionAppliesWithResize(const sp<IBinder>& id);
+    status_t    setGeometryAppliesWithResize(const sp<IBinder>& id);
 
     status_t    destroySurface(const sp<IBinder>& id);
 
     status_t clearLayerFrameStats(const sp<IBinder>& token) const;
     status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
 
+    status_t getTransformToDisplayInverse(const sp<IBinder>& token,
+            bool* outTransformToDisplayInverse) const;
+
     static status_t clearAnimationFrameStats();
     static status_t getAnimationFrameStats(FrameStats* outStats);
 
     static status_t getHdrCapabilities(const sp<IBinder>& display,
             HdrCapabilities* outCapabilities);
 
-    static void setDisplaySurface(const sp<IBinder>& token,
-            const sp<IGraphicBufferProducer>& bufferProducer);
+    static status_t setDisplaySurface(const sp<IBinder>& token,
+            sp<IGraphicBufferProducer> bufferProducer);
     static void setDisplayLayerStack(const sp<IBinder>& token,
             uint32_t layerStack);
     static void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index fafd194..5e731c3 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -73,10 +73,11 @@
     status_t    setCrop(const Rect& crop);
     status_t    setFinalCrop(const Rect& crop);
 
-    // If the size changes in this transaction, position updates specified
+    // If the size changes in this transaction, all geometry updates specified
     // in this transaction will not complete until a buffer of the new size
-    // arrives.
-    status_t    setPositionAppliesWithResize();
+    // arrives. As some elements normally apply immediately, this enables
+    // freezing the total geometry of a surface until a resize is completed.
+    status_t    setGeometryAppliesWithResize();
 
     // Defers applying any changes made in this transaction until the Layer
     // identified by handle reaches the given frameNumber
@@ -96,6 +97,8 @@
     status_t clearLayerFrameStats() const;
     status_t getLayerFrameStats(FrameStats* outStats) const;
 
+    status_t getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const;
+
 private:
     // can't be copied
     SurfaceControl& operator = (SurfaceControl& rhs);
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b59728a..20154eb 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -319,6 +319,10 @@
     DEFINE_KEYCODE(CUT),
     DEFINE_KEYCODE(COPY),
     DEFINE_KEYCODE(PASTE),
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP),
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN),
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT),
+    DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
 
     { NULL, 0 }
 };
diff --git a/include/media/hardware/HDCPAPI.h b/include/media/hardware/HDCPAPI.h
index 3a53e9f..30cd5fd 100644
--- a/include/media/hardware/HDCPAPI.h
+++ b/include/media/hardware/HDCPAPI.h
@@ -73,7 +73,7 @@
     // Module can call the notification function to signal completion/failure
     // of asynchronous operations (such as initialization) or out of band
     // events.
-    HDCPModule(void *cookie, ObserverFunc observerNotify) {};
+    HDCPModule(void * /*cookie*/, ObserverFunc /*observerNotify*/) {};
 
     virtual ~HDCPModule() {};
 
@@ -104,8 +104,8 @@
     // 1 for the second and so on)
     // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
     virtual status_t encrypt(
-            const void *inData, size_t size, uint32_t streamCTR,
-            uint64_t *outInputCTR, void *outData) {
+            const void * /*inData*/, size_t /*size*/, uint32_t /*streamCTR*/,
+            uint64_t * /*outInputCTR*/, void * /*outData*/) {
         return INVALID_OPERATION;
     }
 
@@ -119,8 +119,8 @@
     // 1 for the second and so on)
     // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
     virtual status_t encryptNative(
-            buffer_handle_t buffer, size_t offset, size_t size,
-            uint32_t streamCTR, uint64_t *outInputCTR, void *outData) {
+            buffer_handle_t /*buffer*/, size_t /*offset*/, size_t /*size*/,
+            uint32_t /*streamCTR*/, uint64_t * /*outInputCTR*/, void * /*outData*/) {
         return INVALID_OPERATION;
     }
     // DECRYPTION only:
@@ -133,9 +133,9 @@
     // until outData contains size bytes of decrypted data.
     // Both streamCTR and inputCTR will be provided by the caller.
     virtual status_t decrypt(
-            const void *inData, size_t size,
-            uint32_t streamCTR, uint64_t inputCTR,
-            void *outData) {
+            const void * /*inData*/, size_t /*size*/,
+            uint32_t /*streamCTR*/, uint64_t /*inputCTR*/,
+            void * /*outData*/) {
         return INVALID_OPERATION;
     }
 
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index 03801ca..7ae07ad 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -543,6 +543,8 @@
         case OMX_IndexParamVideoHevc:                   return "ParamVideoHevc";
 //      case OMX_IndexParamSliceSegments:               return "ParamSliceSegments";
         case OMX_IndexConfigAndroidIntraRefresh:        return "ConfigAndroidIntraRefresh";
+        case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering";
+        case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering";
         case OMX_IndexConfigAutoFramerateConversion:    return "ConfigAutoFramerateConversion";
         case OMX_IndexConfigPriority:                   return "ConfigPriority";
         case OMX_IndexConfigOperatingRate:              return "ConfigOperatingRate";
@@ -973,8 +975,8 @@
 inline static const char *asString(
         OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE i, const char *def = "??") {
     switch (i) {
-        case OMX_VIDEO_VPXTemporalLayerPatternNone:   return "VPXTemporalLayerPatternNone";
-        case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "VPXTemporalLayerPatternWebRTC";
+        case OMX_VIDEO_VPXTemporalLayerPatternNone:   return "None";
+        case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "WebRTC";
         default:                                      return def;
     }
 }
@@ -1022,6 +1024,16 @@
     }
 }
 
+inline static const char *asString(
+        OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE i, const char *def = "??") {
+    switch (i) {
+        case OMX_VIDEO_AndroidTemporalLayeringPatternNone:    return "None";
+        case OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC:  return "WebRTC";
+        case OMX_VIDEO_AndroidTemporalLayeringPatternAndroid: return "Android";
+        default:                                              return def;
+    }
+}
+
 #endif // AS_STRING_FOR_OMX_VIDEOEXT_H
 
 #endif // OMX_VideoExt_h
diff --git a/include/media/openmax/OMX_Core.h b/include/media/openmax/OMX_Core.h
index eeacf43..bb974b3 100644
--- a/include/media/openmax/OMX_Core.h
+++ b/include/media/openmax/OMX_Core.h
@@ -516,6 +516,9 @@
      *  but must signal the event no more than 40ms after the first frame in the batch. The frames
      *  must be ordered by system timestamp inside and across batches.
      *
+     *  The component shall signal the render-timestamp of the very first frame (as well as the
+     *  first frame after each flush) unbatched (with nData1 set to 1) within 5 msec.
+     *
      *  If component is doing frame-rate conversion, it must signal the render time of each
      *  converted frame, and must interpolate media timestamps for in-between frames.
      *
@@ -753,15 +756,21 @@
     When the command is "OMX_CommandStateSet" the component will queue a
     state transition to the new state idenfied in nParam.
 
+    The component shall transition from executing to loaded state within 500 msec.
+
     When the command is "OMX_CommandFlush", to flush a port's buffer queues,
     the command will force the component to return all buffers NOT CURRENTLY
     BEING PROCESSED to the application, in the order in which the buffers
     were received.
 
+    The component shall finish flusing each port within 5 msec.
+
     When the command is "OMX_CommandPortDisable" or
     "OMX_CommandPortEnable", the component's port (given by the value of
     nParam) will be stopped or restarted.
 
+    The component shall finish disabling/reenabling each port within 5 msec.
+
     When the command "OMX_CommandMarkBuffer" is used to mark a buffer, the
     pCmdData will point to a OMX_MARKTYPE structure containing the component
     handle of the component to examine the buffer chain for the mark.  nParam1
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index 1724576..b688d1d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -78,6 +78,8 @@
     OMX_IndexParamVideoHevc,                        /**< reference: OMX_VIDEO_PARAM_HEVCTYPE */
     OMX_IndexParamSliceSegments,                    /**< reference: OMX_VIDEO_SLICESEGMENTSTYPE */
     OMX_IndexConfigAndroidIntraRefresh,             /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */
+    OMX_IndexParamAndroidVideoTemporalLayering,     /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */
+    OMX_IndexConfigAndroidVideoTemporalLayering,    /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */
 
     /* Image & Video common configurations */
     OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000,
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index bf15ee4..2c02431 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -170,7 +170,11 @@
     OMX_U32 nSize;
     OMX_VERSIONTYPE nVersion;
     OMX_U32 nPortIndex;
-    OMX_U32 nKeyFrameInterval;
+    OMX_U32 nKeyFrameInterval;        // distance between consecutive key_frames (including one
+                                      // of the key_frames). 0 means interval is unspecified and
+                                      // can be freely chosen by the codec. 1 means a stream of
+                                      // only key_frames.
+
     OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE eTemporalPattern;
     OMX_U32 nTemporalLayerCount;
     OMX_U32 nTemporalLayerBitrateRatio[OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS];
@@ -227,7 +231,10 @@
     OMX_U32 nPortIndex;
     OMX_VIDEO_HEVCPROFILETYPE eProfile;
     OMX_VIDEO_HEVCLEVELTYPE eLevel;
-    OMX_U32 nKeyFrameInterval;
+    OMX_U32 nKeyFrameInterval;        // distance between consecutive I-frames (including one
+                                      // of the I frames). 0 means interval is unspecified and
+                                      // can be freely chosen by the codec. 1 means a stream of
+                                      // only I frames.
 } OMX_VIDEO_PARAM_HEVCTYPE;
 
 /** Structure to define if dependent slice segments should be used */
@@ -289,7 +296,7 @@
  *  nVersion            : OMX specification version information
  *  nPortIndex          : Port that this structure applies to
  *  nRefreshPeriod      : Intra refreh period in frames. Value 0 means disable intra refresh
-*/
+ */
 typedef struct OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE {
     OMX_U32 nSize;
     OMX_VERSIONTYPE nVersion;
@@ -297,6 +304,95 @@
     OMX_U32 nRefreshPeriod;
 } OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE;
 
+/** Maximum number of temporal layers supported by AVC/HEVC */
+#define OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS 8
+
+/** temporal layer patterns */
+typedef enum OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE {
+    OMX_VIDEO_AndroidTemporalLayeringPatternNone = 0,
+    // pattern as defined by WebRTC
+    OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC = 1 << 0,
+    // pattern where frames in any layer other than the base layer only depend on at most the very
+    // last frame from each preceding layer (other than the base layer.)
+    OMX_VIDEO_AndroidTemporalLayeringPatternAndroid = 1 << 1,
+} OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE;
+
+/**
+ * Android specific param for configuration of temporal layering.
+ * Android only supports temporal layering where successive layers each double the
+ * previous layer's framerate.
+ * NOTE: Reading this parameter at run-time SHALL return actual run-time values.
+ *
+ *  nSize                      : Size of the structure in bytes
+ *  nVersion                   : OMX specification version information
+ *  nPortIndex                 : Port that this structure applies to (output port for encoders)
+ *  eSupportedPatterns         : A bitmask of supported layering patterns
+ *  nLayerCountMax             : Max number of temporal coding layers supported
+ *                               by the encoder (must be at least 1, 1 meaning temporal layering
+ *                               is NOT supported)
+ *  nBLayerCountMax            : Max number of layers that can contain B frames
+ *                               (0) to (nLayerCountMax - 1)
+ *  ePattern                   : Layering pattern.
+ *  nPLayerCountActual         : Number of temporal layers to be coded with non-B frames,
+ *                               starting from and including the base-layer.
+ *                               (1 to nLayerCountMax - nBLayerCountActual)
+ *                               If nPLayerCountActual is 1 and nBLayerCountActual is 0, temporal
+ *                               layering is disabled. Otherwise, it is enabled.
+ *  nBLayerCountActual         : Number of temporal layers to be coded with B frames,
+ *                               starting after non-B layers.
+ *                               (0 to nBLayerCountMax)
+ *  bBitrateRatiosSpecified    : Flag to indicate if layer-wise bitrate
+ *                               distribution is specified.
+ *  nBitrateRatios             : Bitrate ratio (100 based) per layer (index 0 is base layer).
+ *                               Honored if bBitrateRatiosSpecified is set.
+ *                               i.e for 4 layers with desired distribution (25% 25% 25% 25%),
+ *                               nBitrateRatio = {25, 50, 75, 100, ... }
+ *                               Values in indices not less than 'the actual number of layers
+ *                               minus 1' MAY be ignored and assumed to be 100.
+ */
+typedef struct OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE eSupportedPatterns;
+    OMX_U32 nLayerCountMax;
+    OMX_U32 nBLayerCountMax;
+    OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern;
+    OMX_U32 nPLayerCountActual;
+    OMX_U32 nBLayerCountActual;
+    OMX_BOOL bBitrateRatiosSpecified;
+    OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS];
+} OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE;
+
+/**
+ * Android specific config for changing the temporal-layer count or
+ * bitrate-distribution at run-time.
+ *
+ *  nSize                      : Size of the structure in bytes
+ *  nVersion                   : OMX specification version information
+ *  nPortIndex                 : Port that this structure applies to (output port for encoders)
+ *  ePattern                   : Layering pattern.
+ *  nPLayerCountActual         : Number of temporal layers to be coded with non-B frames.
+ *                               (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.)
+ *  nBLayerCountActual         : Number of temporal layers to be coded with B frames.
+ *                               (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.)
+ *  bBitrateRatiosSpecified    : Flag to indicate if layer-wise bitrate
+ *                               distribution is specified.
+ *  nBitrateRatios             : Bitrate ratio (100 based, Q16 values) per layer (0 is base layer).
+ *                               Honored if bBitrateRatiosSpecified is set.
+ *                               (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.)
+ */
+typedef struct OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern;
+    OMX_U32 nPLayerCountActual;
+    OMX_U32 nBLayerCountActual;
+    OMX_BOOL bBitrateRatiosSpecified;
+    OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS];
+} OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE;
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4885e05..4b3fcc6 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -55,7 +55,7 @@
         eDeferTransaction           = 0x00000200,
         eFinalCropChanged           = 0x00000400,
         eOverrideScalingModeChanged = 0x00000800,
-        ePositionAppliesWithResize  = 0x00001000,
+        eGeometryAppliesWithResize  = 0x00001000,
     };
 
     layer_state_t()
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index ad73ee7..799944f 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -36,7 +36,6 @@
     bool secure;
     nsecs_t appVsyncOffset;
     nsecs_t presentationDeadline;
-    int colorTransform;
 };
 
 /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index a4c1df7..2fbc9ef 100644
--- a/include/ui/Fence.h
+++ b/include/ui/Fence.h
@@ -79,6 +79,9 @@
     // becomes signaled when both f1 and f2 are signaled (even if f1 or f2 is
     // destroyed before it becomes signaled).  The name argument specifies the
     // human-readable name to associated with the new Fence object.
+    static sp<Fence> merge(const char* name, const sp<Fence>& f1,
+            const sp<Fence>& f2);
+
     static sp<Fence> merge(const String8& name, const sp<Fence>& f1,
             const sp<Fence>& f2);
 
@@ -93,6 +96,17 @@
     // occurs then -1 is returned.
     nsecs_t getSignalTime() const;
 
+    // hasSignaled returns whether the fence has signaled yet. Prefer this to
+    // getSignalTime() or wait() if all you care about is whether the fence has
+    // signaled.
+    inline bool hasSignaled() {
+        // The sync_wait call underlying wait() has been measured to be
+        // significantly faster than the sync_fence_info call underlying
+        // getSignalTime(), which might otherwise appear to be the more obvious
+        // way to check whether a fence has signaled.
+        return wait(0) == NO_ERROR;
+    }
+
     // Flattenable interface
     size_t getFlattenedSize() const;
     size_t getFdCount() const;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index a8513a9..e9859fe 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -20,6 +20,8 @@
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/TypeHelpers.h>
+#include <log/log.h>
+
 #include <ui/Point.h>
 
 #include <android/rect.h>
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 790fa8c..6b5b1af 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -17,21 +17,20 @@
 #define LOG_TAG "IMemory"
 
 #include <atomic>
+#include <fcntl.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
 #include <binder/IMemory.h>
-#include <cutils/log.h>
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/CallStack.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
-#include <binder/Parcel.h>
-#include <utils/CallStack.h>
 
 #define VERBOSE   0
 
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 43a01e4..aed0134 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -16,20 +16,19 @@
 
 #define LOG_TAG "MemoryHeapBase"
 
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
 #include <sys/ioctl.h>
-
-#include <cutils/log.h>
-#include <cutils/ashmem.h>
-#include <cutils/atomic.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <binder/MemoryHeapBase.h>
+#include <log/log.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
 
 namespace android {
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index b558ec9..8e8bb80 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -83,6 +83,7 @@
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
         "LayerState.cpp",
+        "OccupancyTracker.cpp",
         "Sensor.cpp",
         "SensorEventQueue.cpp",
         "SensorManager.cpp",
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ccbb5a2..6de98f5 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -61,6 +61,15 @@
     }
 }
 
+bool BufferQueue::ProxyConsumerListener::getFrameTimestamps(
+        uint64_t frameNumber, FrameTimestamps* outTimestamps) const {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != NULL) {
+        return listener->getFrameTimestamps(frameNumber, outTimestamps);
+    }
+    return false;
+}
+
 void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
         sp<IGraphicBufferConsumer>* outConsumer,
         const sp<IGraphicBufferAlloc>& allocator) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 856115d..7fbf312 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -261,6 +261,7 @@
 
         ATRACE_INT(mCore->mConsumerName.string(),
                 static_cast<int32_t>(mCore->mQueue.size()));
+        mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
 
         VALIDATE_CONSISTENCY();
     }
@@ -718,6 +719,19 @@
     return mCore->mSidebandStream;
 }
 
+status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
+        std::vector<OccupancyTracker::Segment>* outHistory) {
+    Mutex::Autolock lock(mCore->mMutex);
+    *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::discardFreeBuffers() {
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->discardFreeBuffersLocked();
+    return NO_ERROR;
+}
+
 void BufferQueueConsumer::dumpState(String8& result, const char* prefix) const {
     const IPCThreadState* ipc = IPCThreadState::self();
     const pid_t pid = ipc->getCallingPid();
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 032779d..d74d32c 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -47,6 +47,12 @@
             android_atomic_inc(&counter));
 }
 
+static uint64_t getUniqueId() {
+    static std::atomic<uint32_t> counter{0};
+    static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+    return id | counter++;
+}
+
 BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
     mAllocator(allocator),
     mMutex(),
@@ -56,6 +62,7 @@
     mConsumerListener(),
     mConsumerUsageBits(0),
     mConnectedApi(NO_CONNECTED_API),
+    mLinkedToDeath(),
     mConnectedProducerListener(),
     mSlots(),
     mQueue(),
@@ -85,7 +92,8 @@
     mAutoRefresh(false),
     mSharedBufferSlot(INVALID_BUFFER_SLOT),
     mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
-            HAL_DATASPACE_UNKNOWN)
+            HAL_DATASPACE_UNKNOWN),
+    mUniqueId(getUniqueId())
 {
     if (allocator == NULL) {
 
@@ -256,6 +264,16 @@
     VALIDATE_CONSISTENCY();
 }
 
+void BufferQueueCore::discardFreeBuffersLocked() {
+    for (int s : mFreeBuffers) {
+        mFreeSlots.insert(s);
+        clearBufferSlotLocked(s);
+    }
+    mFreeBuffers.clear();
+
+    VALIDATE_CONSISTENCY();
+}
+
 bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
     if (delta >= 0) {
         // If we're going to fail, do so before modifying anything
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index e853dfb..f8f3872 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -28,6 +28,7 @@
 
 #define EGL_EGLEXT_PROTOTYPES
 
+#include <binder/IPCThreadState.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferQueueCore.h>
 #include <gui/BufferQueueProducer.h>
@@ -510,11 +511,15 @@
             mCore->mIsAllocatingCondition.broadcast();
 
             if (graphicBuffer == NULL) {
+                mCore->mFreeSlots.insert(*outSlot);
+                mCore->clearBufferSlotLocked(*outSlot);
                 BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
                 return error;
             }
 
             if (mCore->mIsAbandoned) {
+                mCore->mFreeSlots.insert(*outSlot);
+                mCore->clearBufferSlotLocked(*outSlot);
                 BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                 return NO_INIT;
             }
@@ -897,10 +902,12 @@
 
         output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
                 mCore->mTransformHint,
-                static_cast<uint32_t>(mCore->mQueue.size()));
+                static_cast<uint32_t>(mCore->mQueue.size()),
+                mCore->mFrameCounter + 1);
 
         ATRACE_INT(mCore->mConsumerName.string(),
                 static_cast<int32_t>(mCore->mQueue.size()));
+        mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
 
         // Take a ticket for the callback functions
         callbackTicket = mNextCallbackTicket++;
@@ -1104,27 +1111,32 @@
             mCore->mConnectedApi = api;
             output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
                     mCore->mTransformHint,
-                    static_cast<uint32_t>(mCore->mQueue.size()));
+                    static_cast<uint32_t>(mCore->mQueue.size()),
+                    mCore->mFrameCounter + 1);
 
-            // Set up a death notification so that we can disconnect
-            // automatically if the remote producer dies
-            if (listener != NULL &&
-                    IInterface::asBinder(listener)->remoteBinder() != NULL) {
-                status = IInterface::asBinder(listener)->linkToDeath(
-                        static_cast<IBinder::DeathRecipient*>(this));
-                if (status != NO_ERROR) {
-                    BQ_LOGE("connect: linkToDeath failed: %s (%d)",
-                            strerror(-status), status);
+            if (listener != NULL) {
+                // Set up a death notification so that we can disconnect
+                // automatically if the remote producer dies
+                if (IInterface::asBinder(listener)->remoteBinder() != NULL) {
+                    status = IInterface::asBinder(listener)->linkToDeath(
+                            static_cast<IBinder::DeathRecipient*>(this));
+                    if (status != NO_ERROR) {
+                        BQ_LOGE("connect: linkToDeath failed: %s (%d)",
+                                strerror(-status), status);
+                    }
+                    mCore->mLinkedToDeath = listener;
+                }
+                if (listener->needsReleaseNotify()) {
+                    mCore->mConnectedProducerListener = listener;
                 }
             }
-            mCore->mConnectedProducerListener = listener;
             break;
         default:
             BQ_LOGE("connect: unknown API %d", api);
             status = BAD_VALUE;
             break;
     }
-
+    mCore->mConnectedPid = IPCThreadState::self()->getCallingPid();
     mCore->mBufferHasBeenQueued = false;
     mCore->mDequeueBufferCannotBlock = false;
     if (mDequeueTimeout < 0) {
@@ -1137,7 +1149,7 @@
     return status;
 }
 
-status_t BufferQueueProducer::disconnect(int api) {
+status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) {
     ATRACE_CALL();
     BQ_LOGV("disconnect: api %d", api);
 
@@ -1145,6 +1157,14 @@
     sp<IConsumerListener> listener;
     { // Autolock scope
         Mutex::Autolock lock(mCore->mMutex);
+
+        if (mode == DisconnectMode::AllLocal) {
+            if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) {
+                return NO_ERROR;
+            }
+            api = BufferQueueCore::CURRENTLY_CONNECTED_API;
+        }
+
         mCore->waitWhileAllocatingLocked();
 
         if (mCore->mIsAbandoned) {
@@ -1171,9 +1191,9 @@
                     mCore->freeAllBuffersLocked();
 
                     // Remove our death notification callback if we have one
-                    if (mCore->mConnectedProducerListener != NULL) {
+                    if (mCore->mLinkedToDeath != NULL) {
                         sp<IBinder> token =
-                                IInterface::asBinder(mCore->mConnectedProducerListener);
+                                IInterface::asBinder(mCore->mLinkedToDeath);
                         // This can fail if we're here because of the death
                         // notification, but we just ignore it
                         token->unlinkToDeath(
@@ -1181,8 +1201,10 @@
                     }
                     mCore->mSharedBufferSlot =
                             BufferQueueCore::INVALID_BUFFER_SLOT;
+                    mCore->mLinkedToDeath = NULL;
                     mCore->mConnectedProducerListener = NULL;
                     mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
+                    mCore->mConnectedPid = -1;
                     mCore->mSidebandStream.clear();
                     mCore->mDequeueCondition.broadcast();
                     listener = mCore->mConsumerListener;
@@ -1340,14 +1362,6 @@
     return mConsumerName;
 }
 
-uint64_t BufferQueueProducer::getNextFrameNumber() const {
-    ATRACE_CALL();
-
-    Mutex::Autolock lock(mCore->mMutex);
-    uint64_t nextFrameNumber = mCore->mFrameCounter + 1;
-    return nextFrameNumber;
-}
-
 status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) {
     ATRACE_CALL();
     BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode);
@@ -1416,6 +1430,22 @@
     return NO_ERROR;
 }
 
+bool BufferQueueProducer::getFrameTimestamps(uint64_t frameNumber,
+        FrameTimestamps* outTimestamps) const {
+    ATRACE_CALL();
+    BQ_LOGV("getFrameTimestamps, %" PRIu64, frameNumber);
+    sp<IConsumerListener> listener;
+
+    {
+        Mutex::Autolock lock(mCore->mMutex);
+        listener = mCore->mConsumerListener;
+    }
+    if (listener != NULL) {
+        return listener->getFrameTimestamps(frameNumber, outTimestamps);
+    }
+    return false;
+}
+
 void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
     // If we're here, it means that a producer we were connected to died.
     // We're guaranteed that we are still connected to it because we remove
@@ -1425,4 +1455,11 @@
     disconnect(api);
 }
 
+status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const {
+    BQ_LOGV("getUniqueId");
+
+    *outId = mCore->mUniqueId;
+    return NO_ERROR;
+}
+
 } // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 5d2e5cf..5546d54 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -235,6 +235,25 @@
     return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
 }
 
+status_t ConsumerBase::getOccupancyHistory(bool forceFlush,
+        std::vector<OccupancyTracker::Segment>* outHistory) {
+    Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("getOccupancyHistory: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+    return mConsumer->getOccupancyHistory(forceFlush, outHistory);
+}
+
+status_t ConsumerBase::discardFreeBuffers() {
+    Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("discardFreeBuffers: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+    return mConsumer->discardFreeBuffers();
+}
+
 void ConsumerBase::dumpState(String8& result) const {
     dumpState(result, "");
 }
@@ -296,9 +315,10 @@
     if (!mSlots[slot].mFence.get()) {
         mSlots[slot].mFence = fence;
     } else {
+        char fenceName[32] = {};
+        snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
         sp<Fence> mergedFence = Fence::merge(
-                String8::format("%.28s:%d", mName.string(), slot),
-                mSlots[slot].mFence, fence);
+                fenceName, mSlots[slot].mFence, fence);
         if (!mergedFence.get()) {
             CB_LOGE("failed to merge release fences");
             // synchronization is broken, the best we can do is hope fences
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index aa0db45..10e999c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -1228,14 +1228,19 @@
         EGL_NONE,
     };
     if (!crop.isValid()) {
-        // No crop rect to set, so terminate the attrib array before the crop.
-        attrs[2] = EGL_NONE;
+        // No crop rect to set, so leave the crop out of the attrib array. Make
+        // sure to propagate the protected content attrs if they are set.
+        attrs[2] = attrs[10];
+        attrs[3] = attrs[11];
+        attrs[4] = EGL_NONE;
     } else if (!isEglImageCroppable(crop)) {
         // The crop rect is not at the origin, so we can't set the crop on the
         // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
         // extension.  In the future we can add a layered extension that
         // removes this restriction if there is hardware that can support it.
-        attrs[2] = EGL_NONE;
+        attrs[2] = attrs[10];
+        attrs[3] = attrs[11];
+        attrs[4] = EGL_NONE;
     }
     eglInitialize(dpy, 0, 0);
     EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
diff --git a/libs/gui/GraphicBufferAlloc.cpp b/libs/gui/GraphicBufferAlloc.cpp
index e6150f4..a8f40e0 100644
--- a/libs/gui/GraphicBufferAlloc.cpp
+++ b/libs/gui/GraphicBufferAlloc.cpp
@@ -15,7 +15,7 @@
  ** limitations under the License.
  */
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <ui/GraphicBuffer.h>
 
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 4382ee8..ff7b83a 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -31,6 +31,7 @@
     ON_FRAME_AVAILABLE = IBinder::FIRST_CALL_TRANSACTION,
     ON_BUFFER_RELEASED,
     ON_SIDEBAND_STREAM_CHANGED,
+    GET_FRAME_TIMESTAMPS
 };
 
 class BpConsumerListener : public BpInterface<IConsumerListener>
@@ -60,6 +61,42 @@
         data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
         remote()->transact(ON_SIDEBAND_STREAM_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    virtual bool getFrameTimestamps(uint64_t frameNumber,
+            FrameTimestamps* outTimestamps) const {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(
+                IConsumerListener::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to write token: %d", result);
+            return false;
+        }
+        result = data.writeUint64(frameNumber);
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to write: %d", result);
+            return false;
+        }
+        result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to transact: %d", result);
+            return false;
+        }
+        bool found = false;
+        result = reply.readBool(&found);
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to read: %d", result);
+            return false;
+        }
+        if (found) {
+            result = reply.read(*outTimestamps);
+            if (result != NO_ERROR) {
+                ALOGE("getFrameTimestamps failed to read timestamps: %d",
+                        result);
+                return false;
+            }
+        }
+        return found;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -88,6 +125,30 @@
             CHECK_INTERFACE(IConsumerListener, data, reply);
             onSidebandStreamChanged();
             return NO_ERROR; }
+        case GET_FRAME_TIMESTAMPS: {
+            CHECK_INTERFACE(IConsumerListener, data, reply);
+            uint64_t frameNumber = 0;
+            status_t result = data.readUint64(&frameNumber);
+            if (result != NO_ERROR) {
+                ALOGE("onTransact failed to read: %d", result);
+                return result;
+            }
+            FrameTimestamps timestamps;
+            bool found = getFrameTimestamps(frameNumber, &timestamps);
+            result = reply->writeBool(found);
+            if (result != NO_ERROR) {
+                ALOGE("onTransact failed to write: %d", result);
+                return result;
+            }
+            if (found) {
+                result = reply->write(timestamps);
+                if (result != NO_ERROR) {
+                    ALOGE("onTransact failed to write timestamps: %d", result);
+                    return result;
+                }
+            }
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 6d33a10..2401464 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,6 +51,8 @@
     SET_CONSUMER_USAGE_BITS,
     SET_TRANSFORM_HINT,
     GET_SIDEBAND_STREAM,
+    GET_OCCUPANCY_HISTORY,
+    DISCARD_FREE_BUFFERS,
     DUMP,
 };
 
@@ -260,6 +262,46 @@
         return stream;
     }
 
+    virtual status_t getOccupancyHistory(bool forceFlush,
+            std::vector<OccupancyTracker::Segment>* outHistory) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+        status_t error = data.writeBool(forceFlush);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = remote()->transact(GET_OCCUPANCY_HISTORY, data,
+                &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        error = reply.readParcelableVector(outHistory);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        status_t result = NO_ERROR;
+        error = reply.readInt32(&result);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        return result;
+    }
+
+    virtual status_t discardFreeBuffers() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
+        status_t error = remote()->transact(DISCARD_FREE_BUFFERS, data, &reply);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        int32_t result = NO_ERROR;
+        error = reply.readInt32(&result);
+        if (error != NO_ERROR) {
+            return error;
+        }
+        return result;
+    }
+
     virtual void dumpState(String8& result, const char* prefix) const {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
@@ -409,6 +451,31 @@
             }
             return NO_ERROR;
         }
+        case GET_OCCUPANCY_HISTORY: {
+            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+            bool forceFlush = false;
+            status_t error = data.readBool(&forceFlush);
+            if (error != NO_ERROR) {
+                return error;
+            }
+            std::vector<OccupancyTracker::Segment> history;
+            status_t result = getOccupancyHistory(forceFlush, &history);
+            error = reply->writeParcelableVector(history);
+            if (error != NO_ERROR) {
+                return error;
+            }
+            error = reply->writeInt32(result);
+            if (error != NO_ERROR) {
+                return error;
+            }
+            return NO_ERROR;
+        }
+        case DISCARD_FREE_BUFFERS: {
+            CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
+            status_t result = discardFreeBuffers();
+            status_t error = reply->writeInt32(result);
+            return error;
+        }
         case DUMP: {
             CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
             String8 result = data.readString8();
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 81e9407..846c205 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -50,11 +50,12 @@
     GET_CONSUMER_NAME,
     SET_MAX_DEQUEUED_BUFFER_COUNT,
     SET_ASYNC_MODE,
-    GET_NEXT_FRAME_NUMBER,
     SET_SHARED_BUFFER_MODE,
     SET_AUTO_REFRESH,
     SET_DEQUEUE_TIMEOUT,
     GET_LAST_QUEUED_BUFFER,
+    GET_FRAME_TIMESTAMPS,
+    GET_UNIQUE_ID
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -132,7 +133,11 @@
         bool nonNull = reply.readInt32();
         if (nonNull) {
             *fence = new Fence();
-            reply.read(**fence);
+            result = reply.read(**fence);
+            if (result != NO_ERROR) {
+                fence->clear();
+                return result;
+            }
         }
         result = reply.readInt32();
         return result;
@@ -170,12 +175,21 @@
             bool nonNull = reply.readInt32();
             if (nonNull) {
                 *outBuffer = new GraphicBuffer;
-                reply.read(**outBuffer);
+                result = reply.read(**outBuffer);
+                if (result != NO_ERROR) {
+                    outBuffer->clear();
+                    return result;
+                }
             }
             nonNull = reply.readInt32();
             if (nonNull) {
                 *outFence = new Fence;
-                reply.read(**outFence);
+                result = reply.read(**outFence);
+                if (result != NO_ERROR) {
+                    outBuffer->clear();
+                    outFence->clear();
+                    return result;
+                }
             }
         }
         return result;
@@ -256,10 +270,11 @@
         return result;
     }
 
-    virtual status_t disconnect(int api) {
+    virtual status_t disconnect(int api, DisconnectMode mode) {
         Parcel data, reply;
         data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
         data.writeInt32(api);
+        data.writeInt32(static_cast<int32_t>(mode));
         status_t result =remote()->transact(DISCONNECT, data, &reply);
         if (result != NO_ERROR) {
             return result;
@@ -332,18 +347,6 @@
         return reply.readString8();
     }
 
-    virtual uint64_t getNextFrameNumber() const {
-        Parcel data, reply;
-        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
-        status_t result = remote()->transact(GET_NEXT_FRAME_NUMBER, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("getNextFrameNumber failed to transact: %d", result);
-            return 0;
-        }
-        uint64_t frameNumber = reply.readUint64();
-        return frameNumber;
-    }
-
     virtual status_t setSharedBufferMode(bool sharedBufferMode) {
         Parcel data, reply;
         data.writeInterfaceToken(
@@ -418,6 +421,61 @@
         *outFence = fence;
         return result;
     }
+
+    virtual bool getFrameTimestamps(uint64_t frameNumber,
+                FrameTimestamps* outTimestamps) const {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(
+                IGraphicBufferProducer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to write token: %d", result);
+            return false;
+        }
+        result = data.writeUint64(frameNumber);
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to write: %d", result);
+            return false;
+        }
+        result = remote()->transact(GET_FRAME_TIMESTAMPS, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to transact: %d", result);
+            return false;
+        }
+        bool found = false;
+        result = reply.readBool(&found);
+        if (result != NO_ERROR) {
+            ALOGE("getFrameTimestamps failed to read: %d", result);
+            return false;
+        }
+        if (found) {
+            result = reply.read(*outTimestamps);
+            if (result != NO_ERROR) {
+                ALOGE("getFrameTimestamps failed to read timestamps: %d",
+                        result);
+                return false;
+            }
+        }
+        return found;
+    }
+
+    virtual status_t getUniqueId(uint64_t* outId) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        status_t result = remote()->transact(GET_UNIQUE_ID, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getUniqueId failed to transact: %d", result);
+        }
+        status_t actualResult = NO_ERROR;
+        result = reply.readInt32(&actualResult);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readUint64(outId);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        return actualResult;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -504,9 +562,11 @@
         case ATTACH_BUFFER: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             sp<GraphicBuffer> buffer = new GraphicBuffer();
-            data.read(*buffer.get());
+            status_t result = data.read(*buffer.get());
             int slot = 0;
-            int result = attachBuffer(&slot, buffer);
+            if (result == NO_ERROR) {
+                result = attachBuffer(&slot, buffer);
+            }
             reply->writeInt32(slot);
             reply->writeInt32(result);
             return NO_ERROR;
@@ -527,8 +587,10 @@
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int buf = data.readInt32();
             sp<Fence> fence = new Fence();
-            data.read(*fence.get());
-            status_t result = cancelBuffer(buf, fence);
+            status_t result = data.read(*fence.get());
+            if (result == NO_ERROR) {
+                result = cancelBuffer(buf, fence);
+            }
             reply->writeInt32(result);
             return NO_ERROR;
         }
@@ -560,7 +622,8 @@
         case DISCONNECT: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             int api = data.readInt32();
-            status_t res = disconnect(api);
+            DisconnectMode mode = static_cast<DisconnectMode>(data.readInt32());
+            status_t res = disconnect(api, mode);
             reply->writeInt32(res);
             return NO_ERROR;
         }
@@ -602,12 +665,6 @@
             reply->writeString8(getConsumerName());
             return NO_ERROR;
         }
-        case GET_NEXT_FRAME_NUMBER: {
-            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
-            uint64_t frameNumber = getNextFrameNumber();
-            reply->writeUint64(frameNumber);
-            return NO_ERROR;
-        }
         case SET_SHARED_BUFFER_MODE: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             bool sharedBufferMode = data.readInt32();
@@ -659,6 +716,44 @@
             }
             return NO_ERROR;
         }
+        case GET_FRAME_TIMESTAMPS: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            uint64_t frameNumber = 0;
+            status_t result = data.readUint64(&frameNumber);
+            if (result != NO_ERROR) {
+                ALOGE("onTransact failed to read: %d", result);
+                return result;
+            }
+            FrameTimestamps timestamps;
+            bool found = getFrameTimestamps(frameNumber, &timestamps);
+            result = reply->writeBool(found);
+            if (result != NO_ERROR) {
+                ALOGE("onTransact failed to write: %d", result);
+                return result;
+            }
+            if (found) {
+                result = reply->write(timestamps);
+                if (result != NO_ERROR) {
+                    ALOGE("onTransact failed to write timestamps: %d", result);
+                    return result;
+                }
+            }
+            return NO_ERROR;
+        }
+        case GET_UNIQUE_ID: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            uint64_t outId = 0;
+            status_t actualResult = getUniqueId(&outId);
+            status_t result = reply->writeInt32(actualResult);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply->writeUint64(outId);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 855a488..62abfa8 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -22,6 +22,7 @@
 
 enum {
     ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
+    NEEDS_RELEASE_NOTIFY,
 };
 
 class BpProducerListener : public BpInterface<IProducerListener>
@@ -37,6 +38,23 @@
         data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
         remote()->transact(ON_BUFFER_RELEASED, data, &reply, IBinder::FLAG_ONEWAY);
     }
+
+    virtual bool needsReleaseNotify() {
+        bool result;
+        Parcel data, reply;
+        data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+        status_t err = remote()->transact(NEEDS_RELEASE_NOTIFY, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("IProducerListener: binder call \'needsReleaseNotify\' failed");
+            return true;
+        }
+        err = reply.readBool(&result);
+        if (err != NO_ERROR) {
+            ALOGE("IProducerListener: malformed binder reply");
+            return true;
+        }
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -52,6 +70,10 @@
             CHECK_INTERFACE(IProducerListener, data, reply);
             onBufferReleased();
             return NO_ERROR;
+        case NEEDS_RELEASE_NOTIFY:
+            CHECK_INTERFACE(IProducerListener, data, reply);
+            reply->writeBool(needsReleaseNotify());
+            return NO_ERROR;
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
@@ -60,4 +82,8 @@
 
 DummyProducerListener::~DummyProducerListener() = default;
 
+bool BnProducerListener::needsReleaseNotify() {
+    return true;
+}
+
 } // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 015945a..0a8e6a5 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -32,6 +32,8 @@
 
 #include <private/gui/LayerState.h>
 
+#include <system/graphics.h>
+
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
 #include <ui/HdrCapabilities.h>
@@ -269,6 +271,82 @@
         return reply.readInt32();
     }
 
+    virtual status_t getDisplayColorModes(const sp<IBinder>& display,
+            Vector<android_color_mode_t>* outColorModes) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayColorModes failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayColorModes failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getDisplayColorModes failed to transact: %d", result);
+            return result;
+        }
+        result = static_cast<status_t>(reply.readInt32());
+        if (result == NO_ERROR) {
+            size_t numModes = reply.readUint32();
+            outColorModes->clear();
+            outColorModes->resize(numModes);
+            for (size_t i = 0; i < numModes; ++i) {
+                outColorModes->replaceAt(static_cast<android_color_mode_t>(reply.readInt32()), i);
+            }
+        }
+        return result;
+    }
+
+    virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getActiveColorMode failed to writeInterfaceToken: %d", result);
+            return static_cast<android_color_mode_t>(result);
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getActiveColorMode failed to writeStrongBinder: %d", result);
+            return static_cast<android_color_mode_t>(result);
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getActiveColorMode failed to transact: %d", result);
+            return static_cast<android_color_mode_t>(result);
+        }
+        return static_cast<android_color_mode_t>(reply.readInt32());
+    }
+
+    virtual status_t setActiveColorMode(const sp<IBinder>& display,
+            android_color_mode_t colorMode) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setActiveColorMode failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setActiveColorMode failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = data.writeInt32(colorMode);
+        if (result != NO_ERROR) {
+            ALOGE("setActiveColorMode failed to writeInt32: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_COLOR_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setActiveColorMode failed to transact: %d", result);
+            return result;
+        }
+        return static_cast<status_t>(reply.readInt32());
+    }
+
     virtual status_t clearAnimationFrameStats() {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -469,6 +547,56 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case GET_DISPLAY_COLOR_MODES: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            Vector<android_color_mode_t> colorModes;
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayColorModes failed to readStrongBinder: %d", result);
+                return result;
+            }
+            result = getDisplayColorModes(display, &colorModes);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                reply->writeUint32(static_cast<uint32_t>(colorModes.size()));
+                for (size_t i = 0; i < colorModes.size(); ++i) {
+                    reply->writeInt32(colorModes[i]);
+                }
+            }
+            return NO_ERROR;
+        }
+        case GET_ACTIVE_COLOR_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getActiveColorMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            android_color_mode_t colorMode = getActiveColorMode(display);
+            result = reply->writeInt32(static_cast<int32_t>(colorMode));
+            return result;
+        }
+        case SET_ACTIVE_COLOR_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getActiveColorMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            int32_t colorModeInt = 0;
+            result = data.readInt32(&colorModeInt);
+            if (result != NO_ERROR) {
+                ALOGE("setActiveColorMode failed to readInt32: %d", result);
+                return result;
+            }
+            result = setActiveColorMode(display,
+                    static_cast<android_color_mode_t>(colorModeInt));
+            result = reply->writeInt32(result);
+            return result;
+        }
         case CLEAR_ANIMATION_FRAME_STATS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             status_t result = clearAnimationFrameStats();
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index decffbf..47cb047 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -41,7 +41,8 @@
     CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
     DESTROY_SURFACE,
     CLEAR_LAYER_FRAME_STATS,
-    GET_LAYER_FRAME_STATS
+    GET_LAYER_FRAME_STATS,
+    GET_TRANSFORM_TO_DISPLAY_INVERSE
 };
 
 class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
@@ -94,6 +95,35 @@
         reply.read(*outStats);
         return reply.readInt32();
     }
+
+    virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
+            bool* outTransformToDisplayInverse) const {
+        Parcel data, reply;
+        status_t result =
+                data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = data.writeStrongBinder(handle);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        int transformInverse;
+        result = reply.readInt32(&transformInverse);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        *outTransformToDisplayInverse = transformInverse != 0 ? true : false;
+        status_t result2 = reply.readInt32(&result);
+        if (result2 != NO_ERROR) {
+            return result2;
+        }
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -145,6 +175,25 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case GET_TRANSFORM_TO_DISPLAY_INVERSE: {
+            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+            sp<IBinder> handle;
+            status_t result = data.readStrongBinder(&handle);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            bool transformInverse = false;
+            result = getTransformToDisplayInverse(handle, &transformInverse);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply->writeInt32(transformInverse ? 1 : 0);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            result = reply->writeInt32(NO_ERROR);
+            return result;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/gui/OccupancyTracker.cpp b/libs/gui/OccupancyTracker.cpp
new file mode 100644
index 0000000..9687aaf
--- /dev/null
+++ b/libs/gui/OccupancyTracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "OccupancyTracker"
+
+#include <gui/OccupancyTracker.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+status_t OccupancyTracker::Segment::writeToParcel(Parcel* parcel) const {
+    status_t result = parcel->writeInt64(totalTime);
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->writeUint64(static_cast<uint64_t>(numFrames));
+    if (result != OK) {
+        return result;
+    }
+    result = parcel->writeFloat(occupancyAverage);
+    if (result != OK) {
+        return result;
+    }
+    return parcel->writeBool(usedThirdBuffer);
+}
+
+status_t OccupancyTracker::Segment::readFromParcel(const Parcel* parcel) {
+    status_t result = parcel->readInt64(&totalTime);
+    if (result != OK) {
+        return result;
+    }
+    uint64_t uintNumFrames = 0;
+    result = parcel->readUint64(&uintNumFrames);
+    if (result != OK) {
+        return result;
+    }
+    numFrames = static_cast<size_t>(uintNumFrames);
+    result = parcel->readFloat(&occupancyAverage);
+    if (result != OK) {
+        return result;
+    }
+    return parcel->readBool(&usedThirdBuffer);
+}
+
+void OccupancyTracker::registerOccupancyChange(size_t occupancy) {
+    ATRACE_CALL();
+    nsecs_t now = systemTime();
+    nsecs_t delta = now - mLastOccupancyChangeTime;
+    if (delta > NEW_SEGMENT_DELAY) {
+        recordPendingSegment();
+    } else {
+        mPendingSegment.totalTime += delta;
+        if (mPendingSegment.mOccupancyTimes.count(mLastOccupancy)) {
+            mPendingSegment.mOccupancyTimes[mLastOccupancy] += delta;
+        } else {
+            mPendingSegment.mOccupancyTimes[mLastOccupancy] = delta;
+        }
+    }
+    if (occupancy > mLastOccupancy) {
+        ++mPendingSegment.numFrames;
+    }
+    mLastOccupancyChangeTime = now;
+    mLastOccupancy = occupancy;
+}
+
+std::vector<OccupancyTracker::Segment> OccupancyTracker::getSegmentHistory(
+        bool forceFlush) {
+    if (forceFlush) {
+        recordPendingSegment();
+    }
+    std::vector<Segment> segments(mSegmentHistory.cbegin(),
+            mSegmentHistory.cend());
+    mSegmentHistory.clear();
+    return segments;
+}
+
+void OccupancyTracker::recordPendingSegment() {
+    // Only record longer segments to get a better measurement of actual double-
+    // vs. triple-buffered time
+    if (mPendingSegment.numFrames > LONG_SEGMENT_THRESHOLD) {
+        float occupancyAverage = 0.0f;
+        bool usedThirdBuffer = false;
+        for (const auto& timePair : mPendingSegment.mOccupancyTimes) {
+            size_t occupancy = timePair.first;
+            float timeRatio = static_cast<float>(timePair.second) /
+                    mPendingSegment.totalTime;
+            occupancyAverage += timeRatio * occupancy;
+            usedThirdBuffer = usedThirdBuffer || (occupancy > 1);
+        }
+        mSegmentHistory.push_front({mPendingSegment.totalTime,
+                mPendingSegment.numFrames, occupancyAverage, usedThirdBuffer});
+        if (mSegmentHistory.size() > MAX_HISTORY_SIZE) {
+            mSegmentHistory.pop_back();
+        }
+    }
+    mPendingSegment.clear();
+}
+
+} // namespace android
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index 053d153..7510429 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
+#include <stdint.h>
+#include <sys/limits.h>
+#include <sys/types.h>
 
 #include <binder/AppOpsManager.h>
 #include <binder/IServiceManager.h>
@@ -24,11 +28,6 @@
 #include <utils/String8.h>
 #include <utils/Flattenable.h>
 
-#include <inttypes.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/limits.h>
-
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 1879f8a..d1a9cbb 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -48,7 +48,8 @@
       mSharedBufferMode(false),
       mAutoRefresh(false),
       mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
-      mSharedBufferHasBeenQueued(false)
+      mSharedBufferHasBeenQueued(false),
+      mNextFrameNumber(1)
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
@@ -116,7 +117,8 @@
 }
 
 uint64_t Surface::getNextFrameNumber() const {
-    return mGraphicBufferProducer->getNextFrameNumber();
+    Mutex::Autolock lock(mMutex);
+    return mNextFrameNumber;
 }
 
 String8 Surface::getConsumerName() const {
@@ -133,6 +135,39 @@
             outTransformMatrix);
 }
 
+bool Surface::getFrameTimestamps(uint64_t frameNumber, nsecs_t* outPostedTime,
+        nsecs_t* outAcquireTime, nsecs_t* outRefreshStartTime,
+        nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
+        nsecs_t* outReleaseTime) {
+    ATRACE_CALL();
+
+    FrameTimestamps timestamps;
+    bool found = mGraphicBufferProducer->getFrameTimestamps(frameNumber,
+            &timestamps);
+    if (found) {
+        if (outPostedTime) {
+            *outPostedTime = timestamps.postedTime;
+        }
+        if (outAcquireTime) {
+            *outAcquireTime = timestamps.acquireTime;
+        }
+        if (outRefreshStartTime) {
+            *outRefreshStartTime = timestamps.refreshStartTime;
+        }
+        if (outGlCompositionDoneTime) {
+            *outGlCompositionDoneTime = timestamps.glCompositionDoneTime;
+        }
+        if (outDisplayRetireTime) {
+            *outDisplayRetireTime = timestamps.displayRetireTime;
+        }
+        if (outReleaseTime) {
+            *outReleaseTime = timestamps.releaseTime;
+        }
+        return true;
+    }
+    return false;
+}
+
 int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) {
     Surface* c = getSelf(window);
     return c->setSwapInterval(interval);
@@ -259,8 +294,10 @@
 
     int buf = -1;
     sp<Fence> fence;
+    nsecs_t now = systemTime();
     status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
             reqWidth, reqHeight, reqFormat, reqUsage);
+    mLastDequeueDuration = systemTime() - now;
 
     if (result < 0) {
         ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -463,7 +500,9 @@
         input.setSurfaceDamage(flippedRegion);
     }
 
+    nsecs_t now = systemTime();
     status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+    mLastQueueDuration = systemTime() - now;
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
@@ -471,7 +510,7 @@
     uint32_t numPendingBuffers = 0;
     uint32_t hint = 0;
     output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
-            &numPendingBuffers);
+            &numPendingBuffers, &mNextFrameNumber);
 
     // Disable transform hint if sticky transform is set.
     if (mStickyTransform == 0) {
@@ -542,6 +581,20 @@
                 }
                 return err;
             }
+            case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: {
+                int64_t durationUs = mLastDequeueDuration / 1000;
+                *value = durationUs > std::numeric_limits<int>::max() ?
+                        std::numeric_limits<int>::max() :
+                        static_cast<int>(durationUs);
+                return NO_ERROR;
+            }
+            case NATIVE_WINDOW_LAST_QUEUE_DURATION: {
+                int64_t durationUs = mLastQueueDuration / 1000;
+                *value = durationUs > std::numeric_limits<int>::max() ?
+                        std::numeric_limits<int>::max() :
+                        static_cast<int>(durationUs);
+                return NO_ERROR;
+            }
         }
     }
     return mGraphicBufferProducer->query(what, value);
@@ -617,6 +670,9 @@
     case NATIVE_WINDOW_SET_AUTO_REFRESH:
         res = dispatchSetAutoRefresh(args);
         break;
+    case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS:
+        res = dispatchGetFrameTimestamps(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -737,6 +793,20 @@
     return setAutoRefresh(autoRefresh);
 }
 
+int Surface::dispatchGetFrameTimestamps(va_list args) {
+    uint32_t framesAgo = va_arg(args, uint32_t);
+    nsecs_t* outPostedTime = va_arg(args, int64_t*);
+    nsecs_t* outAcquireTime = va_arg(args, int64_t*);
+    nsecs_t* outRefreshStartTime = va_arg(args, int64_t*);
+    nsecs_t* outGlCompositionDoneTime = va_arg(args, int64_t*);
+    nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
+    nsecs_t* outReleaseTime = va_arg(args, int64_t*);
+    bool ret = getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
+            outPostedTime, outAcquireTime, outRefreshStartTime,
+            outGlCompositionDoneTime, outDisplayRetireTime, outReleaseTime);
+    return ret ? NO_ERROR : BAD_VALUE;
+}
+
 int Surface::connect(int api) {
     static sp<IProducerListener> listener = new DummyProducerListener();
     return connect(api, listener);
@@ -752,7 +822,7 @@
         uint32_t numPendingBuffers = 0;
         uint32_t hint = 0;
         output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
-                &numPendingBuffers);
+                &numPendingBuffers, &mNextFrameNumber);
 
         // Disable transform hint if sticky transform is set.
         if (mStickyTransform == 0) {
@@ -774,14 +844,14 @@
 }
 
 
-int Surface::disconnect(int api) {
+int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) {
     ATRACE_CALL();
     ALOGV("Surface::disconnect");
     Mutex::Autolock lock(mMutex);
     mSharedBufferSlot = BufferItem::INVALID_BUFFER_SLOT;
     mSharedBufferHasBeenQueued = false;
     freeAllBuffers();
-    int err = mGraphicBufferProducer->disconnect(api);
+    int err = mGraphicBufferProducer->disconnect(api, mode);
     if (!err) {
         mReqFormat = 0;
         mReqWidth = 0;
@@ -1102,7 +1172,8 @@
 static status_t copyBlt(
         const sp<GraphicBuffer>& dst,
         const sp<GraphicBuffer>& src,
-        const Region& reg)
+        const Region& reg,
+        int *dstFenceFd)
 {
     // src and dst with, height and format must be identical. no verification
     // is done here.
@@ -1113,9 +1184,10 @@
     ALOGE_IF(err, "error locking src buffer %s", strerror(-err));
 
     uint8_t* dst_bits = NULL;
-    err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
-            reinterpret_cast<void**>(&dst_bits));
+    err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(),
+            reinterpret_cast<void**>(&dst_bits), *dstFenceFd);
     ALOGE_IF(err, "error locking dst buffer %s", strerror(-err));
+    *dstFenceFd = -1;
 
     Region::const_iterator head(reg.begin());
     Region::const_iterator tail(reg.end());
@@ -1149,7 +1221,7 @@
         src->unlock();
 
     if (dst_bits)
-        dst->unlock();
+        dst->unlockAsync(dstFenceFd);
 
     return err;
 }
@@ -1199,8 +1271,9 @@
         if (canCopyBack) {
             // copy the area that is invalid and not repainted this round
             const Region copyback(mDirtyRegion.subtract(newDirtyRegion));
-            if (!copyback.isEmpty())
-                copyBlt(backBuffer, frontBuffer, copyback);
+            if (!copyback.isEmpty()) {
+                copyBlt(backBuffer, frontBuffer, copyback, &fenceFd);
+            }
         } else {
             // if we can't copy-back anything, modify the user's dirty
             // region to make sure they redraw the whole buffer
@@ -1272,13 +1345,17 @@
 
 bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
     Mutex::Autolock lock(mMutex);
-    uint64_t currentFrame = mGraphicBufferProducer->getNextFrameNumber();
-    if (currentFrame > lastFrame) {
+    if (mNextFrameNumber > lastFrame) {
       return true;
     }
     return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
 }
 
+status_t Surface::getUniqueId(uint64_t* outId) const {
+    Mutex::Autolock lock(mMutex);
+    return mGraphicBufferProducer->getUniqueId(outId);
+}
+
 namespace view {
 
 status_t Surface::writeToParcel(Parcel* parcel) const {
@@ -1290,12 +1367,18 @@
 
     status_t res = OK;
 
-    if (!nameAlreadyWritten) res = parcel->writeString16(name);
+    if (!nameAlreadyWritten) {
+        res = parcel->writeString16(name);
+        if (res != OK) return res;
 
-    if (res == OK) {
-        res = parcel->writeStrongBinder(
-                IGraphicBufferProducer::asBinder(graphicBufferProducer));
+        /* isSingleBuffered defaults to no */
+        res = parcel->writeInt32(0);
+        if (res != OK) return res;
     }
+
+    res = parcel->writeStrongBinder(
+            IGraphicBufferProducer::asBinder(graphicBufferProducer));
+
     return res;
 }
 
@@ -1306,13 +1389,20 @@
 status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) {
     if (parcel == nullptr) return BAD_VALUE;
 
+    status_t res = OK;
     if (!nameAlreadyRead) {
         name = readMaybeEmptyString16(parcel);
+        // Discard this for now
+        int isSingleBuffered;
+        res = parcel->readInt32(&isSingleBuffered);
+        if (res != OK) {
+            return res;
+        }
     }
 
     sp<IBinder> binder;
 
-    status_t res = parcel->readStrongBinder(&binder);
+    res = parcel->readStrongBinder(&binder);
     if (res != OK) return res;
 
     graphicBufferProducer = interface_cast<IGraphicBufferProducer>(binder);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2189047..43506e9 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -29,6 +29,8 @@
 #include <binder/IMemory.h>
 #include <binder/IServiceManager.h>
 
+#include <system/graphics.h>
+
 #include <ui/DisplayInfo.h>
 
 #include <gui/CpuConsumer.h>
@@ -165,11 +167,11 @@
             uint64_t frameNumber);
     status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id, int32_t overrideScalingMode);
-    status_t setPositionAppliesWithResize(const sp<SurfaceComposerClient>& client,
+    status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
             const sp<IBinder>& id);
 
-    void setDisplaySurface(const sp<IBinder>& token,
-            const sp<IGraphicBufferProducer>& bufferProducer);
+    status_t setDisplaySurface(const sp<IBinder>& token,
+            sp<IGraphicBufferProducer> bufferProducer);
     void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
     void setDisplayProjection(const sp<IBinder>& token,
             uint32_t orientation,
@@ -445,7 +447,7 @@
     return NO_ERROR;
 }
 
-status_t Composer::setPositionAppliesWithResize(
+status_t Composer::setGeometryAppliesWithResize(
         const sp<SurfaceComposerClient>& client,
         const sp<IBinder>& id) {
     Mutex::Autolock lock(mLock);
@@ -453,7 +455,7 @@
     if (!s) {
         return BAD_INDEX;
     }
-    s->what |= layer_state_t::ePositionAppliesWithResize;
+    s->what |= layer_state_t::eGeometryAppliesWithResize;
     return NO_ERROR;
 }
 
@@ -471,12 +473,24 @@
     return mDisplayStates.editItemAt(static_cast<size_t>(index));
 }
 
-void Composer::setDisplaySurface(const sp<IBinder>& token,
-        const sp<IGraphicBufferProducer>& bufferProducer) {
+status_t Composer::setDisplaySurface(const sp<IBinder>& token,
+        sp<IGraphicBufferProducer> bufferProducer) {
+    if (bufferProducer.get() != nullptr) {
+        // Make sure that composition can never be stalled by a virtual display
+        // consumer that isn't processing buffers fast enough.
+        status_t err = bufferProducer->setAsyncMode(true);
+        if (err != NO_ERROR) {
+            ALOGE("Composer::setDisplaySurface Failed to enable async mode on the "
+                    "BufferQueue. This BufferQueue cannot be used for virtual "
+                    "display. (%d)", err);
+            return err;
+        }
+    }
     Mutex::Autolock _l(mLock);
     DisplayState& s(getDisplayStateLocked(token));
     s.surface = bufferProducer;
     s.what |= DisplayState::eSurfaceChanged;
+    return NO_ERROR;
 }
 
 void Composer::setDisplayLayerStack(const sp<IBinder>& token,
@@ -612,6 +626,14 @@
     return mClient->getLayerFrameStats(token, outStats);
 }
 
+status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token,
+        bool* outTransformToDisplayInverse) const {
+    if (mStatus != NO_ERROR) {
+        return mStatus;
+    }
+    return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse);
+}
+
 inline Composer& SurfaceComposerClient::getComposer() {
     return mComposer;
 }
@@ -699,16 +721,16 @@
             this, id, overrideScalingMode);
 }
 
-status_t SurfaceComposerClient::setPositionAppliesWithResize(
+status_t SurfaceComposerClient::setGeometryAppliesWithResize(
         const sp<IBinder>& id) {
-    return getComposer().setPositionAppliesWithResize(this, id);
+    return getComposer().setGeometryAppliesWithResize(this, id);
 }
 
 // ----------------------------------------------------------------------------
 
-void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
-        const sp<IGraphicBufferProducer>& bufferProducer) {
-    Composer::getInstance().setDisplaySurface(token, bufferProducer);
+status_t SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
+        sp<IGraphicBufferProducer> bufferProducer) {
+    return Composer::getInstance().setDisplaySurface(token, bufferProducer);
 }
 
 void SurfaceComposerClient::setDisplayLayerStack(const sp<IBinder>& token,
@@ -763,6 +785,20 @@
     return ComposerService::getComposerService()->setActiveConfig(display, id);
 }
 
+status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
+        Vector<android_color_mode_t>* outColorModes) {
+    return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
+}
+
+android_color_mode_t SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) {
+    return ComposerService::getComposerService()->getActiveColorMode(display);
+}
+
+status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display,
+        android_color_mode_t colorMode) {
+    return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
+}
+
 void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
         int mode) {
     ComposerService::getComposerService()->setPowerMode(token, mode);
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 4671e50..33c1d90 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -112,10 +112,10 @@
     if (err < 0) return err;
     return mClient->setPosition(mHandle, x, y);
 }
-status_t SurfaceControl::setPositionAppliesWithResize() {
+status_t SurfaceControl::setGeometryAppliesWithResize() {
     status_t err = validate();
     if (err < 0) return err;
-    return mClient->setPositionAppliesWithResize(mHandle);
+    return mClient->setGeometryAppliesWithResize(mHandle);
 }
 status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
     status_t err = validate();
@@ -190,6 +190,13 @@
     return client->getLayerFrameStats(mHandle, outStats);
 }
 
+status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const {
+    status_t err = validate();
+    if (err < 0) return err;
+    const sp<SurfaceComposerClient>& client(mClient);
+    return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse);
+}
+
 status_t SurfaceControl::validate() const
 {
     if (mHandle==0 || mClient==0) {
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 85d63b4..65df7dc 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -34,6 +34,10 @@
 
 #include <gtest/gtest.h>
 
+#include <thread>
+
+using namespace std::chrono_literals;
+
 namespace android {
 
 class BufferQueueTest : public ::testing::Test {
@@ -850,4 +854,217 @@
             returnedBuffer->getNativeBuffer()->handle);
 }
 
+TEST_F(BufferQueueTest, TestOccupancyHistory) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, false, &output));
+
+    int slot = BufferQueue::INVALID_BUFFER_SLOT;
+    sp<Fence> fence = Fence::NO_FENCE;
+    sp<GraphicBuffer> buffer = nullptr;
+    IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+        HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    BufferItem item{};
+
+    // Preallocate, dequeue, request, and cancel 3 buffers so we don't get
+    // BUFFER_NEEDS_REALLOCATION below
+    int slots[3] = {};
+    mProducer->setMaxDequeuedBufferCount(3);
+    for (size_t i = 0; i < 3; ++i) {
+        status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+                0, 0, 0, 0);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+        ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+    }
+    for (size_t i = 0; i < 3; ++i) {
+        ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+    }
+
+    // Create 3 segments
+
+    // The first segment is a two-buffer segment, so we only put one buffer into
+    // the queue at a time
+    for (size_t i = 0; i < 5; ++i) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+        ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+        ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+        std::this_thread::sleep_for(16ms);
+    }
+
+    // Sleep between segments
+    std::this_thread::sleep_for(500ms);
+
+    // The second segment is a double-buffer segment. It starts the same as the
+    // two-buffer segment, but then at the end, we put two buffers in the queue
+    // at the same time before draining it.
+    for (size_t i = 0; i < 5; ++i) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+        ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+        ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+        std::this_thread::sleep_for(16ms);
+    }
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+    std::this_thread::sleep_for(16ms);
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+    // Sleep between segments
+    std::this_thread::sleep_for(500ms);
+
+    // The third segment is a triple-buffer segment, so the queue is switching
+    // between one buffer and two buffers deep.
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    for (size_t i = 0; i < 5; ++i) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+        ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+        ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+        std::this_thread::sleep_for(16ms);
+    }
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+            EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+    // Now we read the segments
+    std::vector<OccupancyTracker::Segment> history;
+    ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+
+    // Since we didn't force a flush, we should only get the first two segments
+    // (since the third segment hasn't been closed out by the appearance of a
+    // new segment yet)
+    ASSERT_EQ(2u, history.size());
+
+    // The first segment (which will be history[1], since the newest segment
+    // should be at the front of the vector) should be a two-buffer segment,
+    // which implies that the occupancy average should be between 0 and 1, and
+    // usedThirdBuffer should be false
+    const auto& firstSegment = history[1];
+    ASSERT_EQ(5u, firstSegment.numFrames);
+    ASSERT_LT(0, firstSegment.occupancyAverage);
+    ASSERT_GT(1, firstSegment.occupancyAverage);
+    ASSERT_EQ(false, firstSegment.usedThirdBuffer);
+
+    // The second segment should be a double-buffered segment, which implies that
+    // the occupancy average should be between 0 and 1, but usedThirdBuffer
+    // should be true
+    const auto& secondSegment = history[0];
+    ASSERT_EQ(7u, secondSegment.numFrames);
+    ASSERT_LT(0, secondSegment.occupancyAverage);
+    ASSERT_GT(1, secondSegment.occupancyAverage);
+    ASSERT_EQ(true, secondSegment.usedThirdBuffer);
+
+    // If we read the segments again without flushing, we shouldn't get any new
+    // segments
+    ASSERT_EQ(OK, mConsumer->getOccupancyHistory(false, &history));
+    ASSERT_EQ(0u, history.size());
+
+    // Read the segments again, this time forcing a flush so we get the third
+    // segment
+    ASSERT_EQ(OK, mConsumer->getOccupancyHistory(true, &history));
+    ASSERT_EQ(1u, history.size());
+
+    // This segment should be a triple-buffered segment, which implies that the
+    // occupancy average should be between 1 and 2, and usedThirdBuffer should
+    // be true
+    const auto& thirdSegment = history[0];
+    ASSERT_EQ(6u, thirdSegment.numFrames);
+    ASSERT_LT(1, thirdSegment.occupancyAverage);
+    ASSERT_GT(2, thirdSegment.occupancyAverage);
+    ASSERT_EQ(true, thirdSegment.usedThirdBuffer);
+}
+
+TEST_F(BufferQueueTest, TestDiscardFreeBuffers) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, false, &output));
+
+    int slot = BufferQueue::INVALID_BUFFER_SLOT;
+    sp<Fence> fence = Fence::NO_FENCE;
+    sp<GraphicBuffer> buffer = nullptr;
+    IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+        HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    BufferItem item{};
+
+    // Preallocate, dequeue, request, and cancel 4 buffers so we don't get
+    // BUFFER_NEEDS_REALLOCATION below
+    int slots[4] = {};
+    mProducer->setMaxDequeuedBufferCount(4);
+    for (size_t i = 0; i < 4; ++i) {
+        status_t result = mProducer->dequeueBuffer(&slots[i], &fence,
+                0, 0, 0, 0);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+        ASSERT_EQ(OK, mProducer->requestBuffer(slots[i], &buffer));
+    }
+    for (size_t i = 0; i < 4; ++i) {
+        ASSERT_EQ(OK, mProducer->cancelBuffer(slots[i], Fence::NO_FENCE));
+    }
+
+    // Get buffers in all states: dequeued, filled, acquired, free
+
+    // Fill 3 buffers
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+    // Dequeue 1 buffer
+    ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+
+    // Acquire and free 1 buffer
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+    ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                    EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+    // Acquire 1 buffer, leaving 1 filled buffer in queue
+    ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+
+    // Now discard the free buffers
+    ASSERT_EQ(OK, mConsumer->discardFreeBuffers());
+
+    // Check no free buffers in dump
+    String8 dumpString;
+    mConsumer->dumpState(dumpString, nullptr);
+
+    // Parse the dump to ensure that all buffer slots that are FREE also
+    // have a null GraphicBuffer
+    // Fragile - assumes the following format for the dump for a buffer entry:
+    // ":%p\][^:]*state=FREE" where %p is the buffer pointer in hex.
+    ssize_t idx = dumpString.find("state=FREE");
+    while (idx != -1) {
+        ssize_t bufferPtrIdx = idx - 1;
+        while (bufferPtrIdx > 0) {
+            if (dumpString[bufferPtrIdx] == ':') {
+                bufferPtrIdx++;
+                break;
+            }
+            bufferPtrIdx--;
+        }
+        ASSERT_GT(bufferPtrIdx, 0) << "Can't parse queue dump to validate";
+        ssize_t nullPtrIdx = dumpString.find("0x0]", bufferPtrIdx);
+        ASSERT_EQ(bufferPtrIdx, nullPtrIdx) << "Free buffer not discarded";
+        idx = dumpString.find("FREE", idx + 1);
+    }
+}
+
 } // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index 45b6463..9f33047 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -370,13 +370,16 @@
         uint32_t height;
         uint32_t transformHint;
         uint32_t numPendingBuffers;
+        uint64_t nextFrameNumber;
 
-        output.deflate(&width, &height, &transformHint, &numPendingBuffers);
+        output.deflate(&width, &height, &transformHint, &numPendingBuffers,
+                &nextFrameNumber);
 
         EXPECT_EQ(DEFAULT_WIDTH, width);
         EXPECT_EQ(DEFAULT_HEIGHT, height);
         EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
         EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
+        EXPECT_EQ(2u, nextFrameNumber);
     }
 
     // Buffer was not in the dequeued state
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index 81d5a57..308bd7d 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -115,13 +115,13 @@
     EXPECT_TRUE(checkPixel(63, 63,   0, 133,   0, 255));
     EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
 
-    EXPECT_TRUE(checkPixel(22, 19, 100, 255,  74, 255));
-    EXPECT_TRUE(checkPixel(45, 11, 100, 255,  74, 255));
-    EXPECT_TRUE(checkPixel(52, 12, 155,   0, 181, 255));
-    EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255));
-    EXPECT_TRUE(checkPixel(31, 54,   0,  71, 117, 255));
-    EXPECT_TRUE(checkPixel(29, 28,   0, 133,   0, 255));
-    EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255));
+    EXPECT_TRUE(checkPixel(22, 19, 100, 255,  74, 255, 3));
+    EXPECT_TRUE(checkPixel(45, 11, 100, 255,  74, 255, 3));
+    EXPECT_TRUE(checkPixel(52, 12, 155,   0, 181, 255, 3));
+    EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255, 3));
+    EXPECT_TRUE(checkPixel(31, 54,   0,  71, 117, 255, 3));
+    EXPECT_TRUE(checkPixel(29, 28,   0, 133,   0, 255, 3));
+    EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255, 3));
 }
 
 TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 2dff4e0..af1c0af 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -19,19 +19,18 @@
 // Log debug messages about touch event resampling
 #define DEBUG_RESAMPLING 0
 
-
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <math.h>
-#include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <unistd.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
-#include <input/InputTransport.h>
+#include <log/log.h>
 
+#include <input/InputTransport.h>
 
 namespace android {
 
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index bf24ffb..7cf8233 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -72,7 +72,7 @@
     return err < 0 ? -errno : status_t(NO_ERROR);
 }
 
-sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
+sp<Fence> Fence::merge(const char* name, const sp<Fence>& f1,
         const sp<Fence>& f2) {
     ATRACE_CALL();
     int result;
@@ -80,24 +80,29 @@
     // valid fence (e.g. NO_FENCE) we merge the one valid fence with itself so
     // that a new fence with the given name is created.
     if (f1->isValid() && f2->isValid()) {
-        result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd);
+        result = sync_merge(name, f1->mFenceFd, f2->mFenceFd);
     } else if (f1->isValid()) {
-        result = sync_merge(name.string(), f1->mFenceFd, f1->mFenceFd);
+        result = sync_merge(name, f1->mFenceFd, f1->mFenceFd);
     } else if (f2->isValid()) {
-        result = sync_merge(name.string(), f2->mFenceFd, f2->mFenceFd);
+        result = sync_merge(name, f2->mFenceFd, f2->mFenceFd);
     } else {
         return NO_FENCE;
     }
     if (result == -1) {
         status_t err = -errno;
         ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)",
-                name.string(), f1->mFenceFd, f2->mFenceFd,
+                name, f1->mFenceFd, f2->mFenceFd,
                 strerror(-err), err);
         return NO_FENCE;
     }
     return sp<Fence>(new Fence(result));
 }
 
+sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
+        const sp<Fence>& f2) {
+    return merge(name.string(), f1, f2);
+}
+
 int Fence::dup() const {
     return ::dup(mFenceFd);
 }
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index edfff4d..f885309 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -18,8 +18,7 @@
 #define LOG_TAG "GraphicBufferAllocator"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <cutils/log.h>
-
+#include <log/log.h>
 #include <utils/Singleton.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index a430a31..b53c563 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -796,6 +796,11 @@
         return NO_MEMORY;
     }
 
+    if (numRects > (UINT32_MAX / sizeof(Rect))) {
+        android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, NULL, 0);
+        return NO_MEMORY;
+    }
+
     Region result;
     result.mStorage.clear();
     for (size_t r = 0; r < numRects; ++r) {
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index 99ea342..ccb54ea 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -65,13 +65,13 @@
 #define EGL_TRUE			1
 
 /* Out-of-band handle values */
-#define EGL_DEFAULT_DISPLAY		((EGLNativeDisplayType)0)
-#define EGL_NO_CONTEXT			((EGLContext)0)
-#define EGL_NO_DISPLAY			((EGLDisplay)0)
-#define EGL_NO_SURFACE			((EGLSurface)0)
+#define EGL_DEFAULT_DISPLAY		EGL_CAST(EGLNativeDisplayType, 0)
+#define EGL_NO_CONTEXT			EGL_CAST(EGLContext, 0)
+#define EGL_NO_DISPLAY			EGL_CAST(EGLDisplay, 0)
+#define EGL_NO_SURFACE			EGL_CAST(EGLSurface, 0)
 
 /* Out-of-band attribute value */
-#define EGL_DONT_CARE			((EGLint)-1)
+#define EGL_DONT_CARE			EGL_CAST(EGLint, -1)
 
 /* Errors / GetError return values */
 #define EGL_SUCCESS			0x3000
@@ -198,7 +198,7 @@
 #define EGL_DISPLAY_SCALING		10000
 
 /* Unknown display resolution/aspect ratio */
-#define EGL_UNKNOWN			((EGLint)-1)
+#define EGL_UNKNOWN			EGL_CAST(EGLint, -1)
 
 /* Back buffer swap behaviors */
 #define EGL_BUFFER_PRESERVED		0x3094	/* EGL_SWAP_BEHAVIOR value */
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index c72388a..8b48032 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -79,7 +79,7 @@
 #define EGL_KHR_image 1
 #define EGL_NATIVE_PIXMAP_KHR			0x30B0	/* eglCreateImageKHR target */
 typedef void *EGLImageKHR;
-#define EGL_NO_IMAGE_KHR			((EGLImageKHR)0)
+#define EGL_NO_IMAGE_KHR			EGL_CAST(EGLImageKHR, 0)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLImageKHR EGLAPIENTRY eglCreateImageKHR (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
 EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImageKHR (EGLDisplay dpy, EGLImageKHR image);
@@ -143,7 +143,7 @@
 #define EGL_SYNC_REUSABLE_KHR			0x30FA
 #define EGL_SYNC_FLUSH_COMMANDS_BIT_KHR		0x0001	/* eglClientWaitSyncKHR <flags> bitfield */
 #define EGL_FOREVER_KHR				0xFFFFFFFFFFFFFFFFull
-#define EGL_NO_SYNC_KHR				((EGLSyncKHR)0)
+#define EGL_NO_SYNC_KHR				EGL_CAST(EGLSyncKHR, 0)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLSyncKHR EGLAPIENTRY eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
 EGLAPI EGLBoolean EGLAPIENTRY eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync);
@@ -220,7 +220,7 @@
 #define EGL_SYNC_TYPE_NV			0x30ED
 #define EGL_SYNC_CONDITION_NV			0x30EE
 #define EGL_SYNC_FENCE_NV			0x30EF
-#define EGL_NO_SYNC_NV				((EGLSyncNV)0)
+#define EGL_NO_SYNC_NV				EGL_CAST(EGLSyncNV, 0)
 typedef void* EGLSyncNV;
 typedef khronos_utime_nanoseconds_t EGLTimeNV;
 #ifdef EGL_EGLEXT_PROTOTYPES
@@ -346,7 +346,7 @@
 #define EGL_KHR_stream 1
 typedef void* EGLStreamKHR;
 typedef khronos_uint64_t EGLuint64KHR;
-#define EGL_NO_STREAM_KHR			((EGLStreamKHR)0)
+#define EGL_NO_STREAM_KHR			EGL_CAST(EGLStreamKHR, 0)
 #define EGL_CONSUMER_LATENCY_USEC_KHR		0x3210
 #define EGL_PRODUCER_FRAME_KHR			0x3212
 #define EGL_CONSUMER_FRAME_KHR			0x3213
@@ -473,7 +473,7 @@
 #ifndef EGL_KHR_stream_cross_process_fd
 #define EGL_KHR_stream_cross_process_fd 1
 typedef int EGLNativeFileDescriptorKHR;
-#define EGL_NO_FILE_DESCRIPTOR_KHR		((EGLNativeFileDescriptorKHR)(-1))
+#define EGL_NO_FILE_DESCRIPTOR_KHR		EGL_CAST(EGLNativeFileDescriptorKHR, -1)
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLNativeFileDescriptorKHR EGLAPIENTRY eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream);
 EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor);
@@ -614,7 +614,7 @@
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLClientBuffer eglCreateNativeClientBufferANDROID (const EGLint *attrib_list);
 #else
-typedef EGLAPI EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (const EGLint *attrib_list);
+typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (const EGLint *attrib_list);
 #endif
 #endif
 
@@ -628,6 +628,24 @@
 #define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000
 #endif
 
+#ifndef EGL_ANDROID_get_frame_timestamps
+#define EGL_ANDROID_get_frame_timestamps 1
+#define EGL_TIMESTAMPS_ANDROID 0x314D
+#define EGL_QUEUE_TIME_ANDROID 0x314E
+#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
+#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
+#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
+#define EGL_READS_DONE_TIME_ANDROID 0x3433
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+#else
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+#endif
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 354ac22..54011c8 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -121,4 +121,10 @@
  */
 typedef khronos_int32_t EGLint;
 
+#if defined(__cplusplus)
+#define EGL_CAST(type, value) (static_cast<type>(value))
+#else
+#define EGL_CAST(type, value) ((type) (value))
+#endif
+
 #endif /* __eglplatform_h */
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index d43b61b..48bf676 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -18,16 +18,16 @@
 #include <assert.h>
 #include <atomic>
 #include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
 #include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <utils/threads.h>
 #include <ui/ANativeObjectBase.h>
@@ -1478,6 +1478,9 @@
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
 
+    if (ggl_unlikely(num_config==NULL))
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+
     GLint numConfigs = NELEM(gConfigs);
     if (!configs) {
         *num_config = numConfigs;
@@ -1497,8 +1500,8 @@
 {
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    
-    if (ggl_unlikely(num_config==0)) {
+
+    if (ggl_unlikely(num_config==NULL)) {
         return setError(EGL_BAD_PARAMETER, EGL_FALSE);
     }
 
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 6a8aac8..60c4b36 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -26,24 +26,28 @@
     name: "libEGL.ndk",
     symbol_file: "libEGL.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
 
 ndk_library {
     name: "libGLESv1_CM.ndk",
     symbol_file: "libGLESv1_CM.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
 
 ndk_library {
     name: "libGLESv2.ndk",
     symbol_file: "libGLESv2.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
 
 ndk_library {
     name: "libGLESv3.ndk",
     symbol_file: "libGLESv3.map.txt",
     first_version: "18",
+    unversioned_until: "current",
 }
 
 cc_defaults {
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 49f501d..218ab35 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -15,16 +15,16 @@
  */
 
 #include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <dlfcn.h>
-#include <limits.h>
 #include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <EGL/egl.h>
 
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 18cf261..ee83ada 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -24,10 +24,9 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <cutils/log.h>
 #include <cutils/atomic.h>
 #include <cutils/properties.h>
-
+#include <log/log.h>
 #include <utils/CallStack.h>
 #include <utils/String8.h>
 
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 0c4b9e9..a42b3f1 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -16,8 +16,8 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <dlfcn.h>
 #include <ctype.h>
+#include <dlfcn.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -27,11 +27,11 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 
-#include <cutils/log.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
-#include <cutils/properties.h>
 #include <cutils/memory.h>
+#include <cutils/properties.h>
+#include <log/log.h>
 
 #include <gui/ISurfaceComposer.h>
 
@@ -56,6 +56,8 @@
 
 using namespace android;
 
+#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0
+
 // ----------------------------------------------------------------------------
 
 namespace android {
@@ -86,6 +88,9 @@
         "EGL_KHR_swap_buffers_with_damage "
         "EGL_ANDROID_create_native_client_buffer "
         "EGL_ANDROID_front_buffer_auto_refresh "
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
+        "EGL_ANDROID_get_frame_timestamps "
+#endif
         ;
 extern char const * const gExtensionString  =
         "EGL_KHR_image "                        // mandatory
@@ -118,6 +123,7 @@
         "EGL_KHR_mutable_render_buffer "
         "EGL_EXT_yuv_surface "
         "EGL_EXT_protected_content "
+        "EGL_IMG_context_priority "
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -207,6 +213,12 @@
             (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
     { "eglCreateStreamFromFileDescriptorKHR",
             (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+    // EGL_ANDROID_get_frame_timestamps
+    { "eglGetFrameTimestampsANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+    { "eglQueryTimestampSupportedANDROID",
+            (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
 };
 
 /*
@@ -1180,7 +1192,7 @@
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
-    egl_surface_t const * const s = get_surface(surface);
+    egl_surface_t * const s = get_surface(surface);
 
     if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
         int err = native_window_set_auto_refresh(s->win.get(),
@@ -1189,6 +1201,13 @@
             setError(EGL_BAD_SURFACE, EGL_FALSE);
     }
 
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
+    if (attribute == EGL_TIMESTAMPS_ANDROID) {
+        s->enableTimestamps = value;
+        return EGL_TRUE;
+    }
+#endif
+
     if (s->cnx->egl.eglSurfaceAttrib) {
         return s->cnx->egl.eglSurfaceAttrib(
                 dp->disp.dpy, s->surface, attribute, value);
@@ -1809,12 +1828,6 @@
                     if (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID) {
                         usage |= GRALLOC_USAGE_HW_TEXTURE;
                     }
-                    // The buffer must be used for either a texture or a
-                    // renderbuffer.
-                    if ((value & EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID) &&
-                        (value & EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID)) {
-                        return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
-                    }
                     break;
                 default:
                     return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0);
@@ -1979,3 +1992,105 @@
 
     return EGL_FALSE;
 }
+
+EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
+        EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+        EGLnsecsANDROID *values)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+
+    if (!s->enableTimestamps) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    nsecs_t* postedTime = nullptr;
+    nsecs_t* acquireTime = nullptr;
+    nsecs_t* refreshStartTime = nullptr;
+    nsecs_t* GLCompositionDoneTime = nullptr;
+    nsecs_t* displayRetireTime = nullptr;
+    nsecs_t* releaseTime = nullptr;
+
+    for (int i = 0; i < numTimestamps; i++) {
+        switch (timestamps[i]) {
+            case EGL_QUEUE_TIME_ANDROID:
+                postedTime = &values[i];
+                break;
+            case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+                acquireTime = &values[i];
+                break;
+            case EGL_COMPOSITION_START_TIME_ANDROID:
+                refreshStartTime = &values[i];
+                break;
+            case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+                GLCompositionDoneTime = &values[i];
+                break;
+            case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+                displayRetireTime = &values[i];
+                break;
+            case EGL_READS_DONE_TIME_ANDROID:
+                releaseTime = &values[i];
+                break;
+            default:
+                setError(EGL_BAD_PARAMETER, EGL_FALSE);
+                return EGL_FALSE;
+        }
+    }
+
+    status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
+            postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
+            displayRetireTime, releaseTime);
+
+    if (ret != NO_ERROR) {
+        setError(EGL_BAD_ACCESS, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    return EGL_TRUE;
+}
+
+EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
+        EGLint timestamp)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    switch (timestamp) {
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
+        case EGL_QUEUE_TIME_ANDROID:
+        case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+        case EGL_COMPOSITION_START_TIME_ANDROID:
+        case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+        case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+        case EGL_READS_DONE_TIME_ANDROID:
+            return EGL_TRUE;
+#endif
+        default:
+            return EGL_FALSE;
+    }
+}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 10523f5..a32f037 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -66,7 +66,10 @@
 
 egl_display_t* egl_display_t::get(EGLDisplay dpy) {
     uintptr_t index = uintptr_t(dpy)-1U;
-    return (index >= NUM_DISPLAYS) ? NULL : &sDisplay[index];
+    if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) {
+        return nullptr;
+    }
+    return &sDisplay[index];
 }
 
 void egl_display_t::addObject(egl_object_t* object) {
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 7fc5609..6a76737 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
         EGLNativeWindowType win, EGLSurface surface,
         egl_connection_t const* cnx) :
     egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
-    connected(true)
+    enableTimestamps(false), connected(true)
 {}
 
 egl_surface_t::~egl_surface_t() {
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 8ceba1d..3150ba6 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,6 +139,7 @@
     EGLConfig config;
     sp<ANativeWindow> win;
     egl_connection_t const* cnx;
+    bool enableTimestamps;
 private:
     bool connected;
     void disconnect();
diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp
index f3739aa..6de5f27 100644
--- a/opengl/libs/EGL/egl_tls.cpp
+++ b/opengl/libs/EGL/egl_tls.cpp
@@ -14,19 +14,17 @@
  ** limitations under the License.
  */
 
-#include <stdlib.h>
 #include <pthread.h>
+#include <stdlib.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
-
+#include <log/log.h>
 #include <utils/CallStack.h>
 
 #include <EGL/egl.h>
 
 #include "egl_tls.h"
 
-
 namespace android {
 
 pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED;
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 336c264..450c402 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -15,10 +15,10 @@
  */
 
 #include <ctype.h>
-#include <stdlib.h>
 #include <errno.h>
+#include <stdlib.h>
 
-#include <cutils/log.h>
+#include <android/log.h>
 
 #include "egldefs.h"
 
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 6dd87c2..c206041 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -15,12 +15,11 @@
  */
 
 #include <ctype.h>
-#include <string.h>
 #include <errno.h>
-
+#include <string.h>
 #include <sys/ioctl.h>
 
-#include <cutils/log.h>
+#include <android/log.h>
 #include <cutils/properties.h>
 
 #include "../hooks.h"
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index 8bde4e5..e698fcd 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -1,31 +1,30 @@
-/* 
+/*
  ** Copyright 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 
+ ** 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 
+ **     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 
+ ** 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 <ctype.h>
-#include <string.h>
 #include <errno.h>
-
+#include <string.h>
 #include <sys/ioctl.h>
 
+#include <android/log.h>
+#include <cutils/properties.h>
+
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
 #include "../hooks.h"
 #include "../egl_impl.h"
 
diff --git a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
index a6fae80..51c6c61 100644
--- a/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
+++ b/opengl/specs/EGL_ANDROID_create_native_client_buffer.txt
@@ -124,12 +124,10 @@
         desirable) do not route the entire composition to the external sink.
 
         EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID: The buffer will be
-        used to create a renderbuffer. This flag must not be set if
-        EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID is set.
+        used to create a color-renderable texture.
 
         EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID: The buffer will be used to
-        create a texture. This flag must not be set if
-        EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID is set.
+        create a filterable texture.
 
     Errors
 
@@ -149,11 +147,6 @@
          with the value of EGL_FORMAT, the error EGL_BAD_PARAMETER is
          Generated.
 
-       * If both the EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID and
-         EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID are set in the value of
-         EGL_NATIVE_BUFFER_USAGE_ANDROID, the error EGL_BAD_PARAMETER is
-         Generated."
-
 Issues
 
     1. Should this extension define what combinations of formats and usage flags
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
new file mode 100644
index 0000000..30337ad
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -0,0 +1,145 @@
+Name
+
+    ANDROID_get_frame_timestamps
+
+Name Strings
+
+    EGL_ANDROID_get_frame_timestamps
+
+Contributors
+
+    Pablo Ceballos
+
+Contact
+
+    Pablo Ceballos, Google Inc. (pceballos 'at' google.com)
+
+Status
+
+    Draft
+
+Version
+
+    Version 1, May 31, 2016
+
+Number
+
+    EGL Extension #XXX
+
+Dependencies
+
+    Requires EGL 1.2
+
+    This extension is written against the wording of the EGL 1.5 Specification
+
+Overview
+
+    This extension allows querying various timestamps related to the composition
+    and display of window surfaces.
+
+    Some examples of how this might be used:
+        - The display retire time can be used to calculate end-to-end latency of
+          the entire graphics pipeline.
+        - The queue time and rendering complete time can be used to determine
+          how long the application's rendering took to complete. Likewise, the
+          composition start time and finish time can be used to determine how
+          long the compositor's rendering work took. In combination these can be
+          used to help determine if the system is GPU or CPU bound.
+
+New Types
+
+    /*
+     * EGLnsecsANDROID is a signed integer type for representing a time in
+     * nanoseconds.
+     */
+    #include <khrplatform.h>
+    typedef khronos_stime_nanoseconds_t EGLnsecsANDROID;
+
+New Procedures and Functions
+
+    EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
+            EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+            EGLnsecsANDROID *values);
+
+    EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
+            surface, EGLint timestamp);
+
+New Tokens
+
+    EGL_TIMESTAMPS_ANDROID 0x314D
+    EGL_QUEUE_TIME_ANDROID 0x314E
+    EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
+    EGL_COMPOSITION_START_TIME_ANDROID 0x3430
+    EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
+    EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
+    EGL_READS_DONE_TIME_ANDROID 0x3433
+
+Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
+"Surface Attributes", page 43:
+
+    If attribute is EGL_TIMESTAMPS_ANDROID, then values specifies whether to
+    enable/disable timestamp collection for this surface. A value of EGL_TRUE
+    enables timestamp collection, while a value of EGL_FALSE disables it. The
+    initial value is false. If surface is not a window surface this has no
+    effect.
+
+Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors)
+
+    Add a new subsection under Section 3,
+
+    "3.13 Composition and Display Timestamps
+
+    The function
+
+        EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface
+            surface, EGLint framesAgo, EGLint numTimestamps,
+            const EGLint *timestamps, EGLnsecsANDROID *values);
+
+    allows querying various timestamps related to the composition and display of
+    a window surface.
+
+    The framesAgo parameter indicates how many frames before the last posted
+    frame to query. So a value of zero would indicate that the query is for the
+    last posted frame. Note that the implementation maintains a limited history
+    of timestamp data. If a query is made for a frame whose timestamp history
+    no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection
+    has not been enabled for the surface then EGL_BAD_SURFACE is generated.
+    Timestamps for events that will not occur or have not yet occurred will be
+    zero. Timestamp queries that are not supported will generate an
+    EGL_BAD_PARAMETER error. If any error is generated the function will return
+    EGL_FALSE.
+
+    The eglGetFrameTimestampsANDROID function takes an array of timestamps to
+    query and returns timestamps in the corresponding indices of the values
+    array. The possible timestamps that can be queried are:
+        - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the
+          application.
+        - EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the
+          application's rendering to the surface was completed.
+        - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor
+          began preparing composition for this frame.
+        - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
+          compositor's rendering work for this frame finished. This will be zero
+          if composition was handled by the display and the compositor didn't do
+          any rendering.
+        - EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
+          replaced by the next frame on-screen.
+        - EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the
+          purpose of display/composition were completed for this frame.
+
+    Not all implementations may support all off the above timestamp queries. The
+    function
+
+        EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
+            surface, EGLint timestamp);
+
+    allows querying which timestamps are supported on the implementation."
+
+Issues
+
+    None
+
+Revision History
+
+#1 (Pablo Ceballos, May 31, 2016)
+    - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
index 8f1eaf3..f0c024e 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -19,4 +19,11 @@
 0x314A               EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
 0x314B               EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
 0x314C               EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
-0x314D - 0x314F      (unused)
+0x314D               EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x314E               EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x314F               EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3430               EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3431               EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3432               EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3433               EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3434 - 0x343F      (unused)
diff --git a/services/inputflinger/InputApplication.cpp b/services/inputflinger/InputApplication.cpp
index a99e637..9e90631 100644
--- a/services/inputflinger/InputApplication.cpp
+++ b/services/inputflinger/InputApplication.cpp
@@ -18,7 +18,7 @@
 
 #include "InputApplication.h"
 
-#include <cutils/log.h>
+#include <android/log.h>
 
 namespace android {
 
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 3f69d49..89475e9 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -45,16 +45,16 @@
 
 #include "InputDispatcher.h"
 
-#include <utils/Trace.h>
-#include <cutils/log.h>
-#include <powermanager/PowerManager.h>
-#include <ui/Region.h>
-
-#include <stddef.h>
-#include <unistd.h>
 #include <errno.h>
 #include <limits.h>
+#include <stddef.h>
 #include <time.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <utils/Trace.h>
+#include <powermanager/PowerManager.h>
+#include <ui/Region.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index dded47d..2ee222b 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -20,7 +20,7 @@
 
 #include "InputListener.h"
 
-#include <cutils/log.h>
+#include <android/log.h>
 
 namespace android {
 
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 6a6547b..519faa6 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -20,7 +20,7 @@
 
 #include "InputManager.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 namespace android {
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 170d91e..c1e6365 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -44,17 +44,18 @@
 
 #include "InputReader.h"
 
-#include <cutils/log.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
+#include <errno.h>
 #include <inttypes.h>
+#include <limits.h>
+#include <math.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <errno.h>
-#include <limits.h>
-#include <math.h>
+
+#include <log/log.h>
+
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -134,6 +135,14 @@
         { AKEYCODE_DPAD_RIGHT,  AKEYCODE_DPAD_UP,     AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN },
         { AKEYCODE_DPAD_UP,     AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT },
         { AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT,  AKEYCODE_DPAD_UP },
+        { AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+            AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT },
+        { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+            AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN },
+        { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+            AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT },
+        { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+            AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP },
 };
 static const size_t keyCodeRotationMapSize =
         sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp
index d7b514b..5e82d75 100644
--- a/services/inputflinger/InputWindow.cpp
+++ b/services/inputflinger/InputWindow.cpp
@@ -19,7 +19,7 @@
 
 #include "InputWindow.h"
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <ui/Rect.h>
 #include <ui/Region.h>
diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp
index 859c3b8..f1d3726 100644
--- a/services/inputflinger/host/InputFlinger.cpp
+++ b/services/inputflinger/host/InputFlinger.cpp
@@ -16,21 +16,19 @@
 
 #define LOG_TAG "InputFlinger"
 
-
 #include <stdint.h>
-#include <unistd.h>
-
 #include <sys/types.h>
-
-#include "InputFlinger.h"
-#include "InputDriver.h"
+#include <unistd.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/PermissionCache.h>
 #include <hardware/input.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <private/android_filesystem_config.h>
 
+#include "InputFlinger.h"
+#include "InputDriver.h"
+
 namespace android {
 
 const String16 sAccessInputFlingerPermission("android.permission.ACCESS_INPUT_FLINGER");
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 754b603..62e9ce0 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -31,7 +31,7 @@
 
 RecentEventLogger::RecentEventLogger(int sensorType) :
         mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)),
-        mRecentEvents(logSizeBySensorType(sensorType)) {
+        mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) {
     // blank
 }
 
@@ -60,18 +60,30 @@
                 (int) ns2ms(ev.mWallTime.tv_nsec));
 
         // data
-        if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
-            buffer.appendFormat("%" PRIu64 ", ", ev.mEvent.u64.step_counter);
-        } else {
-            for (size_t k = 0; k < mEventSize; ++k) {
-                buffer.appendFormat("%.2f, ", ev.mEvent.data[k]);
+        if (!mMaskData) {
+            if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
+                buffer.appendFormat("%" PRIu64 ", ", ev.mEvent.u64.step_counter);
+            } else {
+                for (size_t k = 0; k < mEventSize; ++k) {
+                    buffer.appendFormat("%.2f, ", ev.mEvent.data[k]);
+                }
             }
+        } else {
+            buffer.append("[value masked]");
         }
         buffer.append("\n");
     }
     return std::string(buffer.string());
 }
 
+void RecentEventLogger::setFormat(std::string format) {
+    if (format == "mask_data" ) {
+        mMaskData = true;
+    } else {
+        mMaskData = false;
+    }
+}
+
 bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const {
     std::lock_guard<std::mutex> lk(mLock);
 
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index 973a247..bf1f655 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -43,6 +43,7 @@
 
     // Dumpable interface
     virtual std::string dump() const override;
+    virtual void setFormat(std::string format) override;
 
 protected:
     struct SensorEventLog {
@@ -57,6 +58,8 @@
     mutable std::mutex mLock;
     RingBuffer<SensorEventLog> mRecentEvents;
 
+    bool mMaskData;
+
 private:
     static size_t logSizeBySensorType(int sensorType);
 };
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 4fbaa50..ac03742 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -475,6 +475,11 @@
     return idx;
 }
 
+void SensorDevice::notifyConnectionDestroyed(void* ident) {
+    Mutex::Autolock _l(mLock);
+    mDisabledClients.remove(ident);
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 68bb853..d340da3 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -51,6 +51,7 @@
     void enableAllSensors();
     void autoDisable(void *ident, int handle);
     status_t injectSensorData(const sensors_event_t *event);
+    void notifyConnectionDestroyed(void *ident);
 
     // Dumpable
     virtual std::string dump() const;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index c1e1bad..f2f1444 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -206,7 +206,7 @@
 status_t SensorService::SensorEventConnection::sendEvents(
         sensors_event_t const* buffer, size_t numEvents,
         sensors_event_t* scratch,
-        SensorEventConnection const * const * mapFlushEventsToConnections) {
+        wp<const SensorEventConnection> const * mapFlushEventsToConnections) {
     // filter out events not for this connection
     int count = 0;
     Mutex::Autolock _l(mConnectionLock);
@@ -234,7 +234,7 @@
             FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
             // Check if there is a pending flush_complete event for this sensor on this connection.
             if (buffer[i].type == SENSOR_TYPE_META_DATA && flushInfo.mFirstFlushPending == true &&
-                    this == mapFlushEventsToConnections[i]) {
+                    mapFlushEventsToConnections[i] == this) {
                 flushInfo.mFirstFlushPending = false;
                 ALOGD_IF(DEBUG_CONNECTIONS, "First flush event for sensor==%d ",
                         buffer[i].meta_data.sensor);
@@ -255,7 +255,7 @@
                 // from the same sensor_handle AND the current connection is mapped to the
                 // corresponding flush_complete_event.
                 if (buffer[i].type == SENSOR_TYPE_META_DATA) {
-                    if (this == mapFlushEventsToConnections[i]) {
+                    if (mapFlushEventsToConnections[i] == this) {
                         scratch[count++] = buffer[i];
                     }
                     ++i;
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index b796cc0..883c16e 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -52,7 +52,7 @@
                           bool isDataInjectionMode, const String16& opPackageName);
 
     status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch,
-                        SensorEventConnection const * const * mapFlushEventsToConnections = NULL);
+                        wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL);
     bool hasSensor(int32_t handle) const;
     bool hasAnySensor() const;
     bool hasOneShotSensors() const;
diff --git a/services/sensorservice/SensorRecord.cpp b/services/sensorservice/SensorRecord.cpp
index 644cfb0..53fb9de 100644
--- a/services/sensorservice/SensorRecord.cpp
+++ b/services/sensorservice/SensorRecord.cpp
@@ -21,13 +21,13 @@
 namespace android {
 
 SensorService::SensorRecord::SensorRecord(
-        const sp<SensorEventConnection>& connection)
+        const sp<const SensorEventConnection>& connection)
 {
     mConnections.add(connection);
 }
 
 bool SensorService::SensorRecord::addConnection(
-        const sp<SensorEventConnection>& connection)
+        const sp<const SensorEventConnection>& connection)
 {
     if (mConnections.indexOf(connection) < 0) {
         mConnections.add(connection);
@@ -37,16 +37,16 @@
 }
 
 bool SensorService::SensorRecord::removeConnection(
-        const wp<SensorEventConnection>& connection)
+        const wp<const SensorEventConnection>& connection)
 {
     ssize_t index = mConnections.indexOf(connection);
     if (index >= 0) {
         mConnections.removeItemsAt(index, 1);
     }
     // Remove this connections from the queue of flush() calls made on this sensor.
-    for (Vector< wp<SensorEventConnection> >::iterator it = mPendingFlushConnections.begin();
+    for (Vector< wp<const SensorEventConnection> >::iterator it = mPendingFlushConnections.begin();
             it != mPendingFlushConnections.end(); ) {
-        if (it->unsafe_get() == connection.unsafe_get()) {
+        if (*it == connection) {
             it = mPendingFlushConnections.erase(it);
         } else {
             ++it;
@@ -56,7 +56,7 @@
 }
 
 void SensorService::SensorRecord::addPendingFlushConnection(
-        const sp<SensorEventConnection>& connection) {
+        const sp<const SensorEventConnection>& connection) {
     mPendingFlushConnections.add(connection);
 }
 
@@ -66,10 +66,10 @@
     }
 }
 
-SensorService::SensorEventConnection *
+wp<const SensorService::SensorEventConnection>
         SensorService::SensorRecord::getFirstPendingFlushConnection() {
     if (mPendingFlushConnections.size() > 0) {
-        return mPendingFlushConnections[0].unsafe_get();
+        return mPendingFlushConnections[0];
     }
     return NULL;
 }
diff --git a/services/sensorservice/SensorRecord.h b/services/sensorservice/SensorRecord.h
index 29b970d..5a35410 100644
--- a/services/sensorservice/SensorRecord.h
+++ b/services/sensorservice/SensorRecord.h
@@ -25,20 +25,20 @@
 
 class SensorService::SensorRecord {
 public:
-    SensorRecord(const sp<SensorEventConnection>& connection);
-    bool addConnection(const sp<SensorEventConnection>& connection);
-    bool removeConnection(const wp<SensorEventConnection>& connection);
+    SensorRecord(const sp<const SensorEventConnection>& connection);
+    bool addConnection(const sp<const SensorEventConnection>& connection);
+    bool removeConnection(const wp<const SensorEventConnection>& connection);
     size_t getNumConnections() const { return mConnections.size(); }
 
-    void addPendingFlushConnection(const sp<SensorEventConnection>& connection);
+    void addPendingFlushConnection(const sp<const SensorEventConnection>& connection);
     void removeFirstPendingFlushConnection();
-    SensorEventConnection * getFirstPendingFlushConnection();
+    wp<const SensorEventConnection> getFirstPendingFlushConnection();
     void clearAllPendingFlushConnections();
 private:
-    SortedVector< wp<SensorEventConnection> > mConnections;
+    SortedVector< wp<const SensorEventConnection> > mConnections;
     // A queue of all flush() calls made on this sensor. Flush complete events
     // will be sent in this order.
-    Vector< wp<SensorEventConnection> > mPendingFlushConnections;
+    Vector< wp<const SensorEventConnection> > mPendingFlushConnections;
 };
 
 }
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 319f4d4..7b47709 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -47,6 +47,7 @@
 
 #include <inttypes.h>
 #include <math.h>
+#include <sched.h>
 #include <stdint.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -71,6 +72,7 @@
 
 #define SENSOR_SERVICE_DIR "/data/system/sensor_service"
 #define SENSOR_SERVICE_HMAC_KEY_FILE  SENSOR_SERVICE_DIR "/hmac_key"
+#define SENSOR_SERVICE_SCHED_FIFO_PRIORITY 10
 
 // Permissions.
 static const String16 sDump("android.permission.DUMP");
@@ -117,6 +119,15 @@
     return true;
 }
 
+// Set main thread to SCHED_FIFO to lower sensor event latency when system is under load
+void SensorService::enableSchedFifoMode() {
+    struct sched_param param = {0};
+    param.sched_priority = SENSOR_SERVICE_SCHED_FIFO_PRIORITY;
+    if (sched_setscheduler(getTid(), SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO for SensorService thread");
+    }
+}
+
 void SensorService::onFirstRef() {
     ALOGD("nuSensorService starting...");
     SensorDevice& dev(SensorDevice::getInstance());
@@ -249,7 +260,7 @@
             const size_t minBufferSize = SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT;
             mSensorEventBuffer = new sensors_event_t[minBufferSize];
             mSensorEventScratch = new sensors_event_t[minBufferSize];
-            mMapFlushEventsToConnections = new SensorEventConnection const * [minBufferSize];
+            mMapFlushEventsToConnections = new wp<const SensorEventConnection> [minBufferSize];
             mCurrentOperatingMode = NORMAL;
 
             mNextSensorRegIndex = 0;
@@ -261,6 +272,9 @@
             mAckReceiver = new SensorEventAckReceiver(this);
             mAckReceiver->run("SensorEventAckReceiver", PRIORITY_URGENT_DISPLAY);
             run("SensorService", PRIORITY_URGENT_DISPLAY);
+
+            // priority can only be changed after run
+            enableSchedFifoMode();
         }
     }
 }
@@ -308,6 +322,7 @@
                 IPCThreadState::self()->getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
     } else {
+        bool privileged = IPCThreadState::self()->getCallingUid() == 0;
         if (args.size() > 2) {
            return INVALID_OPERATION;
         }
@@ -379,8 +394,12 @@
             result.append("Recent Sensor events:\n");
             for (auto&& i : mRecentEvent) {
                 sp<SensorInterface> s = mSensors.getInterface(i.first);
-                if (!i.second->isEmpty() &&
-                    s->getSensor().getRequiredPermission().isEmpty()) {
+                if (!i.second->isEmpty()) {
+                    if (privileged || s->getSensor().getRequiredPermission().isEmpty()) {
+                        i.second->setFormat("normal");
+                    } else {
+                        i.second->setFormat("mask_data");
+                    }
                     // if there is events and sensor does not need special permission.
                     result.appendFormat("%s: ", s->getSensor().getName().string());
                     result.append(i.second->dump().c_str());
@@ -435,15 +454,15 @@
                     continue;
                 }
                 if (reg_info.mActivated) {
-                   result.appendFormat("%02d:%02d:%02d activated package=%s handle=0x%08x "
-                           "samplingRate=%dus maxReportLatency=%dus\n",
-                           reg_info.mHour, reg_info.mMin, reg_info.mSec,
-                           reg_info.mPackageName.string(), reg_info.mSensorHandle,
-                           reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs);
+                   result.appendFormat("%02d:%02d:%02d activated handle=0x%08x "
+                           "samplingRate=%dus maxReportLatency=%dus package=%s\n",
+                           reg_info.mHour, reg_info.mMin, reg_info.mSec, reg_info.mSensorHandle,
+                           reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs,
+                           reg_info.mPackageName.string());
                 } else {
-                   result.appendFormat("%02d:%02d:%02d de-activated package=%s handle=0x%08x\n",
+                   result.appendFormat("%02d:%02d:%02d de-activated handle=0x%08x package=%s\n",
                            reg_info.mHour, reg_info.mMin, reg_info.mSec,
-                           reg_info.mPackageName.string(), reg_info.mSensorHandle);
+                           reg_info.mSensorHandle, reg_info.mPackageName.string());
                 }
                 currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
                         SENSOR_REGISTRATIONS_BUF_SIZE;
@@ -978,6 +997,9 @@
     if (c->needsWakeLock()) {
         checkWakeLockStateLocked();
     }
+
+    SensorDevice& dev(SensorDevice::getInstance());
+    dev.notifyConnectionDestroyed(c);
 }
 
 sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index dc49b06..e969d8a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -53,7 +53,7 @@
 // For older HALs which don't support batching, use a smaller socket buffer size.
 #define SOCKET_BUFFER_SIZE_NON_BATCHED (4 * 1024)
 
-#define SENSOR_REGISTRATIONS_BUF_SIZE 20
+#define SENSOR_REGISTRATIONS_BUF_SIZE 200
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -215,6 +215,8 @@
     // Either read from storage or create a new one.
     static bool initializeHmacKey();
 
+    // Enable SCHED_FIFO priority for thread
+    void enableSchedFifoMode();
 
     static uint8_t sHmacGlobalKey[128];
     static bool sHmacGlobalKeyIsValid;
@@ -235,7 +237,7 @@
     SortedVector< wp<SensorEventConnection> > mActiveConnections;
     bool mWakeLockAcquired;
     sensors_event_t *mSensorEventBuffer, *mSensorEventScratch;
-    SensorEventConnection const **mMapFlushEventsToConnections;
+    wp<const SensorEventConnection> * mMapFlushEventsToConnections;
     std::unordered_map<int, RecentEventLogger*> mRecentEvent;
     Mode mCurrentOperatingMode;
 
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 90c12ac..170faa8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -44,7 +44,6 @@
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-#LOCAL_CFLAGS += -DENABLE_FENCE_TRACKING
 
 ifeq ($(TARGET_USES_HWC2),true)
     LOCAL_CFLAGS += -DUSE_HWC2
@@ -80,18 +79,36 @@
     LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK
 endif
 
-# See build/target/board/generic/BoardConfig.mk for a description of this setting.
+# The following two BoardConfig variables define (respectively):
+#
+#   - The phase offset between hardware vsync and when apps are woken up by the
+#     Choreographer callback
+#   - The phase offset between hardware vsync and when SurfaceFlinger wakes up
+#     to consume input
+#
+# Their values can be tuned to trade off between display pipeline latency (both
+# overall latency and the lengths of the app --> SF and SF --> display phases)
+# and frame delivery jitter (which typically manifests as "jank" or "jerkiness"
+# while interacting with the device). The default values should produce a
+# relatively low amount of jitter at the expense of roughly two frames of
+# app --> display latency, and unless significant testing is performed to avoid
+# increased display jitter (both manual investigation using systrace [1] and
+# automated testing using dumpsys gfxinfo [2] are recommended), they should not
+# be modified.
+#
+# [1] https://developer.android.com/studio/profile/systrace.html
+# [2] https://developer.android.com/training/testing/performance.html
+
 ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
     LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
 else
-    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=0
+    LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=1000000
 endif
 
-# See build/target/board/generic/BoardConfig.mk for a description of this setting.
 ifneq ($(SF_VSYNC_EVENT_PHASE_OFFSET_NS),)
     LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=$(SF_VSYNC_EVENT_PHASE_OFFSET_NS)
 else
-    LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=0
+    LOCAL_CFLAGS += -DSF_VSYNC_EVENT_PHASE_OFFSET_NS=1000000
 endif
 
 ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),)
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 3e48cd2..e14a59b 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -170,5 +170,15 @@
     return NO_ERROR;
 }
 
+status_t Client::getTransformToDisplayInverse(const sp<IBinder>& handle,
+        bool* outTransformToDisplayInverse) const {
+    sp<Layer> layer = getLayerUser(handle);
+    if (layer == NULL) {
+        return NAME_NOT_FOUND;
+    }
+    *outTransformToDisplayInverse = layer->getTransformToDisplayInverse();
+    return NO_ERROR;
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index c77651f..9c7d050 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -63,6 +63,8 @@
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
+    virtual status_t getTransformToDisplayInverse(
+            const sp<IBinder>& handle, bool* outTransformToDisplayInverse) const;
 
     virtual status_t onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
diff --git a/services/surfaceflinger/DdmConnection.cpp b/services/surfaceflinger/DdmConnection.cpp
index 659c2c8..35d55f5 100644
--- a/services/surfaceflinger/DdmConnection.cpp
+++ b/services/surfaceflinger/DdmConnection.cpp
@@ -15,8 +15,10 @@
  */
 
 #include <dlfcn.h>
+#include <sys/types.h>
+#include <unistd.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "jni.h"
 #include "DdmConnection.h"
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 37b6420..52c2b84 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -22,20 +22,19 @@
 
 #include <math.h>
 
-#include <cutils/log.h>
+#include <algorithm>
 
-#include <ui/Fence.h>
-
+#include <log/log.h>
 #include <utils/String8.h>
 #include <utils/Thread.h>
 #include <utils/Trace.h>
 #include <utils/Vector.h>
 
+#include <ui/Fence.h>
+
 #include "DispSync.h"
 #include "EventLog/EventLog.h"
 
-#include <algorithm>
-
 using std::max;
 using std::min;
 
@@ -383,6 +382,13 @@
         mThread(new DispSyncThread(name)) {
 
     mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+    // set DispSync to SCHED_FIFO to minimize jitter
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
+    }
+
 
     reset();
     beginResync();
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a67b3ff..5c2c0ad 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -415,6 +415,17 @@
 }
 
 // ----------------------------------------------------------------------------
+#ifdef USE_HWC2
+void DisplayDevice::setActiveColorMode(android_color_mode_t mode) {
+    mActiveColorMode = mode;
+}
+
+android_color_mode_t DisplayDevice::getActiveColorMode() const {
+    return mActiveColorMode;
+}
+#endif
+
+// ----------------------------------------------------------------------------
 
 void DisplayDevice::setLayerStack(uint32_t stack) {
     mLayerStack = stack;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index dd9b104..105e980 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -182,6 +182,11 @@
     void setPowerMode(int mode);
     bool isDisplayOn() const;
 
+#ifdef USE_HWC2
+    android_color_mode_t getActiveColorMode() const;
+    void setActiveColorMode(android_color_mode_t mode);
+#endif
+
     /* ------------------------------------------------------------------------
      * Display active config management.
      */
@@ -252,6 +257,10 @@
     int mPowerMode;
     // Current active config
     int mActiveConfig;
+#ifdef USE_HWC2
+    // current active color mode
+    android_color_mode_t mActiveColorMode;
+#endif
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 18c7945..cdfe34a 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -19,14 +19,13 @@
 #undef LOG_TAG
 #define LOG_TAG "FramebufferSurface"
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
 #include <errno.h>
-
-#include <cutils/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <utils/String8.h>
+#include <log/log.h>
 
 #include <ui/Rect.h>
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ae6ba98..4fe3cfd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -317,9 +317,12 @@
             "Capability size has changed");
     uint32_t numCapabilities = 0;
     mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
-    mCapabilities.resize(numCapabilities);
-    auto asInt = reinterpret_cast<int32_t*>(mCapabilities.data());
+    std::vector<Capability> capabilities(numCapabilities);
+    auto asInt = reinterpret_cast<int32_t*>(capabilities.data());
     mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
+    for (auto capability : capabilities) {
+        mCapabilities.emplace(capability);
+    }
 }
 
 bool Device::hasCapability(HWC2::Capability capability) const
@@ -578,7 +581,7 @@
     return Error::None;
 }
 
-Error Display::getColorModes(std::vector<int32_t>* outModes) const
+Error Display::getColorModes(std::vector<android_color_mode_t>* outModes) const
 {
     uint32_t numModes = 0;
     int32_t intError = mDevice.mGetColorModes(mDevice.mHwcDevice, mId,
@@ -596,7 +599,10 @@
         return error;
     }
 
-    std::swap(*outModes, modes);
+    outModes->resize(numModes);
+    for (size_t i = 0; i < numModes; i++) {
+        (*outModes)[i] = static_cast<android_color_mode_t>(modes[i]);
+    }
     return Error::None;
 }
 
@@ -802,7 +808,7 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::setColorMode(int32_t mode)
+Error Display::setColorMode(android_color_mode_t mode)
 {
     int32_t intError = mDevice.mSetColorMode(mDevice.mHwcDevice, mId, mode);
     return static_cast<Error>(intError);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 25a7d89..32a9de0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -33,6 +33,7 @@
 #include <functional>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 namespace android {
@@ -66,7 +67,7 @@
 
     std::string dump() const;
 
-    const std::vector<Capability>& getCapabilities() const {
+    const std::unordered_set<Capability>& getCapabilities() const {
         return mCapabilities;
     };
 
@@ -181,7 +182,7 @@
     HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
     HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
 
-    std::vector<Capability> mCapabilities;
+    std::unordered_set<Capability> mCapabilities;
     std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
 
     HotplugCallback mHotplug;
@@ -281,7 +282,7 @@
     [[clang::warn_unused_result]] Error getChangedCompositionTypes(
             std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes);
     [[clang::warn_unused_result]] Error getColorModes(
-            std::vector<int32_t>* outModes) const;
+            std::vector<android_color_mode_t>* outModes) const;
 
     // Doesn't call into the HWC2 device, so no errors are possible
     std::vector<std::shared_ptr<const Config>> getConfigs() const;
@@ -306,7 +307,7 @@
             buffer_handle_t target,
             const android::sp<android::Fence>& acquireFence,
             android_dataspace_t dataspace);
-    [[clang::warn_unused_result]] Error setColorMode(int32_t mode);
+    [[clang::warn_unused_result]] Error setColorMode(android_color_mode_t mode);
     [[clang::warn_unused_result]] Error setColorTransform(
             const android::mat4& matrix, android_color_transform_t hint);
     [[clang::warn_unused_result]] Error setOutputBuffer(
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 2641ee6..617ea9f 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -22,15 +22,16 @@
 
 #include "HWC2On1Adapter.h"
 
+#include <inttypes.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <sstream>
+
 #include <hardware/hwcomposer.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
-#include <cstdlib>
-#include <chrono>
-#include <inttypes.h>
-#include <sstream>
-
 using namespace std::chrono_literals;
 
 static bool operator==(const hwc_color_t& lhs, const hwc_color_t& rhs) {
@@ -75,7 +76,7 @@
 
 using namespace HWC2;
 
-static constexpr Attribute ColorTransform = static_cast<Attribute>(6);
+static constexpr Attribute ColorMode = static_cast<Attribute>(6);
 
 namespace android {
 
@@ -268,9 +269,7 @@
                     &Display::setClientTarget, buffer_handle_t, int32_t,
                     int32_t, hwc_region_t>);
         case FunctionDescriptor::SetColorMode:
-            return asFP<HWC2_PFN_SET_COLOR_MODE>(
-                    displayHook<decltype(&Display::setColorMode),
-                    &Display::setColorMode, int32_t>);
+            return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
         case FunctionDescriptor::SetColorTransform:
             return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
         case FunctionDescriptor::SetOutputBuffer:
@@ -894,7 +893,7 @@
     return Error::None;
 }
 
-Error HWC2On1Adapter::Display::setColorMode(int32_t mode)
+Error HWC2On1Adapter::Display::setColorMode(android_color_mode_t mode)
 {
     std::unique_lock<std::recursive_mutex> lock (mStateMutex);
 
@@ -1198,11 +1197,14 @@
         newConfig->setAttribute(Attribute::DpiY,
                 values[attributeMap[HWC_DISPLAY_DPI_Y]]);
         if (hasColor) {
-            newConfig->setAttribute(ColorTransform,
+            // In HWC1, color modes are referred to as color transforms. To avoid confusion with
+            // the HWC2 concept of color transforms, we internally refer to them as color modes for
+            // both HWC1 and 2.
+            newConfig->setAttribute(ColorMode,
                     values[attributeMap[HWC_DISPLAY_COLOR_TRANSFORM]]);
         }
 
-        // We can only do this after attempting to read the color transform
+        // We can only do this after attempting to read the color mode
         newConfig->setHwc1Id(hwc1ConfigId);
 
         for (auto& existingConfig : mConfigs) {
@@ -1678,8 +1680,8 @@
 
 void HWC2On1Adapter::Display::Config::setHwc1Id(uint32_t id)
 {
-    int32_t colorTransform = getAttribute(ColorTransform);
-    mHwc1Ids.emplace(colorTransform, id);
+    android_color_mode_t colorMode = static_cast<android_color_mode_t>(getAttribute(ColorMode));
+    mHwc1Ids.emplace(colorMode, id);
 }
 
 bool HWC2On1Adapter::Display::Config::hasHwc1Id(uint32_t id) const
@@ -1692,18 +1694,20 @@
     return false;
 }
 
-int32_t HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
-        uint32_t id) const
+Error HWC2On1Adapter::Display::Config::getColorModeForHwc1Id(
+        uint32_t id, android_color_mode_t* outMode) const
 {
     for (const auto& idPair : mHwc1Ids) {
         if (id == idPair.second) {
-            return idPair.first;
+            *outMode = idPair.first;
+            return Error::None;
         }
     }
-    return -1;
+    ALOGE("Unable to find color mode for HWC ID %" PRIu32 " on config %u", id, mId);
+    return Error::BadParameter;
 }
 
-Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(int32_t mode,
+Error HWC2On1Adapter::Display::Config::getHwc1IdForColorMode(android_color_mode_t mode,
         uint32_t* outId) const
 {
     for (const auto& idPair : mHwc1Ids) {
@@ -1726,25 +1730,26 @@
             return false;
         }
     }
-    int32_t otherColorTransform = other.getAttribute(ColorTransform);
-    if (mHwc1Ids.count(otherColorTransform) != 0) {
+    android_color_mode_t otherColorMode =
+            static_cast<android_color_mode_t>(other.getAttribute(ColorMode));
+    if (mHwc1Ids.count(otherColorMode) != 0) {
         ALOGE("Attempted to merge two configs (%u and %u) which appear to be "
-                "identical", mHwc1Ids.at(otherColorTransform),
-                other.mHwc1Ids.at(otherColorTransform));
+                "identical", mHwc1Ids.at(otherColorMode),
+                other.mHwc1Ids.at(otherColorMode));
         return false;
     }
-    mHwc1Ids.emplace(otherColorTransform,
-            other.mHwc1Ids.at(otherColorTransform));
+    mHwc1Ids.emplace(otherColorMode,
+            other.mHwc1Ids.at(otherColorMode));
     return true;
 }
 
-std::set<int32_t> HWC2On1Adapter::Display::Config::getColorTransforms() const
+std::set<android_color_mode_t> HWC2On1Adapter::Display::Config::getColorModes() const
 {
-    std::set<int32_t> colorTransforms;
+    std::set<android_color_mode_t> colorModes;
     for (const auto& idPair : mHwc1Ids) {
-        colorTransforms.emplace(idPair.first);
+        colorModes.emplace(idPair.first);
     }
-    return colorTransforms;
+    return colorModes;
 }
 
 std::string HWC2On1Adapter::Display::Config::toString(bool splitLine) const
@@ -1787,15 +1792,15 @@
 
 
     for (const auto& id : mHwc1Ids) {
-        int32_t colorTransform = id.first;
+        android_color_mode_t colorMode = id.first;
         uint32_t hwc1Id = id.second;
         std::memset(buffer, 0, BUFFER_SIZE);
-        if (colorTransform == mDisplay.mActiveColorMode) {
+        if (colorMode == mDisplay.mActiveColorMode) {
             writtenBytes = snprintf(buffer, BUFFER_SIZE, " [%u/%d]", hwc1Id,
-                    colorTransform);
+                    colorMode);
         } else {
             writtenBytes = snprintf(buffer, BUFFER_SIZE, " %u/%d", hwc1Id,
-                    colorTransform);
+                    colorMode);
         }
         output.append(buffer, writtenBytes);
     }
@@ -1814,10 +1819,10 @@
 
 void HWC2On1Adapter::Display::populateColorModes()
 {
-    mColorModes = mConfigs[0]->getColorTransforms();
+    mColorModes = mConfigs[0]->getColorModes();
     for (const auto& config : mConfigs) {
-        std::set<int32_t> intersection;
-        auto configModes = config->getColorTransforms();
+        std::set<android_color_mode_t> intersection;
+        auto configModes = config->getColorModes();
         std::set_intersection(mColorModes.cbegin(), mColorModes.cend(),
                 configModes.cbegin(), configModes.cend(),
                 std::inserter(intersection, intersection.begin()));
@@ -1830,7 +1835,7 @@
     if (mDevice.mHwc1Device->getActiveConfig == nullptr) {
         ALOGV("getActiveConfig is null, choosing config 0");
         mActiveConfig = mConfigs[0];
-        mActiveColorMode = -1;
+        mActiveColorMode = HAL_COLOR_MODE_NATIVE;
         return;
     }
 
@@ -1842,7 +1847,13 @@
                 ALOGV("Setting active config to %d for HWC1 config %u",
                         config->getId(), activeConfig);
                 mActiveConfig = config;
-                mActiveColorMode = config->getColorModeForHwc1Id(activeConfig);
+                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;
             }
         }
@@ -1850,7 +1861,7 @@
             ALOGV("Unable to find active HWC1 config %u, defaulting to "
                     "config 0", activeConfig);
             mActiveConfig = mConfigs[0];
-            mActiveColorMode = -1;
+            mActiveColorMode = HAL_COLOR_MODE_NATIVE;
         }
     }
 }
@@ -2295,7 +2306,14 @@
                 hwc1Layer.compositionType = HWC_FRAMEBUFFER;
                 break;
             case Composition::SolidColor:
-                hwc1Layer.compositionType = HWC_BACKGROUND;
+                // In theory the following line should work, but since the HWC1
+                // version of SurfaceFlinger never used HWC_BACKGROUND, HWC1
+                // devices may not work correctly. To be on the safe side, we
+                // fall back to client composition.
+                //
+                // hwc1Layer.compositionType = HWC_BACKGROUND;
+                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                hwc1Layer.flags |= HWC_SKIP_LAYER;
                 break;
             case Composition::Cursor:
                 hwc1Layer.compositionType = HWC_FRAMEBUFFER;
@@ -2333,7 +2351,7 @@
         int supportedTypes = 0;
         auto result = mHwc1Device->query(mHwc1Device,
                 HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
-        if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL) != 0)) {
+        if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) {
             ALOGI("Found support for HWC virtual displays");
             mHwc1SupportsVirtualDisplays = true;
         }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
index 5debb9a..962361e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -213,7 +213,7 @@
             HWC2::Error setClientTarget(buffer_handle_t target,
                     int32_t acquireFence, int32_t dataspace,
                     hwc_region_t damage);
-            HWC2::Error setColorMode(int32_t mode);
+            HWC2::Error setColorMode(android_color_mode_t mode);
             HWC2::Error setColorTransform(android_color_transform_t hint);
             HWC2::Error setOutputBuffer(buffer_handle_t buffer,
                     int32_t releaseFence);
@@ -258,8 +258,9 @@
 
                     void setHwc1Id(uint32_t id);
                     bool hasHwc1Id(uint32_t id) const;
-                    int32_t getColorModeForHwc1Id(uint32_t id) const;
-                    HWC2::Error getHwc1IdForColorMode(int32_t mode,
+                    HWC2::Error getColorModeForHwc1Id(uint32_t id,
+                            android_color_mode_t *outMode) const;
+                    HWC2::Error getHwc1IdForColorMode(android_color_mode_t mode,
                             uint32_t* outId) const;
 
                     void setId(hwc2_config_t id) { mId = id; }
@@ -269,7 +270,7 @@
                     // mode. Returns whether the merge was successful
                     bool merge(const Config& other);
 
-                    std::set<int32_t> getColorTransforms() const;
+                    std::set<android_color_mode_t> getColorModes() const;
 
                     // splitLine divides the output into two lines suitable for
                     // dumpsys SurfaceFlinger
@@ -281,7 +282,7 @@
                     std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
 
                     // Maps from color transform to HWC1 config ID
-                    std::unordered_map<int32_t, uint32_t> mHwc1Ids;
+                    std::unordered_map<android_color_mode_t, uint32_t> mHwc1Ids;
             };
 
             class Changes {
@@ -378,8 +379,8 @@
 
             std::vector<std::shared_ptr<Config>> mConfigs;
             std::shared_ptr<const Config> mActiveConfig;
-            std::set<int32_t> mColorModes;
-            int32_t mActiveColorMode;
+            std::set<android_color_mode_t> mColorModes;
+            android_color_mode_t mActiveColorMode;
             std::string mName;
             HWC2::DisplayType mType;
             HWC2::PowerMode mPowerMode;
@@ -432,6 +433,12 @@
                 hint);
     }
 
+    static int32_t setColorModeHook(hwc2_device_t* device,
+            hwc2_display_t display, int32_t /*android_color_mode_t*/ intMode) {
+        auto mode = static_cast<android_color_mode_t>(intMode);
+        return callDisplayFunction(device, display, &Display::setColorMode, mode);
+    }
+
     static int32_t setPowerModeHook(hwc2_device_t* device,
             hwc2_display_t display, int32_t intMode) {
         auto mode = static_cast<HWC2::PowerMode>(intMode);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 216bbc9..f0ded39 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -43,8 +43,8 @@
 
 #include <android/configuration.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include "HWComposer.h"
 #include "HWC2On1Adapter.h"
@@ -144,6 +144,11 @@
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
 
+bool HWComposer::hasCapability(HWC2::Capability capability) const
+{
+    return mHwcDevice->getCapabilities().count(capability) > 0;
+}
+
 bool HWComposer::isValidDisplay(int32_t displayId) const {
     return static_cast<size_t>(displayId) < mDisplayData.size() &&
             mDisplayData[displayId].hwcDisplay;
@@ -359,6 +364,46 @@
     return config;
 }
 
+std::vector<android_color_mode_t> HWComposer::getColorModes(int32_t displayId) const {
+    std::vector<android_color_mode_t> modes;
+
+    if (!isValidDisplay(displayId)) {
+        ALOGE("getColorModes: Attempted to access invalid display %d",
+                displayId);
+        return modes;
+    }
+    const std::shared_ptr<HWC2::Display>& hwcDisplay =
+            mDisplayData[displayId].hwcDisplay;
+
+    auto error = hwcDisplay->getColorModes(&modes);
+    if (error != HWC2::Error::None) {
+        ALOGE("getColorModes failed for display %d: %s (%d)", displayId,
+                to_string(error).c_str(), static_cast<int32_t>(error));
+        return std::vector<android_color_mode_t>();
+    }
+
+    return modes;
+}
+
+status_t HWComposer::setActiveColorMode(int32_t displayId, android_color_mode_t mode) {
+    if (!isValidDisplay(displayId)) {
+        ALOGE("setActiveColorMode: Display %d is not valid", displayId);
+        return BAD_INDEX;
+    }
+
+    auto& displayData = mDisplayData[displayId];
+    auto error = displayData.hwcDisplay->setColorMode(mode);
+    if (error != HWC2::Error::None) {
+        ALOGE("setActiveConfig: Failed to set color mode %d on display %d: "
+                "%s (%d)", mode, displayId, to_string(error).c_str(),
+                static_cast<int32_t>(error));
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
+
 void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) {
     if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) {
         ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp);
@@ -685,6 +730,28 @@
     return NO_ERROR;
 }
 
+status_t HWComposer::setColorTransform(int32_t displayId,
+        const mat4& transform) {
+    if (!isValidDisplay(displayId)) {
+        ALOGE("setColorTransform: Display %d is not valid", displayId);
+        return BAD_INDEX;
+    }
+
+    auto& displayData = mDisplayData[displayId];
+    bool isIdentity = transform == mat4();
+    auto error = displayData.hwcDisplay->setColorTransform(transform,
+            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY :
+            HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX);
+    if (error != HWC2::Error::None) {
+        ALOGE("setColorTransform: Failed to set transform on display %d: "
+                "%s (%d)", displayId, to_string(error).c_str(),
+                static_cast<int32_t>(error));
+        return UNKNOWN_ERROR;
+    }
+
+    return NO_ERROR;
+}
+
 void HWComposer::disconnectDisplay(int displayId) {
     LOG_ALWAYS_FATAL_IF(displayId < 0);
     auto& displayData = mDisplayData[displayId];
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b88e250..41671f6 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -81,6 +81,8 @@
 
     void setEventHandler(EventHandler* handler);
 
+    bool hasCapability(HWC2::Capability capability) const;
+
     // Attempts to allocate a virtual display. If the virtual display is created
     // on the HWC device, outId will contain its HWC ID.
     status_t allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -104,6 +106,9 @@
     // set active config
     status_t setActiveConfig(int32_t displayId, size_t configId);
 
+    // Sets a color transform to be applied to the result of composition
+    status_t setColorTransform(int32_t displayId, const mat4& transform);
+
     // reset state when an external, non-virtual display is disconnected
     void disconnectDisplay(int32_t displayId);
 
@@ -137,15 +142,6 @@
 
     void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled);
 
-    struct DisplayConfig {
-        uint32_t width;
-        uint32_t height;
-        float xdpi;
-        float ydpi;
-        nsecs_t refresh;
-        int colorTransform;
-    };
-
     // Query display parameters.  Pass in a display index (e.g.
     // HWC_DISPLAY_PRIMARY).
     nsecs_t getRefreshTimestamp(int32_t disp) const;
@@ -158,6 +154,10 @@
     std::shared_ptr<const HWC2::Display::Config>
             getActiveConfig(int32_t displayId) const;
 
+    std::vector<android_color_mode_t> getColorModes(int32_t displayId) const;
+
+    status_t setActiveColorMode(int32_t displayId, android_color_mode_t mode);
+
     // for debugging ----------------------------------------------------------
     void dump(String8& out) const;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
index 4afd8a2..52cbb34 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp
@@ -39,8 +39,10 @@
 
 #include <android/configuration.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
+
+#include <system/graphics.h>
 
 #include "HWComposer.h"
 
@@ -403,7 +405,7 @@
                     config.ydpi = values[i] / 1000.0f;
                     break;
                 case HWC_DISPLAY_COLOR_TRANSFORM:
-                    config.colorTransform = values[i];
+                    config.colorMode = static_cast<android_color_mode_t>(values[i]);
                     break;
                 default:
                     ALOG_ASSERT(false, "unknown display attribute[%zu] %#x",
@@ -519,6 +521,11 @@
     return mDisplayData[disp].configs[currentConfig].refresh;
 }
 
+android_color_mode_t HWComposer::getColorMode(int disp) const {
+    size_t currentConfig = mDisplayData[disp].currentConfig;
+    return mDisplayData[disp].configs[currentConfig].colorMode;
+}
+
 const Vector<HWComposer::DisplayConfig>& HWComposer::getConfigs(int disp) const {
     return mDisplayData[disp].configs;
 }
@@ -1182,10 +1189,10 @@
             for (size_t c = 0; c < disp.configs.size(); ++c) {
                 const DisplayConfig& config(disp.configs[c]);
                 result.appendFormat("    %s%zd: %ux%u, xdpi=%f, ydpi=%f"
-                        ", refresh=%" PRId64 ", colorTransform=%d\n",
+                        ", refresh=%" PRId64 ", colorMode=%d\n",
                         c == disp.currentConfig ? "* " : "", c,
                         config.width, config.height, config.xdpi, config.ydpi,
-                        config.refresh, config.colorTransform);
+                        config.refresh, config.colorMode);
             }
 
             if (disp.list) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
index c861817..170e382 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h
@@ -22,6 +22,8 @@
 
 #include <hardware/hwcomposer_defs.h>
 
+#include <system/graphics.h>
+
 #include <ui/Fence.h>
 
 #include <utils/BitSet.h>
@@ -257,7 +259,15 @@
         float xdpi;
         float ydpi;
         nsecs_t refresh;
-        int colorTransform;
+        android_color_mode_t colorMode;
+        bool operator==(const DisplayConfig& rhs) const {
+            return width == rhs.width &&
+                    height == rhs.height &&
+                    xdpi == rhs.xdpi &&
+                    ydpi == rhs.ydpi &&
+                    refresh == rhs.refresh &&
+                    colorMode == rhs.colorMode;
+        }
     };
 
     // Query display parameters.  Pass in a display index (e.g.
@@ -274,6 +284,7 @@
     float getDpiX(int disp) const;
     float getDpiY(int disp) const;
     nsecs_t getRefreshPeriod(int disp) const;
+    android_color_mode_t getColorMode(int disp) const;
 
     const Vector<DisplayConfig>& getConfigs(int disp) const;
     size_t getCurrentConfig(int disp) const;
diff --git a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
index bd50b4a..a4a1f99 100644
--- a/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerHAL.cpp
@@ -17,7 +17,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <cutils/log.h>
+#include <android/log.h>
 #include <utils/Errors.h>
 
 #include <binder/IServiceManager.h>
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index bc8dfbb..2190466 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -304,8 +304,11 @@
 
 void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) {
     uint32_t tmpW, tmpH, transformHint, numPendingBuffers;
-    mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers);
-    mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers);
+    uint64_t nextFrameNumber;
+    mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers,
+            &nextFrameNumber);
+    mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers,
+            nextFrameNumber);
 
     mSinkBufferWidth = w;
     mSinkBufferHeight = h;
@@ -560,8 +563,8 @@
     return result;
 }
 
-status_t VirtualDisplaySurface::disconnect(int api) {
-    return mSource[SOURCE_SINK]->disconnect(api);
+status_t VirtualDisplaySurface::disconnect(int api, DisconnectMode mode) {
+    return mSource[SOURCE_SINK]->disconnect(api, mode);
 }
 
 status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
@@ -586,10 +589,6 @@
     return String8("VirtualDisplaySurface");
 }
 
-uint64_t VirtualDisplaySurface::getNextFrameNumber() const {
-    return 0;
-}
-
 status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) {
     ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface");
     return INVALID_OPERATION;
@@ -612,11 +611,17 @@
     return INVALID_OPERATION;
 }
 
+status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const {
+    ALOGE("getUniqueId not supported on VirtualDisplaySurface");
+    return INVALID_OPERATION;
+}
+
 void VirtualDisplaySurface::updateQueueBufferOutput(
         const QueueBufferOutput& qbo) {
     uint32_t w, h, transformHint, numPendingBuffers;
-    qbo.deflate(&w, &h, &transformHint, &numPendingBuffers);
-    mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers);
+    uint64_t nextFrameNumber;
+    qbo.deflate(&w, &h, &transformHint, &numPendingBuffers, &nextFrameNumber);
+    mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers, nextFrameNumber);
 }
 
 void VirtualDisplaySurface::resetPerFrameState() {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 29563b6..70f717f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -115,19 +115,19 @@
     virtual int query(int what, int* value);
     virtual status_t connect(const sp<IProducerListener>& listener,
             int api, bool producerControlledByApp, QueueBufferOutput* output);
-    virtual status_t disconnect(int api);
+    virtual status_t disconnect(int api, DisconnectMode mode);
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
     virtual void allocateBuffers(uint32_t width, uint32_t height,
             PixelFormat format, uint32_t usage);
     virtual status_t allowAllocation(bool allow);
     virtual status_t setGenerationNumber(uint32_t generationNumber);
     virtual String8 getConsumerName() const override;
-    virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    virtual status_t getUniqueId(uint64_t* outId) const override;
 
     //
     // Utility methods
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index feb8936..a104e8f 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -19,21 +19,14 @@
 
 namespace android {
 
-Daltonizer::Daltonizer() :
-    mType(deuteranomaly), mMode(simulation), mDirty(true) {
-}
-
-Daltonizer::~Daltonizer() {
-}
-
-void Daltonizer::setType(Daltonizer::ColorBlindnessTypes type) {
+void Daltonizer::setType(ColorBlindnessType type) {
     if (type != mType) {
         mDirty = true;
         mType = type;
     }
 }
 
-void Daltonizer::setMode(Daltonizer::Mode mode) {
+void Daltonizer::setMode(ColorBlindnessMode mode) {
     if (mode != mMode) {
         mDirty = true;
         mMode = mode;
@@ -49,6 +42,11 @@
 }
 
 void Daltonizer::update() {
+    if (mType == ColorBlindnessType::None) {
+        mColorTransform = mat4();
+        return;
+    }
+
     // converts a linear RGB color to the XYZ space
     const mat4 rgb2xyz( 0.4124, 0.2126, 0.0193, 0,
                         0.3576, 0.7152, 0.1192, 0,
@@ -149,24 +147,25 @@
     mat4 correction(0);
 
     switch (mType) {
-        case protanopia:
-        case protanomaly:
+        case ColorBlindnessType::Protanomaly:
             simulation = lms2lmsp;
-            if (mMode == Daltonizer::correction)
+            if (mMode == ColorBlindnessMode::Correction)
                 correction = errp;
             break;
-        case deuteranopia:
-        case deuteranomaly:
+        case ColorBlindnessType::Deuteranomaly:
             simulation = lms2lmsd;
-            if (mMode == Daltonizer::correction)
+            if (mMode == ColorBlindnessMode::Correction)
                 correction = errd;
             break;
-        case tritanopia:
-        case tritanomaly:
+        case ColorBlindnessType::Tritanomaly:
             simulation = lms2lmst;
-            if (mMode == Daltonizer::correction)
+            if (mMode == ColorBlindnessMode::Correction)
                 correction = errt;
             break;
+        case ColorBlindnessType::None:
+            // We already caught this at the beginning of the method, but the
+            // compiler doesn't know that
+            break;
     }
 
     mColorTransform = lms2rgb *
diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h
index e816437..d21b155 100644
--- a/services/surfaceflinger/Effects/Daltonizer.h
+++ b/services/surfaceflinger/Effects/Daltonizer.h
@@ -21,27 +21,22 @@
 
 namespace android {
 
+enum class ColorBlindnessType {
+    None,               // Disables the Daltonizer
+    Protanomaly,        // L (red) cone deficient
+    Deuteranomaly,      // M (green) cone deficient (most common)
+    Tritanomaly         // S (blue) cone deficient
+};
+
+enum class ColorBlindnessMode {
+    Simulation,
+    Correction
+};
+
 class Daltonizer {
 public:
-    enum ColorBlindnessTypes {
-        protanopia,         // L (red) cone missing
-        deuteranopia,       // M (green) cone missing
-        tritanopia,         // S (blue) cone missing
-        protanomaly,        // L (red) cone deficient
-        deuteranomaly,      // M (green) cone deficient (most common)
-        tritanomaly         // S (blue) cone deficient
-    };
-
-    enum Mode {
-        simulation,
-        correction
-    };
-
-    Daltonizer();
-    ~Daltonizer();
-
-    void setType(ColorBlindnessTypes type);
-    void setMode(Mode mode);
+    void setType(ColorBlindnessType type);
+    void setMode(ColorBlindnessMode mode);
 
     // returns the color transform to apply in the shader
     const mat4& operator()();
@@ -49,9 +44,9 @@
 private:
     void update();
 
-    ColorBlindnessTypes mType;
-    Mode mMode;
-    bool mDirty;
+    ColorBlindnessType mType = ColorBlindnessType::None;
+    ColorBlindnessMode mMode = ColorBlindnessMode::Simulation;
+    bool mDirty = true;
     mat4 mColorTransform;
 };
 
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
index 47bab83..365a0bd 100644
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ b/services/surfaceflinger/EventLog/EventLog.cpp
@@ -16,7 +16,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <utils/String8.h>
 
 #include "EventLog.h"
diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
index 885d712..0e18a93 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -26,7 +26,9 @@
 FenceTracker::FenceTracker() :
         mFrameCounter(0),
         mOffset(0),
-        mFrames() {}
+        mFrames(),
+        mMutex() {
+}
 
 void FenceTracker::dump(String8* outString) {
     Mutex::Autolock lock(mMutex);
@@ -135,31 +137,30 @@
         nsecs_t postedTime;
         sp<Fence> acquireFence;
         sp<Fence> prevReleaseFence;
-        int32_t key = layers[i]->getSequence();
+        int32_t layerId = layers[i]->getSequence();
 
         layers[i]->getFenceData(&name, &frameNumber, &glesComposition,
                 &postedTime, &acquireFence, &prevReleaseFence);
 #ifdef USE_HWC2
         if (glesComposition) {
             frame.layers.emplace(std::piecewise_construct,
-                    std::forward_as_tuple(key),
+                    std::forward_as_tuple(layerId),
                     std::forward_as_tuple(name, frameNumber, glesComposition,
                     postedTime, 0, 0, acquireFence, prevReleaseFence));
             wasGlesCompositionDone = true;
         } else {
             frame.layers.emplace(std::piecewise_construct,
-                    std::forward_as_tuple(key),
+                    std::forward_as_tuple(layerId),
                     std::forward_as_tuple(name, frameNumber, glesComposition,
                     postedTime, 0, 0, acquireFence, Fence::NO_FENCE));
-
-            auto prevLayer = prevFrame.layers.find(key);
+            auto prevLayer = prevFrame.layers.find(layerId);
             if (prevLayer != prevFrame.layers.end()) {
                 prevLayer->second.releaseFence = prevReleaseFence;
             }
         }
 #else
         frame.layers.emplace(std::piecewise_construct,
-                std::forward_as_tuple(key),
+                std::forward_as_tuple(layerId),
                 std::forward_as_tuple(name, frameNumber, glesComposition,
                 postedTime, 0, 0, acquireFence,
                 glesComposition ? Fence::NO_FENCE : prevReleaseFence));
@@ -168,7 +169,7 @@
         }
 #endif
         frame.layers.emplace(std::piecewise_construct,
-                std::forward_as_tuple(key),
+                std::forward_as_tuple(layerId),
                 std::forward_as_tuple(name, frameNumber, glesComposition,
                 postedTime, 0, 0, acquireFence, prevReleaseFence));
     }
@@ -184,8 +185,35 @@
 
     mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
     mFrameCounter++;
+}
 
+bool FenceTracker::getFrameTimestamps(const Layer& layer,
+        uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+    Mutex::Autolock lock(mMutex);
     checkFencesForCompletion();
+    int32_t layerId = layer.getSequence();
+
+    size_t i = 0;
+    for (; i < MAX_FRAME_HISTORY; i++) {
+       if (mFrames[i].layers.count(layerId) &&
+               mFrames[i].layers[layerId].frameNumber == frameNumber) {
+           break;
+       }
+    }
+    if (i == MAX_FRAME_HISTORY) {
+        return false;
+    }
+
+    const FrameRecord& frameRecord = mFrames[i];
+    const LayerRecord& layerRecord = mFrames[i].layers[layerId];
+    outTimestamps->frameNumber = frameNumber;
+    outTimestamps->postedTime = layerRecord.postedTime;
+    outTimestamps->acquireTime = layerRecord.acquireTime;
+    outTimestamps->refreshStartTime = frameRecord.refreshStartTime;
+    outTimestamps->glCompositionDoneTime = frameRecord.glesCompositionDoneTime;
+    outTimestamps->displayRetireTime = frameRecord.retireTime;
+    outTimestamps->releaseTime = layerRecord.releaseTime;
+    return true;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
index de99820..4cb14a5 100644
--- a/services/surfaceflinger/FenceTracker.h
+++ b/services/surfaceflinger/FenceTracker.h
@@ -29,7 +29,7 @@
 namespace android {
 
 class Layer;
-
+struct FrameTimestamps;
 /*
  * Keeps a circular buffer of fence/timestamp data for the last N frames in
  * SurfaceFlinger. Gets timestamps for fences after they have signaled.
@@ -40,9 +40,11 @@
      void dump(String8* outString);
      void addFrame(nsecs_t refreshStartTime, sp<Fence> retireFence,
              const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
+     bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
+             FrameTimestamps* outTimestamps);
 
 protected:
-     static constexpr size_t MAX_FRAME_HISTORY = 128;
+     static constexpr size_t MAX_FRAME_HISTORY = 8;
 
      struct LayerRecord {
          String8 name; // layer name
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index c09bbe4..99c4f62 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -19,13 +19,12 @@
 
 #include <inttypes.h>
 
-#include <cutils/log.h>
+#include <android/log.h>
+#include <utils/String8.h>
 
 #include <ui/Fence.h>
 #include <ui/FrameStats.h>
 
-#include <utils/String8.h>
-
 #include "FrameTracker.h"
 #include "EventLog/EventLog.h"
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 89e37b2..d13b6db 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -51,6 +51,8 @@
 
 #include "RenderEngine/RenderEngine.h"
 
+#include <mutex>
+
 #define DEBUG_RESIZE    0
 
 namespace android {
@@ -155,7 +157,8 @@
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
     mProducer = new MonitoredProducer(producer, mFlinger);
-    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName);
+    mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(consumer, mTextureName,
+            this);
     mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
     mSurfaceFlingerConsumer->setContentsChangedListener(this);
     mSurfaceFlingerConsumer->setName(mName);
@@ -554,7 +557,7 @@
 #endif
             activeCrop.clear();
         }
-        activeCrop = s.active.transform.inverse().transform(activeCrop);
+        activeCrop = s.active.transform.inverse().transform(activeCrop, true);
         // This needs to be here as transform.transform(Rect) computes the
         // transformed rect and then takes the bounding box of the result before
         // returning. This means
@@ -586,19 +589,25 @@
     const Transform& tr(displayDevice->getTransform());
     Rect transformedFrame = tr.transform(frame);
     auto error = hwcLayer->setDisplayFrame(transformedFrame);
-    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set display frame "
-            "[%d, %d, %d, %d]: %s (%d)", mName.string(), transformedFrame.left,
-            transformedFrame.top, transformedFrame.right,
-            transformedFrame.bottom, to_string(error).c_str(),
-            static_cast<int32_t>(error));
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+                mName.string(), transformedFrame.left, transformedFrame.top,
+                transformedFrame.right, transformedFrame.bottom,
+                to_string(error).c_str(), static_cast<int32_t>(error));
+    } else {
+        hwcInfo.displayFrame = transformedFrame;
+    }
 
     FloatRect sourceCrop = computeCrop(displayDevice);
     error = hwcLayer->setSourceCrop(sourceCrop);
-    ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set source crop "
-            "[%.3f, %.3f, %.3f, %.3f]: %s (%d)", mName.string(),
-            sourceCrop.left, sourceCrop.top, sourceCrop.right,
-            sourceCrop.bottom, to_string(error).c_str(),
-            static_cast<int32_t>(error));
+    if (error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+                "%s (%d)", mName.string(), sourceCrop.left, sourceCrop.top,
+                sourceCrop.right, sourceCrop.bottom, to_string(error).c_str(),
+                static_cast<int32_t>(error));
+    } else {
+        hwcInfo.sourceCrop = sourceCrop;
+    }
 
     error = hwcLayer->setPlaneAlpha(s.alpha);
     ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set plane alpha %.3f: "
@@ -1113,6 +1122,20 @@
     return static_cast<uint32_t>(producerStickyTransform);
 }
 
+bool Layer::latchUnsignaledBuffers() {
+    static bool propertyLoaded = false;
+    static bool latch = false;
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    if (!propertyLoaded) {
+        char value[PROPERTY_VALUE_MAX] = {};
+        property_get("debug.sf.latch_unsignaled", value, "0");
+        latch = atoi(value);
+        propertyLoaded = true;
+    }
+    return latch;
+}
+
 uint64_t Layer::getHeadFrameNumber() const {
     Mutex::Autolock lock(mQueueItemLock);
     if (!mQueueItems.empty()) {
@@ -1122,6 +1145,29 @@
     }
 }
 
+bool Layer::headFenceHasSignaled() const {
+#ifdef USE_HWC2
+    if (latchUnsignaledBuffers()) {
+        return true;
+    }
+
+    Mutex::Autolock lock(mQueueItemLock);
+    if (mQueueItems.empty()) {
+        return true;
+    }
+    if (mQueueItems[0].mIsDroppable) {
+        // Even though this buffer's fence may not have signaled yet, it could
+        // be replaced by another buffer before it has a chance to, which means
+        // that it's possible to get into a situation where a buffer is never
+        // able to be latched. To avoid this, grab this buffer anyway.
+        return true;
+    }
+    return mQueueItems[0].mFence->getSignalTime() != INT64_MAX;
+#else
+    return true;
+#endif
+}
+
 bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
     if (point->getFrameNumber() <= mCurrentFrameNumber) {
         // Don't bother with a SyncPoint, since we've already latched the
@@ -1384,9 +1430,10 @@
 
 void Layer::notifyAvailableFrames() {
     auto headFrameNumber = getHeadFrameNumber();
+    bool headFenceSignaled = headFenceHasSignaled();
     Mutex::Autolock lock(mLocalSyncPointMutex);
     for (auto& point : mLocalSyncPoints) {
-        if (headFrameNumber >= point->getFrameNumber()) {
+        if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) {
             point->setFrameAvailable();
         }
     }
@@ -1596,11 +1643,15 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-bool Layer::setCrop(const Rect& crop) {
+
+bool Layer::setCrop(const Rect& crop, bool immediate) {
     if (mCurrentState.crop == crop)
         return false;
     mCurrentState.sequence++;
-    mCurrentState.crop = crop;
+    mCurrentState.requestedCrop = crop;
+    if (immediate) {
+        mCurrentState.crop = crop;
+    }
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1697,7 +1748,8 @@
     return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh;
 }
 
-void Layer::onPostComposition() {
+bool Layer::onPostComposition() {
+    bool frameLatencyNeeded = mFrameLatencyNeeded;
     if (mFrameLatencyNeeded) {
         nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
         mFrameTracker.setDesiredPresentTime(desiredPresentTime);
@@ -1729,6 +1781,7 @@
         mFrameTracker.advanceFrame();
         mFrameLatencyNeeded = false;
     }
+    return frameLatencyNeeded;
 }
 
 #ifdef USE_HWC2
@@ -1777,6 +1830,13 @@
             return outDirtyRegion;
         }
 
+        // If the head buffer's acquire fence hasn't signaled yet, return and
+        // try again later
+        if (!headFenceHasSignaled()) {
+            mFlinger->signalLayerUpdate();
+            return outDirtyRegion;
+        }
+
         // Capture the old state of the layer for comparisons later
         const State& s(getDrawingState());
         const bool oldOpacity = isOpaque(s);
@@ -1789,16 +1849,19 @@
             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)
+                    int32_t overrideScalingMode,
+                    bool& freezePositionUpdates)
                 : front(front), current(current),
                   recomputeVisibleRegions(recomputeVisibleRegions),
                   stickyTransformSet(stickySet),
                   name(name),
-                  overrideScalingMode(overrideScalingMode) {
+                  overrideScalingMode(overrideScalingMode),
+                  freezePositionUpdates(freezePositionUpdates) {
             }
 
             virtual bool reject(const sp<GraphicBuffer>& buf,
@@ -1890,13 +1953,20 @@
                     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);
+                mOverrideScalingMode, mFreezePositionUpdates);
 
 
         // Check all of our local sync points to ensure that all transactions
@@ -2039,7 +2109,6 @@
             if (bufWidth != uint32_t(oldActiveBuffer->width) ||
                 bufHeight != uint32_t(oldActiveBuffer->height)) {
                 recomputeVisibleRegions = true;
-                mFreezePositionUpdates = false;
             }
         }
 
@@ -2170,6 +2239,54 @@
     }
 }
 
+#ifdef USE_HWC2
+void Layer::miniDumpHeader(String8& result) {
+    result.append("----------------------------------------");
+    result.append("---------------------------------------\n");
+    result.append(" Layer name\n");
+    result.append("           Z | ");
+    result.append(" Comp Type | ");
+    result.append("  Disp Frame (LTRB) | ");
+    result.append("         Source Crop (LTRB)\n");
+    result.append("----------------------------------------");
+    result.append("---------------------------------------\n");
+}
+
+void Layer::miniDump(String8& result, int32_t hwcId) const {
+    if (mHwcLayers.count(hwcId) == 0) {
+        return;
+    }
+
+    String8 name;
+    if (mName.length() > 77) {
+        std::string shortened;
+        shortened.append(mName.string(), 36);
+        shortened.append("[...]");
+        shortened.append(mName.string() + (mName.length() - 36), 36);
+        name = shortened.c_str();
+    } else {
+        name = mName;
+    }
+
+    result.appendFormat(" %s\n", name.string());
+
+    const Layer::State& layerState(getDrawingState());
+    const HWCInfo& hwcInfo = mHwcLayers.at(hwcId);
+    result.appendFormat("  %10u | ", layerState.z);
+    result.appendFormat("%10s | ",
+            to_string(getCompositionType(hwcId)).c_str());
+    const Rect& frame = hwcInfo.displayFrame;
+    result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top,
+            frame.right, frame.bottom);
+    const FloatRect& crop = hwcInfo.sourceCrop;
+    result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top,
+            crop.right, crop.bottom);
+
+    result.append("- - - - - - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - - - - -\n");
+}
+#endif
+
 void Layer::dumpFrameStats(String8& result) const {
     mFrameTracker.dumpStats(result);
 }
@@ -2203,6 +2320,24 @@
     *outAcquireFence = mSurfaceFlingerConsumer->getCurrentFence();
     *outPrevReleaseFence = mSurfaceFlingerConsumer->getPrevReleaseFence();
 }
+
+std::vector<OccupancyTracker::Segment> Layer::getOccupancyHistory(
+        bool forceFlush) {
+    std::vector<OccupancyTracker::Segment> history;
+    status_t result = mSurfaceFlingerConsumer->getOccupancyHistory(forceFlush,
+            &history);
+    if (result != NO_ERROR) {
+        ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(),
+                result);
+        return {};
+    }
+    return history;
+}
+
+bool Layer::getTransformToDisplayInverse() const {
+    return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
+}
+
 // ---------------------------------------------------------------------------
 
 Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c96e7d5..24de87e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -96,7 +96,9 @@
         Transform transform;
 
         inline bool operator ==(const Geometry& rhs) const {
-          return (w == rhs.w && h == rhs.h);
+            return (w == rhs.w && h == rhs.h) &&
+                    (transform.tx() == rhs.transform.tx()) &&
+                    (transform.ty() == rhs.transform.ty());
         }
         inline bool operator !=(const Geometry& rhs) const {
             return !operator ==(rhs);
@@ -120,6 +122,8 @@
         bool modified;
 
         Rect crop;
+        Rect requestedCrop;
+
         Rect finalCrop;
 
         // If set, defers this state update until the Layer identified by handle
@@ -156,7 +160,7 @@
     bool setMatrix(const layer_state_t::matrix22_t& matrix);
     bool setTransparentRegionHint(const Region& transparent);
     bool setFlags(uint8_t flags, uint8_t mask);
-    bool setCrop(const Rect& crop);
+    bool setCrop(const Rect& crop, bool immediate);
     bool setFinalCrop(const Rect& crop);
     bool setLayerStack(uint32_t layerStack);
     void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
@@ -274,9 +278,10 @@
     bool onPreComposition();
 
     /*
-     *  called after composition.
+     * called after composition.
+     * returns true if the layer latched a new buffer this frame.
      */
-    void onPostComposition();
+    bool onPostComposition();
 
 #ifdef USE_HWC2
     // If a buffer was replaced this frame, release the former buffer
@@ -397,6 +402,10 @@
 
     /* always call base class first */
     void dump(String8& result, Colorizer& colorizer) const;
+#ifdef USE_HWC2
+    static void miniDumpHeader(String8& result);
+    void miniDump(String8& result, int32_t hwcId) const;
+#endif
     void dumpFrameStats(String8& result) const;
     void clearFrameStats();
     void logFrameStats();
@@ -406,6 +415,15 @@
             bool* outIsGlesComposition, nsecs_t* outPostedTime,
             sp<Fence>* outAcquireFence, sp<Fence>* outPrevReleaseFence) const;
 
+    std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush);
+
+    bool getFrameTimestamps(uint64_t frameNumber,
+            FrameTimestamps* outTimestamps) const {
+        return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
+    }
+
+    bool getTransformToDisplayInverse() const;
+
 protected:
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -451,6 +469,9 @@
     // Temporary - Used only for LEGACY camera mode.
     uint32_t getProducerStickyTransform() const;
 
+    // Loads the corresponding system property once per process
+    static bool latchUnsignaledBuffers();
+
     // -----------------------------------------------------------------------
 
     class SyncPoint
@@ -496,6 +517,7 @@
     std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints;
 
     uint64_t getHeadFrameNumber() const;
+    bool headFenceHasSignaled() const;
 
     // Returns false if the relevant frame has already been latched
     bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
@@ -570,6 +592,8 @@
         bool forceClientComposition;
         HWC2::Composition compositionType;
         bool clearClientTarget;
+        Rect displayFrame;
+        FloatRect sourceCrop;
     };
     std::unordered_map<int32_t, HWCInfo> mHwcLayers;
 #else
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 34dc24b..974c7a3 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -134,31 +134,12 @@
 }
 
 
-/* when INVALIDATE_ON_VSYNC is set SF only processes
- * buffer updates on VSYNC and performs a refresh immediately
- * after.
- *
- * when INVALIDATE_ON_VSYNC is set to false, SF will instead
- * perform the buffer updates immediately, but the refresh only
- * at the next VSYNC.
- * THIS MODE IS BUGGY ON GALAXY NEXUS AND WILL CAUSE HANGS
- */
-#define INVALIDATE_ON_VSYNC 1
-
 void MessageQueue::invalidate() {
-#if INVALIDATE_ON_VSYNC
     mEvents->requestNextVsync();
-#else
-    mHandler->dispatchInvalidate();
-#endif
 }
 
 void MessageQueue::refresh() {
-#if INVALIDATE_ON_VSYNC
     mHandler->dispatchRefresh();
-#else
-    mEvents->requestNextVsync();
-#endif
 }
 
 int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
@@ -172,11 +153,7 @@
     while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
         for (int i=0 ; i<n ; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-#if INVALIDATE_ON_VSYNC
                 mHandler->dispatchInvalidate();
-#else
-                mHandler->dispatchRefresh();
-#endif
                 break;
             }
         }
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index faab62c..ffaee7a 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -102,8 +102,8 @@
     return mProducer->connect(listener, api, producerControlledByApp, output);
 }
 
-status_t MonitoredProducer::disconnect(int api) {
-    return mProducer->disconnect(api);
+status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) {
+    return mProducer->disconnect(api, mode);
 }
 
 status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) {
@@ -127,10 +127,6 @@
     return mProducer->getConsumerName();
 }
 
-uint64_t MonitoredProducer::getNextFrameNumber() const {
-    return mProducer->getNextFrameNumber();
-}
-
 status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) {
     return mProducer->setSharedBufferMode(sharedBufferMode);
 }
@@ -149,6 +145,10 @@
             outTransformMatrix);
 }
 
+status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
+    return mProducer->getUniqueId(outId);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return IInterface::asBinder(mProducer).get();
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index ce756dc..66f6cf0 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -50,20 +50,20 @@
     virtual int query(int what, int* value);
     virtual status_t connect(const sp<IProducerListener>& token, int api,
             bool producerControlledByApp, QueueBufferOutput* output);
-    virtual status_t disconnect(int api);
+    virtual status_t disconnect(int api, DisconnectMode mode);
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
     virtual void allocateBuffers(uint32_t width, uint32_t height,
             PixelFormat format, uint32_t usage);
     virtual status_t allowAllocation(bool allow);
     virtual status_t setGenerationNumber(uint32_t generationNumber);
     virtual String8 getConsumerName() const override;
-    virtual uint64_t getNextFrameNumber() const override;
     virtual status_t setDequeueTimeout(nsecs_t timeout) override;
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
     virtual IBinder* onAsBinder();
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
+    virtual status_t getUniqueId(uint64_t* outId) const override;
 
 private:
     sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/services/surfaceflinger/RenderEngine/Program.cpp
index 0424e0c..48a8da5 100644
--- a/services/surfaceflinger/RenderEngine/Program.cpp
+++ b/services/surfaceflinger/RenderEngine/Program.cpp
@@ -17,11 +17,11 @@
 #include <stdint.h>
 
 #include <log/log.h>
+#include <utils/String8.h>
 
 #include "Program.h"
 #include "ProgramCache.h"
 #include "Description.h"
-#include <utils/String8.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index 27357b9..2aec9e9 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -436,6 +436,13 @@
     return config;
 }
 
+
+void RenderEngine::primeCache() const {
+    // Getting the ProgramCache instance causes it to prime its shader cache,
+    // which is performed in its constructor
+    ProgramCache::getInstance();
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 9cc1ed7..0259881 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -63,6 +63,8 @@
 
     static EGLConfig chooseEglConfig(EGLDisplay display, int format);
 
+    void primeCache() const;
+
     // dump the extension strings. always call the base class.
     virtual void dump(String8& result);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 47f5401..dabece2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -27,8 +27,8 @@
 
 #include <EGL/egl.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -90,19 +90,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
@@ -165,7 +152,6 @@
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
-        mDaltonize(false),
         mHasColorMatrix(false),
         mHasPoweredOff(false),
         mFrameBuckets(),
@@ -180,9 +166,6 @@
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
     mGpuToCpuSupported = !atoi(value);
 
-    property_get("debug.sf.drop_missed_frames", value, "0");
-    mDropMissedFrames = atoi(value);
-
     property_get("debug.sf.showupdates", value, "0");
     mDebugRegion = atoi(value);
 
@@ -196,6 +179,14 @@
     }
     ALOGI_IF(mDebugRegion, "showupdates enabled");
     ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
+
+    property_get("debug.sf.disable_backpressure", value, "0");
+    mPropagateBackpressure = !atoi(value);
+    ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation");
+
+    property_get("debug.sf.disable_hwc_vds", value, "0");
+    mUseHwcVirtualDisplays = !atoi(value);
+    ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -478,6 +469,13 @@
         mSFEventThread = new EventThread(sfVsyncSrc, *this);
         mEventQueue.setEventThread(mSFEventThread);
 
+        // set SFEventThread to SCHED_FIFO to minimize jitter
+        struct sched_param param = {0};
+        param.sched_priority = 2;
+        if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, &param) != 0) {
+            ALOGE("Couldn't set SCHED_FIFO for SFEventThread");
+        }
+
         // Get a RenderEngine for the given display / config (can't fail)
         mRenderEngine = RenderEngine::create(mEGLDisplay,
                 HAL_PIXEL_FORMAT_RGBA_8888);
@@ -510,6 +508,8 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
+    mRenderEngine->primeCache();
+
     // start boot animation
     startBootAnim();
 
@@ -618,9 +618,6 @@
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
         info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
 
-        // TODO: Hook this back up
-        info.colorTransform = 0;
-
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
         // on the screen at time N, you must submit the buffer before
@@ -659,6 +656,10 @@
 }
 
 int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
+    if (display == NULL) {
+        ALOGE("%s : display is NULL", __func__);
+        return BAD_VALUE;
+    }
     sp<DisplayDevice> device(getDisplayDevice(display));
     if (device != NULL) {
         return device->getActiveConfig();
@@ -701,6 +702,7 @@
             if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
                 ALOGE("Attempt to set active config = %d for display with %zu configs",
                         mMode, configs.size());
+                return true;
             }
             sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
             if (hw == NULL) {
@@ -719,6 +721,101 @@
     postMessageSync(msg);
     return NO_ERROR;
 }
+status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display,
+        Vector<android_color_mode_t>* outColorModes) {
+    if ((outColorModes == nullptr) || (display.get() == nullptr)) {
+        return BAD_VALUE;
+    }
+
+    if (!display.get()) {
+        return NAME_NOT_FOUND;
+    }
+
+    int32_t type = NAME_NOT_FOUND;
+    for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
+        if (display == mBuiltinDisplays[i]) {
+            type = i;
+            break;
+        }
+    }
+
+    if (type < 0) {
+        return type;
+    }
+
+    std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type);
+    outColorModes->clear();
+    std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes));
+
+    return NO_ERROR;
+}
+
+android_color_mode_t SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) {
+    sp<DisplayDevice> device(getDisplayDevice(display));
+    if (device != nullptr) {
+        return device->getActiveColorMode();
+    }
+    return static_cast<android_color_mode_t>(BAD_VALUE);
+}
+
+void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw,
+        android_color_mode_t mode) {
+    ALOGD("Set active color mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(),
+          this);
+    int32_t type = hw->getDisplayType();
+    android_color_mode_t currentMode = hw->getActiveColorMode();
+
+    if (mode == currentMode) {
+        ALOGD("Screen type=%d is already in color mode=%d", hw->getDisplayType(), mode);
+        return;
+    }
+
+    if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
+        ALOGW("Trying to set config for virtual display");
+        return;
+    }
+
+    hw->setActiveColorMode(mode);
+    getHwComposer().setActiveColorMode(type, mode);
+}
+
+
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display,
+        android_color_mode_t colorMode) {
+    class MessageSetActiveColorMode: public MessageBase {
+        SurfaceFlinger& mFlinger;
+        sp<IBinder> mDisplay;
+        android_color_mode_t mMode;
+    public:
+        MessageSetActiveColorMode(SurfaceFlinger& flinger, const sp<IBinder>& disp,
+                               android_color_mode_t mode) :
+            mFlinger(flinger), mDisplay(disp) { mMode = mode; }
+        virtual bool handler() {
+            Vector<android_color_mode_t> modes;
+            mFlinger.getDisplayColorModes(mDisplay, &modes);
+            bool exists = std::find(std::begin(modes), std::end(modes), mMode) != std::end(modes);
+            if (mMode < 0 || !exists) {
+                ALOGE("Attempt to set invalid active color mode = %d for display %p", mMode,
+                        mDisplay.get());
+                return true;
+            }
+            sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay));
+            if (hw == nullptr) {
+                ALOGE("Attempt to set active color mode = %d for null display %p",
+                        mMode, mDisplay.get());
+            } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
+                ALOGW("Attempt to set active color mode= %d for virtual display",
+                        mMode);
+            } else {
+                mFlinger.setActiveColorModeInternal(hw, mMode);
+            }
+            return true;
+        }
+    };
+    sp<MessageBase> msg = new MessageSetActiveColorMode(*this, display, colorMode);
+    postMessageSync(msg);
+    return NO_ERROR;
+}
 
 status_t SurfaceFlinger::clearAnimationFrameStats() {
     Mutex::Autolock _l(mStateLock);
@@ -917,6 +1014,15 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
+            bool frameMissed = !mHadClientComposition &&
+                    mPreviousPresentFence != Fence::NO_FENCE &&
+                    mPreviousPresentFence->getSignalTime() == INT64_MAX;
+            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
+            if (mPropagateBackpressure && frameMissed) {
+                signalLayerUpdate();
+                break;
+            }
+
             bool refreshNeeded = handleMessageTransaction();
             refreshNeeded |= handleMessageInvalidate();
             refreshNeeded |= mRepaintEverything;
@@ -952,32 +1058,22 @@
 void SurfaceFlinger::handleMessageRefresh() {
     ATRACE_CALL();
 
-#ifdef ENABLE_FENCE_TRACKING
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#else
-    nsecs_t refreshStartTime = 0;
-#endif
-    static nsecs_t previousExpectedPresent = 0;
-    nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
-    static bool previousFrameMissed = false;
-    bool frameMissed = (expectedPresent == previousExpectedPresent);
-    if (frameMissed != previousFrameMissed) {
-        ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
-    }
-    previousFrameMissed = frameMissed;
 
-    if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
-        // Latch buffers, but don't send anything to HWC, then signal another
-        // wakeup for the next vsync
-        preComposition();
-        repaintEverything();
-    } else {
-        preComposition();
-        rebuildLayerStacks();
-        setUpHWComposer();
-        doDebugFlashRegions();
-        doComposition();
-        postComposition(refreshStartTime);
+    preComposition();
+    rebuildLayerStacks();
+    setUpHWComposer();
+    doDebugFlashRegions();
+    doComposition();
+    postComposition(refreshStartTime);
+
+    mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+
+    mHadClientComposition = false;
+    for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
+        const sp<DisplayDevice>& displayDevice = mDisplays[displayId];
+        mHadClientComposition = mHadClientComposition ||
+                mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
     }
 
     // Release any buffers which were replaced this frame
@@ -985,8 +1081,6 @@
         layer->releasePendingBuffer();
     }
     mLayersWithQueuedFrames.clear();
-
-    previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0);
 }
 
 void SurfaceFlinger::doDebugFlashRegions()
@@ -1051,11 +1145,7 @@
     }
 }
 
-#ifdef ENABLE_FENCE_TRACKING
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
 {
     ATRACE_CALL();
     ALOGV("postComposition");
@@ -1063,7 +1153,11 @@
     const LayerVector& layers(mDrawingState.layersSortedByZ);
     const size_t count = layers.size();
     for (size_t i=0 ; i<count ; i++) {
-        layers[i]->onPostComposition();
+        bool frameLatched = layers[i]->onPostComposition();
+        if (frameLatched) {
+            recordBufferingStats(layers[i]->getName().string(),
+                    layers[i]->getOccupancyHistory(false));
+        }
     }
 
     sp<Fence> presentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
@@ -1083,10 +1177,8 @@
         }
     }
 
-#ifdef ENABLE_FENCE_TRACKING
     mFenceTracker.addFrame(refreshStartTime, presentFence,
             hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
 
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
@@ -1231,8 +1323,7 @@
                     }
 
                     layer->setGeometry(displayDevice);
-                    if (mDebugDisableHWC || mDebugRegion || mDaltonize ||
-                            mHasColorMatrix) {
+                    if (mDebugDisableHWC || mDebugRegion) {
                         layer->forceClientComposition(hwcId);
                     }
                 }
@@ -1240,6 +1331,9 @@
         }
     }
 
+
+    mat4 colorMatrix = mColorMatrix * mDaltonizer();
+
     // Set the per-frame data
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
         auto& displayDevice = mDisplays[displayId];
@@ -1247,11 +1341,18 @@
         if (hwcId < 0) {
             continue;
         }
+        if (colorMatrix != mPreviousColorMatrix) {
+            status_t result = mHwc->setColorTransform(hwcId, colorMatrix);
+            ALOGE_IF(result != NO_ERROR, "Failed to set color transform on "
+                    "display %zd: %d", displayId, result);
+        }
         for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             layer->setPerFrameData(displayDevice);
         }
     }
 
+    mPreviousColorMatrix = colorMatrix;
+
     for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
         auto& displayDevice = mDisplays[displayId];
         if (!displayDevice->isDisplayOn()) {
@@ -1489,26 +1590,28 @@
                         // etc.) but no internal state (i.e. a DisplayDevice).
                         if (state.surface != NULL) {
 
-                            int width = 0;
-                            int status = state.surface->query(
-                                    NATIVE_WINDOW_WIDTH, &width);
-                            ALOGE_IF(status != NO_ERROR,
-                                    "Unable to query width (%d)", status);
-                            int height = 0;
-                            status = state.surface->query(
-                                    NATIVE_WINDOW_HEIGHT, &height);
-                            ALOGE_IF(status != NO_ERROR,
-                                    "Unable to query height (%d)", status);
-                            int intFormat = 0;
-                            status = state.surface->query(
-                                    NATIVE_WINDOW_FORMAT, &intFormat);
-                            ALOGE_IF(status != NO_ERROR,
-                                    "Unable to query format (%d)", status);
-                            auto format = static_cast<android_pixel_format_t>(
-                                    intFormat);
+                            if (mUseHwcVirtualDisplays) {
+                                int width = 0;
+                                int status = state.surface->query(
+                                        NATIVE_WINDOW_WIDTH, &width);
+                                ALOGE_IF(status != NO_ERROR,
+                                        "Unable to query width (%d)", status);
+                                int height = 0;
+                                status = state.surface->query(
+                                        NATIVE_WINDOW_HEIGHT, &height);
+                                ALOGE_IF(status != NO_ERROR,
+                                        "Unable to query height (%d)", status);
+                                int intFormat = 0;
+                                status = state.surface->query(
+                                        NATIVE_WINDOW_FORMAT, &intFormat);
+                                ALOGE_IF(status != NO_ERROR,
+                                        "Unable to query format (%d)", status);
+                                auto format = static_cast<android_pixel_format_t>(
+                                        intFormat);
 
-                            mHwc->allocateVirtualDisplay(width, height, &format,
-                                    &hwcId);
+                                mHwc->allocateVirtualDisplay(width, height, &format,
+                                        &hwcId);
+                            }
 
                             // TODO: Plumb requested format back up to consumer
 
@@ -1672,6 +1775,8 @@
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+            recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
+                    mLayersPendingRemoval[i]->getOccupancyHistory(true));
             mLayersPendingRemoval[i]->onRemoved();
         }
         mLayersPendingRemoval.clear();
@@ -1931,18 +2036,7 @@
         }
     }
 
-    if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
-        if (!doComposeSurfaces(hw, dirtyRegion)) return;
-    } else {
-        RenderEngine& engine(getRenderEngine());
-        mat4 colorMatrix = mColorMatrix;
-        if (mDaltonize) {
-            colorMatrix = colorMatrix * mDaltonizer();
-        }
-        mat4 oldMatrix = engine.setupColorTransform(colorMatrix);
-        doComposeSurfaces(hw, dirtyRegion);
-        engine.setupColorTransform(oldMatrix);
-    }
+    if (!doComposeSurfaces(hw, dirtyRegion)) return;
 
     // update the swap region and clear the dirty region
     hw->swapRegion.orSelf(dirtyRegion);
@@ -1957,6 +2051,15 @@
     ALOGV("doComposeSurfaces");
 
     const auto hwcId = displayDevice->getHwcDisplayId();
+
+    mat4 oldColorMatrix;
+    const bool applyColorMatrix = !mHwc->hasDeviceComposition(hwcId) &&
+            !mHwc->hasCapability(HWC2::Capability::SkipClientColorTransform);
+    if (applyColorMatrix) {
+        mat4 colorMatrix = mColorMatrix * mDaltonizer();
+        oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix);
+    }
+
     bool hasClientComposition = mHwc->hasClientComposition(hwcId);
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
@@ -2074,6 +2177,10 @@
         }
     }
 
+    if (applyColorMatrix) {
+        getRenderEngine().setupColorTransform(oldColorMatrix);
+    }
+
     // disable scissor at the end of the frame
     mRenderEngine->disableScissor();
     return true;
@@ -2283,10 +2390,10 @@
     sp<Layer> layer(client->getLayerUser(s.surface));
     if (layer != 0) {
         const uint32_t what = s.what;
-        bool positionAppliesWithResize =
-                what & layer_state_t::ePositionAppliesWithResize;
+        bool geometryAppliesWithResize =
+                what & layer_state_t::eGeometryAppliesWithResize;
         if (what & layer_state_t::ePositionChanged) {
-            if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
+            if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
                 flags |= eTraversalNeeded;
             }
         }
@@ -2323,7 +2430,7 @@
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eCropChanged) {
-            if (layer->setCrop(s.crop))
+            if (layer->setCrop(s.crop, !geometryAppliesWithResize))
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eFinalCropChanged) {
@@ -2515,6 +2622,7 @@
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
+        // Turn on the display
         getHwComposer().setPowerMode(type, mode);
         if (type == DisplayDevice::DISPLAY_PRIMARY) {
             // FIXME: eventthread only knows about the main display right now
@@ -2525,7 +2633,19 @@
         mVisibleRegionsDirty = true;
         mHasPoweredOff = true;
         repaintEverything();
+
+        struct sched_param param = {0};
+        param.sched_priority = 1;
+        if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
+            ALOGW("Couldn't set SCHED_FIFO on display on");
+        }
     } else if (mode == HWC_POWER_MODE_OFF) {
+        // Turn off the display
+        struct sched_param param = {0};
+        if (sched_setscheduler(0, SCHED_OTHER, &param) != 0) {
+            ALOGW("Couldn't set SCHED_OTHER on display off");
+        }
+
         if (type == DisplayDevice::DISPLAY_PRIMARY) {
             disableHardwareVsync(true); // also cancels any in-progress resync
 
@@ -2632,14 +2752,12 @@
                 dumpAll = false;
             }
 
-#ifdef ENABLE_FENCE_TRACKING
             if ((index < numArgs) &&
                     (args[index] == String16("--fences"))) {
                 index++;
                 mFenceTracker.dump(&result);
                 dumpAll = false;
             }
-#endif
         }
 
         if (dumpAll) {
@@ -2760,6 +2878,59 @@
             NUM_BUCKETS - 1, bucketTimeSec, percent);
 }
 
+void SurfaceFlinger::recordBufferingStats(const char* layerName,
+        std::vector<OccupancyTracker::Segment>&& history) {
+    Mutex::Autolock lock(mBufferingStatsMutex);
+    auto& stats = mBufferingStats[layerName];
+    for (const auto& segment : history) {
+        if (!segment.usedThirdBuffer) {
+            stats.twoBufferTime += segment.totalTime;
+        }
+        if (segment.occupancyAverage < 1.0f) {
+            stats.doubleBufferedTime += segment.totalTime;
+        } else if (segment.occupancyAverage < 2.0f) {
+            stats.tripleBufferedTime += segment.totalTime;
+        }
+        ++stats.numSegments;
+        stats.totalTime += segment.totalTime;
+    }
+}
+
+void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+    result.append("Buffering stats:\n");
+    result.append("  [Layer name] <Active time> <Two buffer> "
+            "<Double buffered> <Triple buffered>\n");
+    Mutex::Autolock lock(mBufferingStatsMutex);
+    typedef std::tuple<std::string, float, float, float> BufferTuple;
+    std::map<float, BufferTuple, std::greater<float>> sorted;
+    for (const auto& statsPair : mBufferingStats) {
+        const char* name = statsPair.first.c_str();
+        const BufferingStats& stats = statsPair.second;
+        if (stats.numSegments == 0) {
+            continue;
+        }
+        float activeTime = ns2ms(stats.totalTime) / 1000.0f;
+        float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
+                stats.totalTime;
+        float doubleBufferRatio = static_cast<float>(
+                stats.doubleBufferedTime) / stats.totalTime;
+        float tripleBufferRatio = static_cast<float>(
+                stats.tripleBufferedTime) / stats.totalTime;
+        sorted.insert({activeTime, {name, twoBufferRatio,
+                doubleBufferRatio, tripleBufferRatio}});
+    }
+    for (const auto& sortedPair : sorted) {
+        float activeTime = sortedPair.first;
+        const BufferTuple& values = sortedPair.second;
+        result.appendFormat("  [%s] %.2f %.3f %.3f %.3f\n",
+                std::get<0>(values).c_str(), activeTime,
+                std::get<1>(values), std::get<2>(values),
+                std::get<3>(values));
+    }
+    result.append("\n");
+}
+
+
 void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
         String8& result) const
 {
@@ -2813,6 +2984,8 @@
     dumpStaticScreenStats(result);
     result.append("\n");
 
+    dumpBufferingStats(result);
+
     /*
      * Dump the visible layer list
      */
@@ -2888,6 +3061,26 @@
      * VSYNC state
      */
     mEventThread->dump(result);
+    result.append("\n");
+
+    /*
+     * HWC layer minidump
+     */
+    for (size_t d = 0; d < mDisplays.size(); d++) {
+        const sp<const DisplayDevice>& displayDevice(mDisplays[d]);
+        int32_t hwcId = displayDevice->getHwcDisplayId();
+        if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) {
+            continue;
+        }
+
+        result.appendFormat("Display %d HWC layers:\n", hwcId);
+        Layer::miniDumpHeader(result);
+        for (size_t l = 0; l < count; l++) {
+            const sp<Layer>& layer(currentLayers[l]);
+            layer->miniDump(result, hwcId);
+        }
+        result.append("\n");
+    }
 
     /*
      * Dump HWComposer state
@@ -2895,8 +3088,7 @@
     colorizer.bold(result);
     result.append("h/w composer state:\n");
     colorizer.reset(result);
-    bool hwcDisabled = mDebugDisableHWC || mDebugRegion || mDaltonize ||
-            mHasColorMatrix;
+    bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
     result.appendFormat("  h/w composer %s\n",
             hwcDisabled ? "disabled" : "enabled");
     hwc.dump(result);
@@ -3051,16 +3243,24 @@
                 // daltonize
                 n = data.readInt32();
                 switch (n % 10) {
-                    case 1: mDaltonizer.setType(Daltonizer::protanomaly);   break;
-                    case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
-                    case 3: mDaltonizer.setType(Daltonizer::tritanomaly);   break;
+                    case 1:
+                        mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+                        break;
+                    case 2:
+                        mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
+                        break;
+                    case 3:
+                        mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
+                        break;
+                    default:
+                        mDaltonizer.setType(ColorBlindnessType::None);
+                        break;
                 }
                 if (n >= 10) {
-                    mDaltonizer.setMode(Daltonizer::correction);
+                    mDaltonizer.setMode(ColorBlindnessMode::Correction);
                 } else {
-                    mDaltonizer.setMode(Daltonizer::simulation);
+                    mDaltonizer.setMode(ColorBlindnessMode::Simulation);
                 }
-                mDaltonize = n > 0;
                 invalidateHwcGeometry();
                 repaintEverything();
                 return NO_ERROR;
@@ -3068,15 +3268,14 @@
             case 1015: {
                 // apply a color matrix
                 n = data.readInt32();
-                mHasColorMatrix = n ? 1 : 0;
                 if (n) {
                     // color matrix is sent as mat3 matrix followed by vec3
                     // offset, then packed into a mat4 where the last row is
                     // the offset and extra values are 0
                     for (size_t i = 0 ; i < 4; i++) {
-                      for (size_t j = 0; j < 4; j++) {
-                          mColorMatrix[i][j] = data.readFloat();
-                      }
+                        for (size_t j = 0; j < 4; j++) {
+                            mColorMatrix[i][j] = data.readFloat();
+                        }
                     }
                 } else {
                     mColorMatrix = mat4();
@@ -3107,6 +3306,11 @@
                 mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
                 return NO_ERROR;
             }
+            case 1021: { // Disable HWC virtual displays
+                n = data.readInt32();
+                mUseHwcVirtualDisplays = !n;
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -3449,6 +3653,14 @@
     // create a surface (because we're a producer, and we need to
     // dequeue/queue a buffer)
     sp<Surface> sur = new Surface(producer, false);
+
+    // Put the screenshot Surface into async mode so that
+    // Layer::headFenceHasSignaled will always return true and we'll latch the
+    // first buffer regardless of whether or not its acquire fence has
+    // signaled. This is needed to avoid a race condition in the rotation
+    // animation. See b/30209608
+    sur->setAsyncMode(true);
+
     ANativeWindow* window = sur.get();
 
     status_t result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
@@ -3585,6 +3797,11 @@
     }
 }
 
+bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
+        uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+    return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
+}
+
 // ---------------------------------------------------------------------------
 
 SurfaceFlinger::LayerVector::LayerVector() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8279a85..b98924b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -42,9 +42,12 @@
 
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
+#include <gui/OccupancyTracker.h>
 
 #include <hardware/hwcomposer_defs.h>
 
+#include <system/graphics.h>
+
 #include <private/gui/LayerState.h>
 
 #include "Barrier.h"
@@ -57,6 +60,9 @@
 #include "DisplayHardware/HWComposer.h"
 #include "Effects/Daltonizer.h"
 
+#include <map>
+#include <string>
+
 namespace android {
 
 // ---------------------------------------------------------------------------
@@ -218,6 +224,10 @@
     virtual status_t getDisplayConfigs(const sp<IBinder>& display,
             Vector<DisplayInfo>* configs);
     virtual int getActiveConfig(const sp<IBinder>& display);
+    virtual status_t getDisplayColorModes(const sp<IBinder>& display,
+            Vector<android_color_mode_t>* configs);
+    virtual android_color_mode_t getActiveColorMode(const sp<IBinder>& display);
+    virtual status_t setActiveColorMode(const sp<IBinder>& display, android_color_mode_t colorMode);
     virtual void setPowerMode(const sp<IBinder>& display, int mode);
     virtual status_t setActiveConfig(const sp<IBinder>& display, int id);
     virtual status_t clearAnimationFrameStats();
@@ -256,6 +266,9 @@
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode);
 
+    // Called on the main thread in response to setActiveColorMode()
+    void setActiveColorModeInternal(const sp<DisplayDevice>& hw, android_color_mode_t colorMode);
+
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
 
@@ -364,6 +377,16 @@
         return mDisplays.valueFor(dpy);
     }
 
+    int32_t getDisplayType(const sp<IBinder>& display) {
+        if (!display.get()) return NAME_NOT_FOUND;
+        for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
+            if (display == mBuiltinDisplays[i]) {
+                return i;
+            }
+        }
+        return NAME_NOT_FOUND;
+    }
+
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(uint32_t layerStack, const Region& dirty);
@@ -432,6 +455,13 @@
 
     void dumpStaticScreenStats(String8& result) const;
 
+    void recordBufferingStats(const char* layerName,
+            std::vector<OccupancyTracker::Segment>&& history);
+    void dumpBufferingStats(String8& result) const;
+
+    bool getFrameTimestamps(const Layer& layer, uint64_t frameNumber,
+            FrameTimestamps* outTimestamps);
+
     /* ------------------------------------------------------------------------
      * Attributes
      */
@@ -457,7 +487,6 @@
     RenderEngine* mRenderEngine;
     nsecs_t mBootTime;
     bool mGpuToCpuSupported;
-    bool mDropMissedFrames;
     sp<EventThread> mEventThread;
     sp<EventThread> mSFEventThread;
     sp<EventControlThread> mEventControlThread;
@@ -477,6 +506,8 @@
     bool mAnimCompositionPending;
 #ifdef USE_HWC2
     std::vector<sp<Layer>> mLayersWithQueuedFrames;
+    sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+    bool mHadClientComposition = false;
 #endif
 
     // this may only be written from the main thread with mStateLock held
@@ -495,6 +526,10 @@
     bool mBootFinished;
     bool mForceFullDamage;
     FenceTracker mFenceTracker;
+#ifdef USE_HWC2
+    bool mPropagateBackpressure = true;
+#endif
+    bool mUseHwcVirtualDisplays = true;
 
     // these are thread safe
     mutable MessageQueue mEventQueue;
@@ -515,8 +550,11 @@
      */
 
     Daltonizer mDaltonizer;
+#ifndef USE_HWC2
     bool mDaltonize;
+#endif
 
+    mat4 mPreviousColorMatrix;
     mat4 mColorMatrix;
     bool mHasColorMatrix;
 
@@ -526,6 +564,29 @@
     nsecs_t mFrameBuckets[NUM_BUCKETS];
     nsecs_t mTotalTime;
     std::atomic<nsecs_t> mLastSwapTime;
+
+    // Double- vs. triple-buffering stats
+    struct BufferingStats {
+        BufferingStats()
+          : numSegments(0),
+            totalTime(0),
+            twoBufferTime(0),
+            doubleBufferedTime(0),
+            tripleBufferedTime(0) {}
+
+        size_t numSegments;
+        nsecs_t totalTime;
+
+        // "Two buffer" means that a third buffer was never used, whereas
+        // "double-buffered" means that on average the segment only used two
+        // buffers (though it may have used a third for some part of the
+        // segment)
+        nsecs_t twoBufferTime;
+        nsecs_t doubleBufferedTime;
+        nsecs_t tripleBufferedTime;
+    };
+    mutable Mutex mBufferingStatsMutex;
+    std::unordered_map<std::string, BufferingStats> mBufferingStats;
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index edd53a3..6f2520b 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 
 #include "SurfaceFlingerConsumer.h"
+#include "Layer.h"
 
 #include <private/gui/SyncFeatures.h>
 
@@ -128,6 +129,7 @@
 }
 
 bool SurfaceFlingerConsumer::getTransformToDisplayInverse() const {
+    Mutex::Autolock lock(mMutex);
     return mTransformToDisplayInverse;
 }
 
@@ -251,6 +253,12 @@
     }
 }
 
+bool SurfaceFlingerConsumer::getFrameTimestamps(uint64_t frameNumber,
+        FrameTimestamps* outTimestamps) const {
+    sp<const Layer> l = mLayer.promote();
+    return l.get() ? l->getFrameTimestamps(frameNumber, outTimestamps) : false;
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 51b002f..4271039 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -23,6 +23,8 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
+class Layer;
+
 /*
  * This is a thin wrapper around GLConsumer.
  */
@@ -35,10 +37,10 @@
     };
 
     SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
-            uint32_t tex)
+            uint32_t tex, const Layer* layer)
         : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
           mTransformToDisplayInverse(false), mSurfaceDamage(),
-          mPrevReleaseFence(Fence::NO_FENCE)
+          mPrevReleaseFence(Fence::NO_FENCE), mLayer(layer)
     {}
 
     class BufferRejecter {
@@ -64,8 +66,9 @@
     // See GLConsumer::bindTextureImageLocked().
     status_t bindTextureImage();
 
-    // must be called from SF main thread
     bool getTransformToDisplayInverse() const;
+
+    // must be called from SF main thread
     const Region& getSurfaceDamage() const;
 
     // Sets the contents changed listener. This should be used instead of
@@ -82,6 +85,9 @@
     void releasePendingBuffer();
 #endif
 
+    virtual bool getFrameTimestamps(uint64_t frameNumber,
+            FrameTimestamps* outTimestamps) const override;
+
 private:
     virtual void onSidebandStreamChanged();
 
@@ -103,6 +109,9 @@
 
     // The release fence of the already displayed buffer (previous frame).
     sp<Fence> mPrevReleaseFence;
+
+    // The layer for this SurfaceFlingerConsumer
+    wp<const Layer> mLayer;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 072de81..6710bca 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -16,18 +16,18 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <dlfcn.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdatomic.h>
 #include <stdint.h>
 #include <sys/types.h>
-#include <errno.h>
-#include <math.h>
-#include <dlfcn.h>
-#include <inttypes.h>
-#include <stdatomic.h>
 
 #include <EGL/egl.h>
 
-#include <cutils/log.h>
 #include <cutils/properties.h>
+#include <log/log.h>
 
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -59,6 +59,8 @@
 #include <private/android_filesystem_config.h>
 #include <private/gui/SyncFeatures.h>
 
+#include <set>
+
 #include "Client.h"
 #include "clz.h"
 #include "Colorizer.h"
@@ -166,9 +168,6 @@
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
     mGpuToCpuSupported = !atoi(value);
 
-    property_get("debug.sf.drop_missed_frames", value, "0");
-    mDropMissedFrames = atoi(value);
-
     property_get("debug.sf.showupdates", value, "0");
     mDebugRegion = atoi(value);
 
@@ -182,6 +181,10 @@
     }
     ALOGI_IF(mDebugRegion, "showupdates enabled");
     ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
+
+    property_get("debug.sf.disable_hwc_vds", value, "0");
+    mUseHwcVirtualDisplays = !atoi(value);
+    ALOGI_IF(!mUseHwcVirtualDisplays, "Disabling HWC virtual displays");
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -462,6 +465,14 @@
     mSFEventThread = new EventThread(sfVsyncSrc, *this);
     mEventQueue.setEventThread(mSFEventThread);
 
+    // set SFEventThread to SCHED_FIFO to minimize jitter
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO for SFEventThread");
+    }
+
+
     // Initialize the H/W composer object.  There may or may not be an
     // actual hardware composer underneath.
     mHwc = new HWComposer(this,
@@ -527,6 +538,8 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
+    mRenderEngine->primeCache();
+
     // start boot animation
     startBootAnim();
 }
@@ -565,20 +578,8 @@
         return BAD_VALUE;
     }
 
-    if (!display.get())
-        return NAME_NOT_FOUND;
-
-    int32_t type = NAME_NOT_FOUND;
-    for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
-        if (display == mBuiltinDisplays[i]) {
-            type = i;
-            break;
-        }
-    }
-
-    if (type < 0) {
-        return type;
-    }
+    int32_t type = getDisplayType(display);
+    if (type < 0) return type;
 
     // TODO: Not sure if display density should handled by SF any longer
     class Density {
@@ -640,7 +641,6 @@
         info.ydpi = ydpi;
         info.fps = float(1e9 / hwConfig.refresh);
         info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
-        info.colorTransform = hwConfig.colorTransform;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -741,6 +741,55 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display,
+        Vector<android_color_mode_t>* outColorModes) {
+    if (outColorModes == nullptr || display.get() == nullptr) {
+        return BAD_VALUE;
+    }
+
+    int32_t type = getDisplayType(display);
+    if (type < 0) return type;
+
+    std::set<android_color_mode_t> colorModes;
+    for (const HWComposer::DisplayConfig& hwConfig : getHwComposer().getConfigs(type)) {
+        colorModes.insert(hwConfig.colorMode);
+    }
+
+    outColorModes->clear();
+    std::copy(colorModes.cbegin(), colorModes.cend(), std::back_inserter(*outColorModes));
+
+    return NO_ERROR;
+}
+
+android_color_mode_t SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) {
+    if (display.get() == nullptr) return static_cast<android_color_mode_t>(BAD_VALUE);
+
+    int32_t type = getDisplayType(display);
+    if (type < 0) return static_cast<android_color_mode_t>(type);
+
+    return getHwComposer().getColorMode(type);
+}
+
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display,
+        android_color_mode_t colorMode) {
+    if (display.get() == nullptr || colorMode < 0) {
+        return BAD_VALUE;
+    }
+
+    int32_t type = getDisplayType(display);
+    if (type < 0) return type;
+    const Vector<HWComposer::DisplayConfig>& hwConfigs = getHwComposer().getConfigs(type);
+    HWComposer::DisplayConfig desiredConfig = hwConfigs[getHwComposer().getCurrentConfig(type)];
+    desiredConfig.colorMode = colorMode;
+    for (size_t c = 0; c < hwConfigs.size(); ++c) {
+        const HWComposer::DisplayConfig config = hwConfigs[c];
+        if (config == desiredConfig) {
+            return setActiveConfig(display, c);
+        }
+    }
+    return BAD_VALUE;
+}
+
 status_t SurfaceFlinger::clearAnimationFrameStats() {
     Mutex::Autolock _l(mStateLock);
     mAnimFrameTracker.clearStats();
@@ -943,35 +992,14 @@
 void SurfaceFlinger::handleMessageRefresh() {
     ATRACE_CALL();
 
-#ifdef ENABLE_FENCE_TRACKING
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#else
-    nsecs_t refreshStartTime = 0;
-#endif
-    static nsecs_t previousExpectedPresent = 0;
-    nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
-    static bool previousFrameMissed = false;
-    bool frameMissed = (expectedPresent == previousExpectedPresent);
-    if (frameMissed != previousFrameMissed) {
-        ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
-    }
-    previousFrameMissed = frameMissed;
 
-    if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
-        // Latch buffers, but don't send anything to HWC, then signal another
-        // wakeup for the next vsync
-        preComposition();
-        repaintEverything();
-    } else {
-        preComposition();
-        rebuildLayerStacks();
-        setUpHWComposer();
-        doDebugFlashRegions();
-        doComposition();
-        postComposition(refreshStartTime);
-    }
-
-    previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0);
+    preComposition();
+    rebuildLayerStacks();
+    setUpHWComposer();
+    doDebugFlashRegions();
+    doComposition();
+    postComposition(refreshStartTime);
 }
 
 void SurfaceFlinger::doDebugFlashRegions()
@@ -1029,16 +1057,16 @@
     }
 }
 
-#ifdef ENABLE_FENCE_TRACKING
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
 {
     const LayerVector& layers(mDrawingState.layersSortedByZ);
     const size_t count = layers.size();
     for (size_t i=0 ; i<count ; i++) {
-        layers[i]->onPostComposition();
+        bool frameLatched = layers[i]->onPostComposition();
+        if (frameLatched) {
+            recordBufferingStats(layers[i]->getName().string(),
+                    layers[i]->getOccupancyHistory(false));
+        }
     }
 
     const HWComposer& hwc = getHwComposer();
@@ -1059,10 +1087,8 @@
         }
     }
 
-#ifdef ENABLE_FENCE_TRACKING
     mFenceTracker.addFrame(refreshStartTime, presentFence,
             hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
 
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
@@ -1486,9 +1512,10 @@
                                     NATIVE_WINDOW_HEIGHT, &height);
                             ALOGE_IF(status != NO_ERROR,
                                     "Unable to query height (%d)", status);
-                            if (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 ||
+                            if (mUseHwcVirtualDisplays &&
+                                    (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 ||
                                     (width <= MAX_VIRTUAL_DISPLAY_DIMENSION &&
-                                     height <= MAX_VIRTUAL_DISPLAY_DIMENSION)) {
+                                     height <= MAX_VIRTUAL_DISPLAY_DIMENSION))) {
                                 hwcDisplayId = allocateHwcDisplayId(state.type);
                             }
 
@@ -1666,6 +1693,8 @@
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
+            recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
+                    mLayersPendingRemoval[i]->getOccupancyHistory(true));
             mLayersPendingRemoval[i]->onRemoved();
         }
         mLayersPendingRemoval.clear();
@@ -2273,10 +2302,10 @@
     sp<Layer> layer(client->getLayerUser(s.surface));
     if (layer != 0) {
         const uint32_t what = s.what;
-        bool positionAppliesWithResize =
-                what & layer_state_t::ePositionAppliesWithResize;
+        bool geometryAppliesWithResize =
+                what & layer_state_t::eGeometryAppliesWithResize;
         if (what & layer_state_t::ePositionChanged) {
-            if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
+            if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
                 flags |= eTraversalNeeded;
             }
         }
@@ -2313,7 +2342,7 @@
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eCropChanged) {
-            if (layer->setCrop(s.crop))
+            if (layer->setCrop(s.crop, !geometryAppliesWithResize))
                 flags |= eTraversalNeeded;
         }
         if (what & layer_state_t::eFinalCropChanged) {
@@ -2505,6 +2534,7 @@
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
+        // Turn on the display
         getHwComposer().setPowerMode(type, mode);
         if (type == DisplayDevice::DISPLAY_PRIMARY) {
             // FIXME: eventthread only knows about the main display right now
@@ -2515,7 +2545,19 @@
         mVisibleRegionsDirty = true;
         mHasPoweredOff = true;
         repaintEverything();
+
+        struct sched_param param = {0};
+        param.sched_priority = 1;
+        if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
+            ALOGW("Couldn't set SCHED_FIFO on display on");
+        }
     } else if (mode == HWC_POWER_MODE_OFF) {
+        // Turn off the display
+        struct sched_param param = {0};
+        if (sched_setscheduler(0, SCHED_OTHER, &param) != 0) {
+            ALOGW("Couldn't set SCHED_OTHER on display off");
+        }
+
         if (type == DisplayDevice::DISPLAY_PRIMARY) {
             disableHardwareVsync(true); // also cancels any in-progress resync
 
@@ -2622,14 +2664,12 @@
                 dumpAll = false;
             }
 
-#ifdef ENABLE_FENCE_TRACKING
             if ((index < numArgs) &&
                     (args[index] == String16("--fences"))) {
                 index++;
                 mFenceTracker.dump(&result);
                 dumpAll = false;
             }
-#endif
         }
 
         if (dumpAll) {
@@ -2750,6 +2790,58 @@
             NUM_BUCKETS - 1, bucketTimeSec, percent);
 }
 
+void SurfaceFlinger::recordBufferingStats(const char* layerName,
+        std::vector<OccupancyTracker::Segment>&& history) {
+    Mutex::Autolock lock(mBufferingStatsMutex);
+    auto& stats = mBufferingStats[layerName];
+    for (const auto& segment : history) {
+        if (!segment.usedThirdBuffer) {
+            stats.twoBufferTime += segment.totalTime;
+        }
+        if (segment.occupancyAverage < 1.0f) {
+            stats.doubleBufferedTime += segment.totalTime;
+        } else if (segment.occupancyAverage < 2.0f) {
+            stats.tripleBufferedTime += segment.totalTime;
+        }
+        ++stats.numSegments;
+        stats.totalTime += segment.totalTime;
+    }
+}
+
+void SurfaceFlinger::dumpBufferingStats(String8& result) const {
+    result.append("Buffering stats:\n");
+    result.append("  [Layer name] <Active time> <Two buffer> "
+            "<Double buffered> <Triple buffered>\n");
+    Mutex::Autolock lock(mBufferingStatsMutex);
+    typedef std::tuple<std::string, float, float, float> BufferTuple;
+    std::map<float, BufferTuple, std::greater<float>> sorted;
+    for (const auto& statsPair : mBufferingStats) {
+        const char* name = statsPair.first.c_str();
+        const BufferingStats& stats = statsPair.second;
+        if (stats.numSegments == 0) {
+            continue;
+        }
+        float activeTime = ns2ms(stats.totalTime) / 1000.0f;
+        float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
+                stats.totalTime;
+        float doubleBufferRatio = static_cast<float>(
+                stats.doubleBufferedTime) / stats.totalTime;
+        float tripleBufferRatio = static_cast<float>(
+                stats.tripleBufferedTime) / stats.totalTime;
+        sorted.insert({activeTime, {name, twoBufferRatio,
+                doubleBufferRatio, tripleBufferRatio}});
+    }
+    for (const auto& sortedPair : sorted) {
+        float activeTime = sortedPair.first;
+        const BufferTuple& values = sortedPair.second;
+        result.appendFormat("  [%s] %.2f %.3f %.3f %.3f\n",
+                std::get<0>(values).c_str(), activeTime,
+                std::get<1>(values), std::get<2>(values),
+                std::get<3>(values));
+    }
+    result.append("\n");
+}
+
 void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
         String8& result) const
 {
@@ -2801,6 +2893,8 @@
     dumpStaticScreenStats(result);
     result.append("\n");
 
+    dumpBufferingStats(result);
+
     /*
      * Dump the visible layer list
      */
@@ -3039,14 +3133,20 @@
                 // daltonize
                 n = data.readInt32();
                 switch (n % 10) {
-                    case 1: mDaltonizer.setType(Daltonizer::protanomaly);   break;
-                    case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
-                    case 3: mDaltonizer.setType(Daltonizer::tritanomaly);   break;
+                    case 1:
+                        mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+                        break;
+                    case 2:
+                        mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
+                        break;
+                    case 3:
+                        mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
+                        break;
                 }
                 if (n >= 10) {
-                    mDaltonizer.setMode(Daltonizer::correction);
+                    mDaltonizer.setMode(ColorBlindnessMode::Correction);
                 } else {
-                    mDaltonizer.setMode(Daltonizer::simulation);
+                    mDaltonizer.setMode(ColorBlindnessMode::Simulation);
                 }
                 mDaltonize = n > 0;
                 invalidateHwcGeometry();
@@ -3095,6 +3195,11 @@
                 mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
                 return NO_ERROR;
             }
+            case 1021: { // Disable HWC virtual displays
+                n = data.readInt32();
+                mUseHwcVirtualDisplays = !n;
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -3547,6 +3652,11 @@
     return result;
 }
 
+bool SurfaceFlinger::getFrameTimestamps(const Layer& layer,
+        uint64_t frameNumber, FrameTimestamps* outTimestamps) {
+    return mFenceTracker.getFrameTimestamps(layer, frameNumber, outTimestamps);
+}
+
 void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr,
         const sp<const DisplayDevice>& hw, uint32_t minLayerZ, uint32_t maxLayerZ) {
     if (DEBUG_SCREENSHOTS) {
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
index c2be91d..6be9ae2 100644
--- a/services/surfaceflinger/Transform.cpp
+++ b/services/surfaceflinger/Transform.cpp
@@ -196,7 +196,7 @@
     return transform( Rect(w, h) );
 }
 
-Rect Transform::transform(const Rect& bounds) const
+Rect Transform::transform(const Rect& bounds, bool roundOutwards) const
 {
     Rect r;
     vec2 lt( bounds.left,  bounds.top    );
@@ -209,10 +209,17 @@
     lb = transform(lb);
     rb = transform(rb);
 
-    r.left   = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
-    r.top    = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
-    r.right  = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
-    r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+    if (roundOutwards) {
+        r.left   = floorf(min(lt[0], rt[0], lb[0], rb[0]));
+        r.top    = floorf(min(lt[1], rt[1], lb[1], rb[1]));
+        r.right  = ceilf(max(lt[0], rt[0], lb[0], rb[0]));
+        r.bottom = ceilf(max(lt[1], rt[1], lb[1], rb[1]));
+    } else {
+        r.left   = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
+        r.top    = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+        r.right  = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
+        r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+    }
 
     return r;
 }
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index 90855da..66463a0 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -78,7 +78,8 @@
             Rect    makeBounds(int w, int h) const;
             vec2    transform(int x, int y) const;
             Region  transform(const Region& reg) const;
-            Rect    transform(const Rect& bounds) const;
+            Rect    transform(const Rect& bounds,
+                    bool roundOutwards = false) const;
             Transform operator * (const Transform& rhs) const;
             // assumes the last row is < 0 , 0 , 1 >
             vec2 transform(const vec2& v) const;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 97a1e8b..53a63bd 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -16,6 +16,8 @@
 
 #include <sys/resource.h>
 
+#include <sched.h>
+
 #include <cutils/sched_policy.h>
 #include <binder/IServiceManager.h>
 #include <binder/IPCThreadState.h>
@@ -61,6 +63,12 @@
     sp<GpuService> gpuservice = new GpuService();
     sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);
 
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO");
+    }
+
     // run surface flinger in this thread
     flinger->run();
 
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index 2b4ea2a..435aa0c 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -3,4 +3,4 @@
     user system
     group graphics drmrpc readproc
     onrestart restart zygote
-    writepid /sys/fs/cgroup/stune/foreground/tasks
+    writepid /dev/stune/foreground/tasks
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index d97cf5e..ba3cf79 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -20,6 +20,13 @@
     license: "include/vulkan/NOTICE",
 }
 
+cc_library_static {
+    name: "vulkan_headers",
+    export_include_dirs: ["include"],
+}
+
 subdirs = [
+    "nulldrv",
     "libvulkan",
+    "tools",
 ]
diff --git a/vulkan/Android.mk b/vulkan/Android.mk
deleted file mode 100644
index d125673..0000000
--- a/vulkan/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-named-subdir-makefiles, libvulkan nulldrv tools)
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5e3f4dd..147cc56 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -17,4 +17,64 @@
     name: "libvulkan.ndk",
     symbol_file: "libvulkan.map.txt",
     first_version: "24",
+    unversioned_until: "current",
+}
+
+cc_library_shared {
+    name: "libvulkan",
+    clang: true,
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+
+    cflags: [
+        "-DLOG_TAG=\"vulkan\"",
+        "-DVK_USE_PLATFORM_ANDROID_KHR",
+        "-DVK_NO_PROTOTYPES",
+        "-fvisibility=hidden",
+        "-fstrict-aliasing",
+        "-Weverything",
+        "-Werror",
+        "-Wno-padded",
+        "-Wno-switch-enum",
+        "-Wno-undef",
+
+        //"-DLOG_NDEBUG=0",
+    ],
+
+    cppflags: [
+        "-std=c++14",
+        "-Wno-c99-extensions",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-exit-time-destructors",
+        "-Wno-global-constructors",
+        "-Wno-zero-length-array",
+    ],
+
+    srcs: [
+        "api.cpp",
+        "api_gen.cpp",
+        "debug_report.cpp",
+        "driver.cpp",
+        "driver_gen.cpp",
+        "layers_extensions.cpp",
+        "stubhal.cpp",
+        "swapchain.cpp",
+        "vulkan_loader_data.cpp",
+    ],
+
+    export_static_lib_headers: ["vulkan_headers"],
+    static_libs: [
+        "vulkan_headers",
+        "libziparchive",
+    ],
+    shared_libs: [
+        "libhardware",
+        "libsync",
+        "libbase",
+        "liblog",
+        "libutils",
+        "libcutils",
+        "libz",
+    ],
 }
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
deleted file mode 100644
index f1155ca..0000000
--- a/vulkan/libvulkan/Android.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-LOCAL_CFLAGS := -DLOG_TAG=\"vulkan\" \
-	-DVK_USE_PLATFORM_ANDROID_KHR \
-	-DVK_NO_PROTOTYPES \
-	-std=c99 -fvisibility=hidden -fstrict-aliasing \
-	-Weverything -Werror \
-	-Wno-padded \
-	-Wno-switch-enum \
-	-Wno-undef
-
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-LOCAL_CPPFLAGS := -std=c++14 \
-	-Wno-c99-extensions \
-	-Wno-c++98-compat-pedantic \
-	-Wno-exit-time-destructors \
-	-Wno-global-constructors \
-	-Wno-zero-length-array
-
-LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include \
-	system/core/libsync/include
-
-LOCAL_SRC_FILES := \
-	api.cpp \
-	api_gen.cpp \
-	debug_report.cpp \
-	driver.cpp \
-	driver_gen.cpp \
-	layers_extensions.cpp \
-	stubhal.cpp \
-	swapchain.cpp \
-	vulkan_loader_data.cpp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_STATIC_LIBRARIES := libziparchive
-LOCAL_SHARED_LIBRARIES := libhardware libsync libbase liblog libutils libcutils libz
-
-LOCAL_MODULE := libvulkan
-include $(BUILD_SHARED_LIBRARY)
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index b699fe9..36755a2 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -23,10 +23,12 @@
 
 #include <stdlib.h>
 #include <string.h>
+
 #include <algorithm>
 #include <mutex>
 #include <new>
 #include <utility>
+
 #include <cutils/properties.h>
 #include <log/log.h>
 
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 0a1dda2..b72aa0a 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -17,7 +17,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.
 
 #include <string.h>
+
 #include <algorithm>
+
 #include <log/log.h>
 
 // to catch mismatches between vulkan.h and this file
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index f9a4670..46333ec 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -91,7 +91,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.

 #include <string.h>

 #include <algorithm>

 #include <log/log.h>

 // to catch mismatches between vulkan.h and this file
@@ -270,7 +272,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.

 #include <string.h>

 #include <algorithm>

 #include <log/log.h>

 #include "driver.h"
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 2555272..56396f4 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
+#include <malloc.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/prctl.h>
+
 #include <algorithm>
 #include <array>
 #include <new>
-#include <malloc.h>
-#include <sys/prctl.h>
+
+#include <log/log.h>
 
 #include "driver.h"
 #include "stubhal.h"
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index a1612c7..e058439 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -18,8 +18,10 @@
 #define LIBVULKAN_DRIVER_H 1
 
 #include <inttypes.h>
+
 #include <bitset>
 #include <type_traits>
+
 #include <log/log.h>
 
 #include <vulkan/vulkan.h>
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index d979a34..8cbd398 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -17,7 +17,9 @@
 // WARNING: This file is generated. See ../README.md for instructions.
 
 #include <string.h>
+
 #include <algorithm>
+
 #include <log/log.h>
 
 #include "driver.h"
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 82169ff..6e44126 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -19,14 +19,15 @@
 #include <alloca.h>
 #include <dirent.h>
 #include <dlfcn.h>
-#include <mutex>
-#include <sys/prctl.h>
-#include <string>
 #include <string.h>
+#include <sys/prctl.h>
+
+#include <mutex>
+#include <string>
 #include <vector>
 
-#include <android-base/strings.h>
 #include <android/dlext.h>
+#include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <log/log.h>
 #include <ziparchive/zip_archive.h>
diff --git a/vulkan/libvulkan/stubhal.cpp b/vulkan/libvulkan/stubhal.cpp
index 869317b..2926268 100644
--- a/vulkan/libvulkan/stubhal.cpp
+++ b/vulkan/libvulkan/stubhal.cpp
@@ -29,8 +29,10 @@
 #include <array>
 #include <bitset>
 #include <mutex>
-#include <hardware/hwvulkan.h>
+
 #include <log/log.h>
+#include <hardware/hwvulkan.h>
+
 #include "stubhal.h"
 
 namespace vulkan {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 63c597c..bfe7aa7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,8 +16,8 @@
 
 #include <algorithm>
 
-#include <gui/BufferQueue.h>
 #include <log/log.h>
+#include <gui/BufferQueue.h>
 #include <sync/sync.h>
 #include <utils/StrongPointer.h>
 
@@ -723,6 +723,8 @@
                          const VkAllocationCallbacks* allocator) {
     const auto& dispatch = GetData(device).driver;
     Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
+    if (!swapchain)
+        return;
     bool active = swapchain->surface.swapchain_handle == swapchain_handle;
     ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
 
diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp
new file mode 100644
index 0000000..ea3b781
--- /dev/null
+++ b/vulkan/nulldrv/Android.bp
@@ -0,0 +1,47 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_shared {
+    // Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM)
+    name: "vulkan.default",
+    proprietary: true,
+    relative_install_path: "hw",
+
+    clang: true,
+    cflags: [
+        "-fvisibility=hidden",
+        "-fstrict-aliasing",
+        "-DLOG_TAG=\"vknulldrv\"",
+        "-Weverything",
+        "-Werror",
+        "-Wno-padded",
+        "-Wno-undef",
+        "-Wno-zero-length-array",
+
+        "-DLOG_NDEBUG=0",
+    ],
+    cppflags: [
+        "-std=c++1y",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-c99-extensions",
+    ],
+
+    srcs: [
+        "null_driver.cpp",
+        "null_driver_gen.cpp",
+    ],
+
+    static_libs: ["vulkan_headers"],
+    shared_libs: ["liblog"],
+}
diff --git a/vulkan/nulldrv/Android.mk b/vulkan/nulldrv/Android.mk
deleted file mode 100644
index 77d4746..0000000
--- a/vulkan/nulldrv/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing \
-	-DLOG_TAG=\"vknulldrv\" \
-	-Weverything -Werror \
-	-Wno-padded \
-	-Wno-undef \
-	-Wno-zero-length-array
-#LOCAL_CFLAGS += -DLOG_NDEBUG=0
-LOCAL_CPPFLAGS := -std=c++1y \
-	-Wno-c++98-compat-pedantic \
-	-Wno-c99-extensions
-
-LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include
-
-LOCAL_SRC_FILES := \
-	null_driver.cpp \
-	null_driver_gen.cpp
-
-LOCAL_SHARED_LIBRARIES := liblog
-
-# Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM)
-LOCAL_MODULE := vulkan.default
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3bf3ff7..4ac994b 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -16,12 +16,13 @@
 
 #include <hardware/hwvulkan.h>
 
-#include <algorithm>
-#include <array>
 #include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <algorithm>
+#include <array>
+
 #include <log/log.h>
 #include <utils/Errors.h>
 
diff --git a/vulkan/tools/Android.bp b/vulkan/tools/Android.bp
new file mode 100644
index 0000000..d81d9ec
--- /dev/null
+++ b/vulkan/tools/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "vkinfo",
+
+    clang: true,
+    cflags: [
+        "-fvisibility=hidden",
+        "-fstrict-aliasing",
+
+        "-DLOG_TAG=\"vkinfo\"",
+
+        "-Weverything",
+        "-Werror",
+        "-Wno-padded",
+        "-Wno-undef",
+        "-Wno-switch-enum",
+    ],
+    cppflags: [
+        "-std=c++1y",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-c99-extensions",
+        "-Wno-old-style-cast",
+    ],
+
+    srcs: ["vkinfo.cpp"],
+
+    shared_libs: [
+        "libvulkan",
+        "liblog",
+    ],
+}
diff --git a/vulkan/tools/Android.mk b/vulkan/tools/Android.mk
deleted file mode 100644
index 337e683..0000000
--- a/vulkan/tools/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -std=c99 -fvisibility=hidden -fstrict-aliasing
-LOCAL_CFLAGS += -DLOG_TAG=\"vkinfo\"
-LOCAL_CFLAGS += -Weverything -Werror -Wno-padded -Wno-undef -Wno-switch-enum
-LOCAL_CPPFLAGS := -std=c++1y \
-	-Wno-c++98-compat-pedantic \
-	-Wno-c99-extensions \
-	-Wno-old-style-cast
-
-LOCAL_C_INCLUDES := \
-	frameworks/native/vulkan/include
-
-LOCAL_SRC_FILES := vkinfo.cpp
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_SHARED_LIBRARIES := libvulkan liblog
-
-LOCAL_MODULE := vkinfo
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_EXECUTABLE)
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 7cf85e6..801eca8 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -14,18 +14,17 @@
  * limitations under the License.
  */
 
-#include <algorithm>
-#include <array>
 #include <inttypes.h>
 #include <stdlib.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
 #include <sstream>
 #include <vector>
 
 #include <vulkan/vulkan.h>
 
-#define LOG_TAG "vkinfo"
-#include <log/log.h>
-
 namespace {
 
 struct Options {