Merge "Revert "Delete HWC1""
diff --git a/Android.bp b/Android.bp
index f5620f8..086a2c6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8,6 +8,7 @@
 subdirs = [
     "cmds/*",
     "libs/*",
-    "opengl/*",
+    "opengl",
     "services/*",
+    "vulkan",
 ]
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 44a994c..695e464 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -1,30 +1,100 @@
 LOCAL_PATH:= $(call my-dir)
 
-include $(CLEAR_VARS)
+# ================#
+# Common settings #
+# ================#
+# ZipArchive support, the order matters here to get all symbols.
+COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto_static
 
-ifdef BOARD_WLAN_DEVICE
-LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
-endif
-
-LOCAL_SRC_FILES := \
-        dumpstate.cpp \
+# TODO: ideally the tests should depend on a shared dumpstate library, but currently libdumpstate
+# is used to define the device-specific HAL library. Instead, both dumpstate and dumpstate_test
+# shares a lot of common settings
+COMMON_LOCAL_CFLAGS := \
+       -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+COMMON_SRC_FILES := \
         utils.cpp
-
-LOCAL_MODULE := dumpstate
-
-LOCAL_SHARED_LIBRARIES := \
+COMMON_SHARED_LIBRARIES := \
         libbase \
         libcutils \
         libhardware_legacy \
         liblog \
         libselinux
 
-# ZipArchive support, the order matters here to get all symbols.
-ZIP_LIBRARIES := libziparchive libz libcrypto_static
+# ==========#
+# dumpstate #
+# ==========#
+include $(CLEAR_VARS)
 
-LOCAL_STATIC_LIBRARIES := $(ZIP_LIBRARIES)
+ifdef BOARD_WLAN_DEVICE
+LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
+endif
+
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+        dumpstate.cpp
+
+LOCAL_MODULE := dumpstate
+
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES)
+
 LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
+
+LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)
+
 LOCAL_INIT_RC := dumpstate.rc
 
 include $(BUILD_EXECUTABLE)
+
+# ===============#
+# dumpstate_test #
+# ===============#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+        tests/dumpstate_test.cpp
+
+LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) \
+        libgmock
+
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+
+include $(BUILD_NATIVE_TEST)
+
+# =======================#
+# dumpstate_test_fixture #
+# =======================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := dumpstate_test_fixture
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_SRC_FILES := \
+        tests/dumpstate_test_fixture.cpp
+
+dist_zip_root := $(TARGET_OUT_DATA)
+dumpstate_tests_subpath_from_data := nativetest/dumpstate_test_fixture
+dumpstate_tests_root_in_device := /data/$(dumpstate_tests_subpath_from_data)
+dumpstate_tests_root_for_test_zip := $(dist_zip_root)/$(dumpstate_tests_subpath_from_data)
+testdata_files := $(call find-subdir-files, testdata/*)
+
+GEN := $(addprefix $(dumpstate_tests_root_for_test_zip)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(dumpstate_tests_root_for_test_zip)/testdata/% : $(LOCAL_PATH)/testdata/%
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+LOCAL_PICKUP_FILES := $(dist_zip_root)
+
+include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index f3e68b3..32dbf8d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -52,8 +52,6 @@
 
 #include <openssl/sha.h>
 
-using android::base::StringPrintf;
-
 /* read before root is shed */
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
@@ -62,7 +60,6 @@
 static std::string args;
 
 // TODO: variables below should be part of dumpstate object
-static std::string buildType;
 static time_t now;
 static std::unique_ptr<ZipWriter> zip_writer;
 static std::set<std::string> mount_points;
@@ -127,14 +124,6 @@
 static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
 static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
 
-bool Dumpstate::IsUserBuild() {
-    return "user" == buildType;
-}
-
-bool Dumpstate::IsDryRun() {
-    return dryRun_;
-}
-
 /* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
  * otherwise, gets just those modified in the last half an hour. */
 static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
@@ -155,7 +144,7 @@
 }
 
 // for_each_pid() callback to get mount info about a process.
-void do_mountinfo(int pid, const char *name) {
+void do_mountinfo(int pid, const char* name __attribute__((unused))) {
     char path[PATH_MAX];
 
     // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
@@ -426,7 +415,7 @@
     return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
 }
 
-static bool skip_none(const char *path) {
+static bool skip_none(const char* path __attribute__((unused))) {
     return false;
 }
 
@@ -704,7 +693,6 @@
 
     build = android::base::GetProperty("ro.build.display.id", "(unknown)");
     fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
-    buildType = android::base::GetProperty("ro.build.type", "(unknown)");
     radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
     bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
     network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
@@ -727,7 +715,7 @@
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
     printf("Bugreport format version: %s\n", version.c_str());
     printf("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", ds.id_, getpid(),
-           ds.dryRun_, args.c_str(), extraOptions.c_str());
+           ds.IsDryRun(), args.c_str(), extraOptions.c_str());
     printf("\n");
 }
 
@@ -805,7 +793,7 @@
 }
 
 /* adds a file to the existing zipped bugreport */
-static int _add_file_from_fd(const char *title, const char *path, int fd) {
+static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
     return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
 }
 
@@ -853,15 +841,16 @@
 static void dump_iptables() {
     RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
     RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
-    RunCommand("IPTABLE NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
+    RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
     /* no ip6 nat */
-    RunCommand("IPTABLE MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
-    RunCommand("IP6TABLE MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
-    RunCommand("IPTABLE RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
-    RunCommand("IP6TABLE RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
+    RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
+    RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
+    RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
+    RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
-static void dumpstate(const std::string& screenshot_path, const std::string& version) {
+static void dumpstate(const std::string& screenshot_path,
+                      const std::string& version __attribute__((unused))) {
     DurationReporter durationReporter("DUMPSTATE");
     unsigned long timeout;
 
@@ -1193,7 +1182,7 @@
     }
 }
 
-static void sig_handler(int signo) {
+static void sig_handler(int signo __attribute__((unused))) {
     wake_lock_releaser();
     _exit(EXIT_FAILURE);
 }
@@ -1319,8 +1308,7 @@
         register_sig_handler();
     }
 
-    ds.dryRun_ = android::base::GetBoolProperty("dumpstate.dry_run", false);
-    if (ds.dryRun_) {
+    if (ds.IsDryRun()) {
         MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
 
@@ -1620,6 +1608,10 @@
     add_mountinfo();
     dump_iptables();
 
+    // Capture any IPSec policies in play.  No keys are exposed here.
+    RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
+               CommandOptions::WithTimeout(10).Build());
+
     // Run ss as root so we can see socket marks.
     RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build());
 
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 70af270..adaf29e 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -37,9 +37,6 @@
 #include <string>
 #include <vector>
 
-// TODO: remove once dumpstate_board() devices use CommandOptions
-#define SU_PATH "/system/xbin/su"
-
 // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
 // std::vector<std::string>
 // TODO: remove once not used
@@ -202,6 +199,8 @@
  * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
  */
 class Dumpstate {
+    friend class DumpstateTest;
+
   public:
     static Dumpstate& GetInstance();
 
@@ -256,7 +255,13 @@
      */
     int DumpFile(const std::string& title, const std::string& path);
 
-    // TODO: fields below should be private once refactor is finished
+    // TODO: members below should be private once refactor is finished
+
+    /*
+     * Updates the overall progress of the bugreport generation by the given weight increment.
+     */
+    void UpdateProgress(int delta);
+
     // TODO: initialize fields on constructor
 
     // dumpstate id - unique after each device reboot.
@@ -274,15 +279,19 @@
     // When set, defines a socket file-descriptor use to report progress to bugreportz.
     int controlSocketFd_ = -1;
 
-    // Whether this is a dry run.
-    bool dryRun_;
 
     // Full path of the directory where the bugreport files will be written;
     std::string bugreportDir_;
 
   private:
     // Used by GetInstance() only.
-    Dumpstate();
+    Dumpstate(bool dryRun = false, const std::string& buildType = "user");
+
+    // Whether this is a dry run.
+    bool dryRun_;
+
+    // Build type (such as 'user' or 'eng').
+    std::string buildType_;
 };
 
 // for_each_pid_func = void (*)(int, const char*);
@@ -323,9 +332,6 @@
 /* sends a broadcast using Activity Manager */
 void send_broadcast(const std::string& action, const std::vector<std::string>& args);
 
-/* updates the overall progress of dumpstate by the given weight increment */
-void update_progress(int weight);
-
 /* prints all the system properties */
 void print_properties();
 
diff --git a/cmds/dumpstate/testdata/multiple-lines-with-newline.txt b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
new file mode 100644
index 0000000..7b7a187
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines-with-newline.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
diff --git a/cmds/dumpstate/testdata/multiple-lines.txt b/cmds/dumpstate/testdata/multiple-lines.txt
new file mode 100644
index 0000000..bead103
--- /dev/null
+++ b/cmds/dumpstate/testdata/multiple-lines.txt
@@ -0,0 +1,3 @@
+I AM LINE1
+I AM LINE2
+I AM LINE3
\ No newline at end of file
diff --git a/cmds/dumpstate/testdata/single-line-with-newline.txt b/cmds/dumpstate/testdata/single-line-with-newline.txt
new file mode 100644
index 0000000..cb48c82
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line-with-newline.txt
@@ -0,0 +1 @@
+I AM LINE1
diff --git a/cmds/dumpstate/testdata/single-line.txt b/cmds/dumpstate/testdata/single-line.txt
new file mode 100644
index 0000000..2f64046
--- /dev/null
+++ b/cmds/dumpstate/testdata/single-line.txt
@@ -0,0 +1 @@
+I AM LINE1
\ No newline at end of file
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
new file mode 100644
index 0000000..8d70704
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -0,0 +1,424 @@
+/*
+ * 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 "dumpstate.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <libgen.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <thread>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+using ::testing::EndsWith;
+using ::testing::IsEmpty;
+using ::testing::StrEq;
+using ::testing::StartsWith;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+// Not used on test cases yet...
+void dumpstate_board(void) {
+}
+
+class DumpstateTest : public Test {
+  public:
+    void SetUp() {
+        SetDryRun(false);
+        SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
+        ds.updateProgress_ = false;
+    }
+
+    // Runs a command and capture `stdout` and `stderr`.
+    int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+                   const CommandOptions& options = CommandOptions::DEFAULT) {
+        CaptureStdout();
+        CaptureStderr();
+        int status = ds.RunCommand(title, fullCommand, options);
+        out = GetCapturedStdout();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    // Dumps a file and capture `stdout` and `stderr`.
+    int DumpFile(const std::string& title, const std::string& path) {
+        CaptureStdout();
+        CaptureStderr();
+        int status = ds.DumpFile(title, path);
+        out = GetCapturedStdout();
+        err = GetCapturedStderr();
+        return status;
+    }
+
+    void SetDryRun(bool dryRun) {
+        ALOGD("Setting dryRun_ to %s\n", dryRun ? "true" : "false");
+        ds.dryRun_ = dryRun;
+    }
+
+    void SetBuildType(const std::string& buildType) {
+        ALOGD("Setting buildType_ to '%s'\n", buildType.c_str());
+        ds.buildType_ = buildType;
+    }
+
+    bool IsUserBuild() {
+        return "user" == android::base::GetProperty("ro.build.type", "(unknown)");
+    }
+
+    void DropRoot() {
+        drop_root_user();
+        uid_t uid = getuid();
+        ASSERT_EQ(2000, (int)uid);
+    }
+
+    // TODO: remove when progress is set by Binder callbacks.
+    void AssertSystemProperty(const std::string& key, const std::string& expectedValue) {
+        std::string actualValue = android::base::GetProperty(key, "not set");
+        EXPECT_THAT(expectedValue, StrEq(actualValue)) << "invalid value for property " << key;
+    }
+
+    std::string GetProgressMessage(int progress, int weightTotal, int oldWeightTotal = 0) {
+        EXPECT_EQ(progress, ds.progress_) << "invalid progress";
+        EXPECT_EQ(weightTotal, ds.weightTotal_) << "invalid weightTotal";
+
+        AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
+                             std::to_string(progress));
+
+        bool maxIncreased = oldWeightTotal > 0;
+
+        std::string adjustmentMessage = "";
+        if (maxIncreased) {
+            AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.max", getpid()),
+                                 std::to_string(weightTotal));
+            adjustmentMessage = android::base::StringPrintf(
+                "Adjusting total weight from %d to %d\n", oldWeightTotal, weightTotal);
+        }
+
+        return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
+                                           adjustmentMessage.c_str(), getpid(), progress,
+                                           weightTotal);
+    }
+
+    // `stdout` and `stderr` from the last command ran.
+    std::string out, err;
+
+    std::string testPath = dirname(android::base::GetExecutablePath().c_str());
+    std::string fixturesPath = testPath + "/../dumpstate_test_fixture/";
+    std::string testDataPath = fixturesPath + "/testdata/";
+    std::string simpleCommand = fixturesPath + "dumpstate_test_fixture";
+    std::string echoCommand = "/system/bin/echo";
+
+    Dumpstate& ds = Dumpstate::GetInstance();
+};
+
+TEST_F(DumpstateTest, RunCommandNoArgs) {
+    EXPECT_EQ(-1, RunCommand("", {}));
+}
+
+TEST_F(DumpstateTest, RunCommandNoTitle) {
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithTitle) {
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleCommand}));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out,
+                StartsWith("------ I AM GROOT (" + simpleCommand + ") ------\nstdout\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
+    EXPECT_EQ(
+        0, RunCommand("", {simpleCommand},
+                      CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandRedirectStderr) {
+    EXPECT_EQ(0, RunCommand("", {simpleCommand},
+                            CommandOptions::WithTimeout(10).RedirectStderr().Build()));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithOneArg) {
+    EXPECT_EQ(0, RunCommand("", {echoCommand, "one"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
+    EXPECT_EQ(0, RunCommand("", {echoCommand, "one", "is", "the", "loniest", "number"}));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("one is the loniest number\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandDryRun) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleCommand}));
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simpleCommand +
+                                ") ------\n\t(skipped on dry run)\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandDryRunAlways) {
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandNotFound) {
+    EXPECT_NE(0, RunCommand("", {"/there/cannot/be/such/command"}));
+    EXPECT_THAT(out, StartsWith("*** command '/there/cannot/be/such/command' failed: exit code"));
+    EXPECT_THAT(err, StartsWith("execvp on command '/there/cannot/be/such/command' failed"));
+}
+
+TEST_F(DumpstateTest, RunCommandFails) {
+    EXPECT_EQ(42, RunCommand("", {simpleCommand, "--exit", "42"}));
+    EXPECT_THAT(
+        out, StrEq("stdout\n*** command '" + simpleCommand + " --exit 42' failed: exit code 42\n"));
+    EXPECT_THAT(
+        err, StrEq("stderr\n*** command '" + simpleCommand + " --exit 42' failed: exit code 42\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandCrashes) {
+    EXPECT_NE(0, RunCommand("", {simpleCommand, "--crash"}));
+    // We don't know the exit code, so check just the prefix.
+    EXPECT_THAT(
+        out, StartsWith("stdout\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+    EXPECT_THAT(
+        err, StartsWith("stderr\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+}
+
+TEST_F(DumpstateTest, RunCommandTimesout) {
+    EXPECT_EQ(-1, RunCommand("", {simpleCommand, "--sleep", "2"},
+                             CommandOptions::WithTimeout(1).Build()));
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simpleCommand +
+                                " --sleep 2' timed out after 1"));
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simpleCommand +
+                                " --sleep 2' timed out after 1"));
+}
+
+TEST_F(DumpstateTest, RunCommandIsKilled) {
+    CaptureStdout();
+    CaptureStderr();
+
+    std::thread t([=]() {
+        EXPECT_EQ(SIGTERM, ds.RunCommand("", {simpleCommand, "--pid", "--sleep", "20"},
+                                         CommandOptions::WithTimeout(100).Always().Build()));
+    });
+
+    // Capture pid and pre-sleep output.
+    sleep(1);  // Wait a little bit to make sure pid and 1st line were printed.
+    std::string err = GetCapturedStderr();
+    EXPECT_THAT(err, StrEq("sleeping for 20s\n"));
+
+    std::string out = GetCapturedStdout();
+    std::vector<std::string> lines = android::base::Split(out, "\n");
+    ASSERT_EQ(3, (int)lines.size()) << "Invalid lines before sleep: " << out;
+
+    int pid = atoi(lines[0].c_str());
+    EXPECT_THAT(lines[1], StrEq("stdout line1"));
+    EXPECT_THAT(lines[2], IsEmpty());  // \n
+
+    // Then kill the process.
+    CaptureStdout();
+    CaptureStderr();
+    ASSERT_EQ(0, kill(pid, SIGTERM)) << "failed to kill pid " << pid;
+    t.join();
+
+    // Finally, check output after murder.
+    out = GetCapturedStdout();
+    err = GetCapturedStderr();
+
+    EXPECT_THAT(out, StrEq("*** command '" + simpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+    EXPECT_THAT(err, StrEq("*** command '" + simpleCommand +
+                           " --pid --sleep 20' failed: killed by signal 15\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandProgress) {
+    ds.updateProgress_ = true;
+    ds.progress_ = 0;
+    ds.weightTotal_ = 30;
+
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(20).Build()));
+    std::string progressMessage = GetProgressMessage(20, 30);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(10).Build()));
+    progressMessage = GetProgressMessage(30, 30);
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+
+    // Run a command that will increase maximum timeout.
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(1).Build()));
+    progressMessage = GetProgressMessage(31, 36, 30);  // 20% increase
+    EXPECT_THAT(out, StrEq("stdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+
+    // Make sure command ran while in dryRun is counted.
+    SetDryRun(true);
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(4).Build()));
+    progressMessage = GetProgressMessage(35, 36);
+    EXPECT_THAT(out, IsEmpty());
+    EXPECT_THAT(err, StrEq(progressMessage));
+}
+
+TEST_F(DumpstateTest, RunCommandDropRoot) {
+    // First check root case - only available when running with 'adb root'.
+    uid_t uid = getuid();
+    if (uid == 0) {
+        EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"}));
+        EXPECT_THAT(out, StrEq("0\nstdout\n"));
+        EXPECT_THAT(err, StrEq("stderr\n"));
+        return;
+    }
+    // Then drop root.
+
+    EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).DropRoot().Build()));
+    EXPECT_THAT(out, StrEq("2000\nstdout\n"));
+    EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootUserBuild) {
+    if (!IsUserBuild()) {
+        // Emulates user build if necessarily.
+        SetBuildType("user");
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    // We don't know the exact path of su, so we just check for the 'root ...' commands
+    EXPECT_THAT(out, StartsWith("Skipping"));
+    EXPECT_THAT(out, EndsWith("root " + simpleCommand + "' on user build.\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) {
+    if (IsUserBuild()) {
+        ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n");
+        return;
+    }
+
+    DropRoot();
+
+    EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"},
+                            CommandOptions::WithTimeout(1).AsRoot().Build()));
+
+    EXPECT_THAT(out, StrEq("0\nstdout\n"));
+    EXPECT_THAT(err, StrEq("stderr\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) {
+    EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(out,
+                StrEq("*** Error dumping /I/cant/believe/I/exist: No such file or directory\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) {
+    EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist"));
+    EXPECT_THAT(err, IsEmpty());
+    // We don't know the exact duration, so we check the prefix and suffix
+    EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No "
+                                "such file or directory\n"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLine) {
+    EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+}
+
+TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
+    EXPECT_EQ(0, DumpFile("", testDataPath + "single-line-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLines) {
+    EXPECT_EQ(0, DumpFile("", testDataPath + "multiple-lines.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
+    EXPECT_EQ(0, DumpFile("", testDataPath + "multiple-lines-with-newline.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
+    SetDryRun(true);
+    EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileOnDryRun) {
+    SetDryRun(true);
+    EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", testDataPath + "single-line.txt"));
+    EXPECT_THAT(err, IsEmpty());
+    EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + testDataPath +
+                                "single-line.txt) ------\n\t(skipped on dry run)\n------"));
+    EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+    EXPECT_THAT(err, IsEmpty());
+}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+    ds.updateProgress_ = true;
+    ds.progress_ = 0;
+    ds.weightTotal_ = 30;
+
+    EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+
+    std::string progressMessage = GetProgressMessage(5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
+
+    EXPECT_THAT(err, StrEq(progressMessage));
+    EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
+}
diff --git a/cmds/dumpstate/tests/dumpstate_test_fixture.cpp b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
new file mode 100644
index 0000000..5be4719
--- /dev/null
+++ b/cmds/dumpstate/tests/dumpstate_test_fixture.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+void PrintDefaultOutput() {
+    fprintf(stdout, "stdout\n");
+    fflush(stdout);
+    fprintf(stderr, "stderr\n");
+    fflush(stderr);
+}
+
+/*
+ * Binary used to on RunCommand tests.
+ *
+ * Usage:
+ *
+ * - Unless stated otherwise this command:
+ *
+ *   1.Prints `stdout\n` on `stdout` and flushes it.
+ *   2.Prints `stderr\n` on `stderr` and flushes it.
+ *   3.Exit with status 0.
+ *
+ * - If 1st argument is '--pid', it first prints its pid on `stdout`.
+ *
+ * - If 1st argument is '--uid', it first prints its uid on `stdout`.
+ *
+ * - If 1st argument is '--crash', it uses ALOGF to crash and returns 666.
+ *
+ * - With argument '--exit' 'CODE', returns CODE;
+ *
+ * - With argument '--sleep 'TIME':
+ *
+ *   1.Prints `stdout line1\n` on `stdout` and `sleeping TIME s\n` on `stderr`
+ *   2.Sleeps for TIME s
+ *   3.Prints `stdout line2\n` on `stdout` and `woke up\n` on `stderr`
+ */
+int main(int argc, char* const argv[]) {
+    if (argc == 2) {
+        if (strcmp(argv[1], "--crash") == 0) {
+            PrintDefaultOutput();
+            LOG_FATAL("D'OH\n");
+            return 666;
+        }
+    }
+    if (argc == 3) {
+        if (strcmp(argv[1], "--exit") == 0) {
+            PrintDefaultOutput();
+            return atoi(argv[2]);
+        }
+    }
+
+    if (argc > 1) {
+        int index = 1;
+
+        // First check arguments that can shift the index.
+        if (strcmp(argv[1], "--pid") == 0) {
+            index++;
+            fprintf(stdout, "%d\n", getpid());
+            fflush(stdout);
+        } else if (strcmp(argv[1], "--uid") == 0) {
+            index++;
+            fprintf(stdout, "%d\n", getuid());
+            fflush(stdout);
+        }
+
+        // Then the "common" arguments, if any.
+        if (argc > index + 1) {
+            if (strcmp(argv[index], "--sleep") == 0) {
+                int napTime = atoi(argv[index + 1]);
+                fprintf(stdout, "stdout line1\n");
+                fflush(stdout);
+                fprintf(stderr, "sleeping for %ds\n", napTime);
+                fflush(stderr);
+                sleep(napTime);
+                fprintf(stdout, "stdout line2\n");
+                fflush(stdout);
+                fprintf(stderr, "woke up\n");
+                fflush(stderr);
+                return 0;
+            }
+        }
+    }
+
+    PrintDefaultOutput();
+    return 0;
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index e29d90d..fc1f721 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -50,6 +50,8 @@
 
 #include "dumpstate.h"
 
+#define SU_PATH "/system/xbin/su"
+
 static const int64_t NANOS_PER_SEC = 1000000000;
 
 static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
@@ -60,6 +62,12 @@
                       const CommandOptions& options = CommandOptions::DEFAULT) {
     return ds.RunCommand(title, fullCommand, options);
 }
+static bool IsDryRun() {
+    return Dumpstate::GetInstance().IsDryRun();
+}
+static void UpdateProgress(int delta) {
+    ds.UpdateProgress(delta);
+}
 
 /* list of native processes to include in the native dumps */
 // This matches the /proc/pid/exe link instead of /proc/pid/cmdline.
@@ -154,11 +162,13 @@
     return CommandOptions::CommandOptionsBuilder(timeout);
 }
 
-Dumpstate::Dumpstate() {
+Dumpstate::Dumpstate(bool dryRun, const std::string& buildType)
+    : dryRun_(dryRun), buildType_(buildType) {
 }
 
 Dumpstate& Dumpstate::GetInstance() {
-    static Dumpstate sSingleton;
+    static Dumpstate sSingleton(android::base::GetBoolProperty("dumpstate.dry_run", false),
+                                android::base::GetProperty("ro.build.type", "(unknown)"));
     return sSingleton;
 }
 
@@ -190,13 +200,16 @@
     return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
 }
 
-// TODO: temporary function used during the C++ refactoring
-static bool is_dry_run() {
-    return Dumpstate::GetInstance().IsDryRun();
+bool Dumpstate::IsDryRun() {
+    return dryRun_;
+}
+
+bool Dumpstate::IsUserBuild() {
+    return "user" == buildType_;
 }
 
 void for_each_userid(void (*func)(int), const char *header) {
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
 
     DIR *d;
     struct dirent *de;
@@ -279,7 +292,7 @@
 }
 
 void for_each_pid(for_each_pid_func func, const char *header) {
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
 
     __for_each_pid(for_each_pid_helper, header, (void *) func);
 }
@@ -333,13 +346,13 @@
 }
 
 void for_each_tid(for_each_tid_func func, const char *header) {
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
 
     __for_each_pid(for_each_tid_helper, header, (void *) func);
 }
 
 void show_wchan(int pid, int tid, const char *name) {
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
 
     char path[255];
     char buffer[255];
@@ -406,7 +419,7 @@
 }
 
 void show_showtime(int pid, const char *name) {
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
 
     char path[255];
     char buffer[1023];
@@ -474,7 +487,7 @@
     DurationReporter duration_reporter(title);
     printf("------ %s ------\n", title);
 
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
 
     /* Get size of kernel buffer */
     int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
@@ -526,8 +539,8 @@
         }
         printf(") ------\n");
     }
-    if (is_dry_run()) {
-        update_progress(WEIGHT_FILE);
+    if (IsDryRun()) {
+        UpdateProgress(WEIGHT_FILE);
         close(fd);
         return 0;
     }
@@ -568,7 +581,7 @@
             }
         }
     }
-    update_progress(WEIGHT_FILE);
+    UpdateProgress(WEIGHT_FILE);
     close(fd);
 
     if (!newline) printf("\n");
@@ -578,11 +591,23 @@
 
 int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
     DurationReporter durationReporter(title);
+    if (IsDryRun()) {
+        if (!title.empty()) {
+            printf("------ %s (%s) ------\n", title.c_str(), path.c_str());
+            printf("\t(skipped on dry run)\n");
+        }
+        UpdateProgress(WEIGHT_FILE);
+        return 0;
+    }
+
     int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
     if (fd < 0) {
         int err = errno;
-        printf("*** %s: %s\n", path.c_str(), strerror(err));
-        if (!title.empty()) printf("\n");
+        if (title.empty()) {
+            printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+        } else {
+            printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
+        }
         return -1;
     }
     return _dump_file_from_fd(title, path.c_str(), fd);
@@ -627,7 +652,7 @@
     if (!title.empty()) {
         printf("------ %s (%s) ------\n", title.c_str(), dir);
     }
-    if (is_dry_run()) return 0;
+    if (IsDryRun()) return 0;
 
     if (dir[strlen(dir) - 1] == '/') {
         ++slash;
@@ -684,7 +709,7 @@
  * stuck.
  */
 int dump_file_from_fd(const char *title, const char *path, int fd) {
-    if (is_dry_run()) return 0;
+    if (IsDryRun()) return 0;
 
     int flags = fcntl(fd, F_GETFL);
     if (flags == -1) {
@@ -796,8 +821,11 @@
         MYLOGI(loggingMessage.c_str(), commandString.c_str());
     }
 
-    if (is_dry_run() && !options.Always()) {
-        update_progress(options.Timeout());
+    if (IsDryRun() && !options.Always()) {
+        if (!title.empty()) {
+            printf("\t(skipped on dry run)\n");
+        }
+        UpdateProgress(options.Timeout());
         return 0;
     }
 
@@ -861,7 +889,7 @@
             if (!silent)
                 printf("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
                        (float)elapsed / NANOS_PER_SEC, pid);
-            MYLOGE("command '%s' timed out after %.3fs (killing pid %d)\n", command,
+            MYLOGE("*** command '%s' timed out after %.3fs (killing pid %d)\n", command,
                    (float)elapsed / NANOS_PER_SEC, pid);
         } else {
             if (!silent)
@@ -871,9 +899,9 @@
                    (float)elapsed / NANOS_PER_SEC, pid);
         }
         kill(pid, SIGTERM);
-        if (!waitpid_with_timeout(pid, 5, NULL)) {
+        if (!waitpid_with_timeout(pid, 5, nullptr)) {
             kill(pid, SIGKILL);
-            if (!waitpid_with_timeout(pid, 5, NULL)) {
+            if (!waitpid_with_timeout(pid, 5, nullptr)) {
                 if (!silent)
                     printf("could not kill command '%s' (pid %d) even with SIGKILL.\n", command,
                            pid);
@@ -884,15 +912,17 @@
     }
 
     if (WIFSIGNALED(status)) {
-        if (!silent) printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
-        MYLOGE("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
+        if (!silent)
+            printf("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
+        MYLOGE("*** command '%s' failed: killed by signal %d\n", command, WTERMSIG(status));
     } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
-        if (!silent) printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
-        MYLOGE("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
+        status = WEXITSTATUS(status);
+        if (!silent) printf("*** command '%s' failed: exit code %d\n", command, status);
+        MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
     }
 
     if (weight > 0) {
-        update_progress(weight);
+        UpdateProgress(weight);
     }
     return status;
 }
@@ -907,7 +937,7 @@
 
 bool drop_root_user() {
     if (getgid() == AID_SHELL && getuid() == AID_SHELL) {
-        MYLOGD("drop_root_user(): already running as Shell");
+        MYLOGD("drop_root_user(): already running as Shell\n");
         return true;
     }
     /* ensure we will keep capabilities when we drop root */
@@ -988,7 +1018,7 @@
     const char* title = "SYSTEM PROPERTIES";
     DurationReporter duration_reporter(title);
     printf("------ %s ------\n", title);
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
     size_t i;
     num_props = 0;
     property_list(print_prop, NULL);
@@ -1094,7 +1124,7 @@
 /* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
 const char *dump_traces() {
     DurationReporter duration_reporter("DUMP TRACES", nullptr);
-    if (is_dry_run()) return nullptr;
+    if (IsDryRun()) return nullptr;
 
     const char* result = nullptr;
 
@@ -1246,7 +1276,7 @@
 
 void dump_route_tables() {
     DurationReporter duration_reporter("DUMP ROUTE TABLES");
-    if (is_dry_run()) return;
+    if (IsDryRun()) return;
     const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
     ds.DumpFile("RT_TABLES", RT_TABLES_PATH);
     FILE* fp = fopen(RT_TABLES_PATH, "re");
@@ -1266,21 +1296,21 @@
 }
 
 // TODO: make this function thread safe if sections are generated in parallel.
-void update_progress(int delta) {
-    if (!ds.updateProgress_) return;
+void Dumpstate::UpdateProgress(int delta) {
+    if (!updateProgress_) return;
 
-    ds.progress_ += delta;
+    progress_ += delta;
 
     char key[PROPERTY_KEY_MAX];
     char value[PROPERTY_VALUE_MAX];
 
     // adjusts max on the fly
-    if (ds.progress_ > ds.weightTotal_) {
-        int newTotal = ds.weightTotal_ * 1.2;
-        MYLOGD("Adjusting total weight from %d to %d\n", ds.weightTotal_, newTotal);
-        ds.weightTotal_ = newTotal;
+    if (progress_ > weightTotal_) {
+        int newTotal = weightTotal_ * 1.2;
+        MYLOGD("Adjusting total weight from %d to %d\n", weightTotal_, newTotal);
+        weightTotal_ = newTotal;
         snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
-        snprintf(value, sizeof(value), "%d", ds.weightTotal_);
+        snprintf(value, sizeof(value), "%d", weightTotal_);
         int status = property_set(key, value);
         if (status != 0) {
             MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
@@ -1289,20 +1319,20 @@
     }
 
     snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
-    snprintf(value, sizeof(value), "%d", ds.progress_);
+    snprintf(value, sizeof(value), "%d", progress_);
 
-    if (ds.progress_ % 100 == 0) {
+    if (progress_ % 100 == 0) {
         // We don't want to spam logcat, so only log multiples of 100.
-        MYLOGD("Setting progress (%s): %s/%d\n", key, value, ds.weightTotal_);
+        MYLOGD("Setting progress (%s): %s/%d\n", key, value, weightTotal_);
     } else {
         // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
         // directly for debuggging.
-        fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, ds.weightTotal_);
+        fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weightTotal_);
     }
 
-    if (ds.controlSocketFd_ >= 0) {
-        dprintf(ds.controlSocketFd_, "PROGRESS:%d/%d\n", ds.progress_, ds.weightTotal_);
-        fsync(ds.controlSocketFd_);
+    if (controlSocketFd_ >= 0) {
+        dprintf(controlSocketFd_, "PROGRESS:%d/%d\n", progress_, weightTotal_);
+        fsync(controlSocketFd_);
     }
 
     int status = property_set(key, value);
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
index 28d4481..dac4314 100644
--- a/cmds/surfacereplayer/replayer/Android.mk
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -35,6 +35,7 @@
     libEGL \
     libGLESv2 \
     libbinder \
+    liblog \
     libcutils \
     libgui \
     libui \
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 3ab63d0..5232d0f 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -74,13 +74,7 @@
     // to set by queueBuffer each time this slot is queued. This value
     // is guaranteed to be monotonically increasing for each newly
     // acquired buffer.
-    union {
-        int64_t mTimestamp;
-        struct {
-            uint32_t mTimestampLo;
-            uint32_t mTimestampHi;
-        };
-    };
+    int64_t mTimestamp;
 
     // mIsAutoTimestamp indicates whether mTimestamp was generated
     // automatically when the buffer was queued.
@@ -92,13 +86,7 @@
     android_dataspace mDataSpace;
 
     // mFrameNumber is the number of the queued frame for this slot.
-    union {
-        uint64_t mFrameNumber;
-        struct {
-            uint32_t mFrameNumberLo;
-            uint32_t mFrameNumberHi;
-        };
-    };
+    uint64_t mFrameNumber;
 
     // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
     int mSlot;
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 15b7dbe..b1c730a 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -185,8 +185,12 @@
     // 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
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index b3291f4..65dea0d 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-class BufferSlot;
+struct BufferSlot;
 
 class BufferQueueProducer : public BnGraphicBufferProducer,
                             private IBinder::DeathRecipient {
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/OccupancyTracker.h b/include/gui/OccupancyTracker.h
index 1d15e7f..d4de8f2 100644
--- a/include/gui/OccupancyTracker.h
+++ b/include/gui/OccupancyTracker.h
@@ -45,12 +45,12 @@
             occupancyAverage(0.0f),
             usedThirdBuffer(false) {}
 
-        Segment(nsecs_t totalTime, size_t numFrames, float occupancyAverage,
-                bool usedThirdBuffer)
-          : totalTime(totalTime),
-            numFrames(numFrames),
-            occupancyAverage(occupancyAverage),
-            usedThirdBuffer(usedThirdBuffer) {}
+        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;
diff --git a/include/ui/Fence.h b/include/ui/Fence.h
index a4c1df7..d45ad76 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);
 
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index e354b11..4f77eed 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -46,7 +46,6 @@
         "-g",
         "-Wall",
         "-Werror",
-        "-std=c++11",
         "-Wno-missing-field-initializers",
         "-Wno-sign-compare",
         "-O3",
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index f5961cf..71b5cca 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -36,6 +36,9 @@
 # Don't warn about struct padding
 LOCAL_CPPFLAGS += -Wno-padded
 
+# android/sensors.h uses nested anonymous unions and anonymous structs
+LOCAL_CPPFLAGS += -Wno-nested-anon-types -Wno-gnu-anonymous-struct
+
 LOCAL_CPPFLAGS += -DDEBUG_ONLY_CODE=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
 
 LOCAL_SRC_FILES := \
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 2ffeb0d..669124e 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -23,6 +23,21 @@
 
 namespace android {
 
+template<typename T>
+static inline constexpr uint32_t low32(const T n) {
+    return static_cast<uint32_t>(static_cast<uint64_t>(n));
+}
+
+template<typename T>
+static inline constexpr uint32_t high32(const T n) {
+    return static_cast<uint32_t>(static_cast<uint64_t>(n)>>32);
+}
+
+template<typename T>
+static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+    return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo);
+}
+
 BufferItem::BufferItem() :
     mGraphicBuffer(NULL),
     mFence(NULL),
@@ -56,12 +71,12 @@
     addAligned(size, mCrop);
     addAligned(size, mTransform);
     addAligned(size, mScalingMode);
-    addAligned(size, mTimestampLo);
-    addAligned(size, mTimestampHi);
+    addAligned(size, low32(mTimestamp));
+    addAligned(size, high32(mTimestamp));
     addAligned(size, mIsAutoTimestamp);
     addAligned(size, mDataSpace);
-    addAligned(size, mFrameNumberLo);
-    addAligned(size, mFrameNumberHi);
+    addAligned(size, low32(mFrameNumber));
+    addAligned(size, high32(mFrameNumber));
     addAligned(size, mSlot);
     addAligned(size, mIsDroppable);
     addAligned(size, mAcquireCalled);
@@ -144,12 +159,12 @@
     writeAligned(buffer, size, mCrop);
     writeAligned(buffer, size, mTransform);
     writeAligned(buffer, size, mScalingMode);
-    writeAligned(buffer, size, mTimestampLo);
-    writeAligned(buffer, size, mTimestampHi);
+    writeAligned(buffer, size, low32(mTimestamp));
+    writeAligned(buffer, size, high32(mTimestamp));
     writeAligned(buffer, size, mIsAutoTimestamp);
     writeAligned(buffer, size, mDataSpace);
-    writeAligned(buffer, size, mFrameNumberLo);
-    writeAligned(buffer, size, mFrameNumberHi);
+    writeAligned(buffer, size, low32(mFrameNumber));
+    writeAligned(buffer, size, high32(mFrameNumber));
     writeAligned(buffer, size, mSlot);
     writeAligned(buffer, size, mIsDroppable);
     writeAligned(buffer, size, mAcquireCalled);
@@ -200,15 +215,20 @@
         return NO_MEMORY;
     }
 
+    uint32_t timestampLo = 0, timestampHi = 0;
+    uint32_t frameNumberLo = 0, frameNumberHi = 0;
+
     readAligned(buffer, size, mCrop);
     readAligned(buffer, size, mTransform);
     readAligned(buffer, size, mScalingMode);
-    readAligned(buffer, size, mTimestampLo);
-    readAligned(buffer, size, mTimestampHi);
+    readAligned(buffer, size, timestampLo);
+    readAligned(buffer, size, timestampHi);
+    mTimestamp = to64<int64_t>(timestampLo, timestampHi);
     readAligned(buffer, size, mIsAutoTimestamp);
     readAligned(buffer, size, mDataSpace);
-    readAligned(buffer, size, mFrameNumberLo);
-    readAligned(buffer, size, mFrameNumberHi);
+    readAligned(buffer, size, frameNumberLo);
+    readAligned(buffer, size, frameNumberHi);
+    mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
     readAligned(buffer, size, mSlot);
     readAligned(buffer, size, mIsDroppable);
     readAligned(buffer, size, mAcquireCalled);
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 13f4e1c..6e6cce2 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -62,6 +62,7 @@
     mConsumerListener(),
     mConsumerUsageBits(0),
     mConnectedApi(NO_CONNECTED_API),
+    mLinkedToDeath(),
     mConnectedProducerListener(),
     mSlots(),
     mQueue(),
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 13166f6..90b4b9d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1122,18 +1122,22 @@
                     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);
@@ -1195,9 +1199,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(
@@ -1205,6 +1209,7 @@
                     }
                     mCore->mSharedBufferSlot =
                             BufferQueueCore::INVALID_BUFFER_SLOT;
+                    mCore->mLinkedToDeath = NULL;
                     mCore->mConnectedProducerListener = NULL;
                     mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
                     mCore->mConnectedPid = -1;
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 805a10d..5546d54 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -315,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/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/input/tests/Android.bp b/libs/input/tests/Android.bp
index f0b5eda..029a420 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -23,7 +23,6 @@
     name: "StructLayout_test",
     srcs: ["StructLayout_test.cpp"],
     cflags: [
-        "-std=c++11",
         "-O0",
     ],
 }
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/opengl/Android.bp b/opengl/Android.bp
new file mode 100644
index 0000000..164be20
--- /dev/null
+++ b/opengl/Android.bp
@@ -0,0 +1,52 @@
+// 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.
+
+ndk_headers {
+    name: "libEGL_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/EGL/**/*.h"],
+}
+
+ndk_headers {
+    name: "libGLESv1_CM_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/GLES/**/*.h"],
+}
+
+ndk_headers {
+    name: "libGLESv2_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/GLES2/**/*.h"],
+}
+
+ndk_headers {
+    name: "libGLESv3_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/GLES3/**/*.h"],
+}
+
+ndk_headers {
+    name: "khr_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/KHR/**/*.h"],
+}
+
+subdirs = [
+    "*",
+]
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index f28d4ff..bdd5152 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -20,3 +20,28 @@
         },
     },
 }
+
+// The headers modules are in frameworks/native/opengl/Android.bp.
+ndk_library {
+    name: "libEGL.ndk",
+    symbol_file: "libEGL.map.txt",
+    first_version: "9",
+}
+
+ndk_library {
+    name: "libGLESv1_CM.ndk",
+    symbol_file: "libGLESv1_CM.map.txt",
+    first_version: "9",
+}
+
+ndk_library {
+    name: "libGLESv2.ndk",
+    symbol_file: "libGLESv2.map.txt",
+    first_version: "9",
+}
+
+ndk_library {
+    name: "libGLESv3.ndk",
+    symbol_file: "libGLESv3.map.txt",
+    first_version: "18",
+}
diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt
new file mode 100644
index 0000000..c8b83f5
--- /dev/null
+++ b/opengl/libs/libEGL.map.txt
@@ -0,0 +1,67 @@
+LIBEGL {
+  global:
+    eglBindAPI;
+    eglBindTexImage;
+    eglChooseConfig;
+    eglClientWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglCopyBuffers;
+    eglCreateContext;
+    eglCreateImageKHR;
+    eglCreateNativeClientBufferANDROID; # introduced=24
+    eglCreatePbufferFromClientBuffer;
+    eglCreatePbufferSurface;
+    eglCreatePixmapSurface;
+    eglCreateStreamFromFileDescriptorKHR; # introduced=23
+    eglCreateStreamKHR; # introduced=23
+    eglCreateStreamProducerSurfaceKHR; # introduced=23
+    eglCreateSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglCreateWindowSurface;
+    eglDestroyContext;
+    eglDestroyImageKHR;
+    eglDestroyStreamKHR; # introduced=23
+    eglDestroySurface;
+    eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglGetConfigAttrib;
+    eglGetConfigs;
+    eglGetCurrentContext;
+    eglGetCurrentDisplay;
+    eglGetCurrentSurface;
+    eglGetDisplay;
+    eglGetError;
+    eglGetProcAddress;
+    eglGetStreamFileDescriptorKHR; # introduced=23
+    eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglGetSystemTimeFrequencyNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+    eglGetSystemTimeNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+    eglInitialize;
+    eglLockSurfaceKHR;
+    eglMakeCurrent;
+    eglPresentationTimeANDROID; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglQueryAPI;
+    eglQueryContext;
+    eglQueryStreamKHR; # introduced=23
+    eglQueryStreamTimeKHR; # introduced=23
+    eglQueryStreamu64KHR; # introduced=23
+    eglQueryString;
+    eglQuerySurface;
+    eglReleaseTexImage;
+    eglReleaseThread;
+    eglSetDamageRegionKHR; # introduced=23
+    eglSignalSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    eglStreamAttribKHR; # introduced=23
+    eglStreamConsumerAcquireKHR; # introduced=23
+    eglStreamConsumerGLTextureExternalKHR; # introduced=23
+    eglStreamConsumerReleaseKHR; # introduced=23
+    eglSurfaceAttrib;
+    eglSwapBuffers;
+    eglSwapBuffersWithDamageKHR; # introduced=23
+    eglSwapInterval;
+    eglTerminate;
+    eglUnlockSurfaceKHR;
+    eglWaitClient;
+    eglWaitGL;
+    eglWaitNative;
+    eglWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+  local:
+    *;
+};
diff --git a/opengl/libs/libGLESv1_CM.map.txt b/opengl/libs/libGLESv1_CM.map.txt
new file mode 100644
index 0000000..8ba91e6
--- /dev/null
+++ b/opengl/libs/libGLESv1_CM.map.txt
@@ -0,0 +1,283 @@
+LIBGLESV1_CM {
+  global:
+    glActiveTexture;
+    glAlphaFunc;
+    glAlphaFuncx;
+    glAlphaFuncxOES;
+    glBindBuffer;
+    glBindFramebufferOES;
+    glBindRenderbufferOES;
+    glBindTexture;
+    glBindVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glBlendEquationOES;
+    glBlendEquationSeparateOES;
+    glBlendFunc;
+    glBlendFuncSeparateOES;
+    glBufferData;
+    glBufferSubData;
+    glCheckFramebufferStatusOES;
+    glClear;
+    glClearColor;
+    glClearColorx;
+    glClearColorxOES;
+    glClearDepthf;
+    glClearDepthfOES;
+    glClearDepthx;
+    glClearDepthxOES;
+    glClearStencil;
+    glClientActiveTexture;
+    glClipPlanef;
+    glClipPlanefIMG; # introduced-mips=9 introduced-x86=9
+    glClipPlanefOES;
+    glClipPlanex;
+    glClipPlanexIMG; # introduced-mips=9 introduced-x86=9
+    glClipPlanexOES;
+    glColor4f;
+    glColor4ub;
+    glColor4x;
+    glColor4xOES;
+    glColorMask;
+    glColorPointer;
+    glColorPointerBounds;
+    glCompressedTexImage2D;
+    glCompressedTexSubImage2D;
+    glCopyTexImage2D;
+    glCopyTexSubImage2D;
+    glCullFace;
+    glCurrentPaletteMatrixOES;
+    glDeleteBuffers;
+    glDeleteFencesNV; # introduced-mips=9 introduced-x86=9
+    glDeleteFramebuffersOES;
+    glDeleteRenderbuffersOES;
+    glDeleteTextures;
+    glDeleteVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glDepthFunc;
+    glDepthMask;
+    glDepthRangef;
+    glDepthRangefOES;
+    glDepthRangex;
+    glDepthRangexOES;
+    glDisable;
+    glDisableClientState;
+    glDisableDriverControlQCOM; # introduced-mips=9 introduced-x86=9
+    glDiscardFramebufferEXT; # introduced-mips=9 introduced-x86=9
+    glDrawArrays;
+    glDrawElements;
+    glDrawTexfOES;
+    glDrawTexfvOES;
+    glDrawTexiOES;
+    glDrawTexivOES;
+    glDrawTexsOES;
+    glDrawTexsvOES;
+    glDrawTexxOES;
+    glDrawTexxvOES;
+    glEGLImageTargetRenderbufferStorageOES;
+    glEGLImageTargetTexture2DOES;
+    glEnable;
+    glEnableClientState;
+    glEnableDriverControlQCOM; # introduced-mips=9 introduced-x86=9
+    glEndTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBufferPointervQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetFramebuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramBinarySourceQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramsQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetRenderbuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetShadersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexLevelParameterivQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexSubImageQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexturesQCOM; # introduced-mips=9 introduced-x86=9
+    glExtIsProgramBinaryQCOM; # introduced-mips=9 introduced-x86=9
+    glExtTexObjectStateOverrideiQCOM; # introduced-mips=9 introduced-x86=9
+    glFinish;
+    glFinishFenceNV; # introduced-mips=9 introduced-x86=9
+    glFlush;
+    glFogf;
+    glFogfv;
+    glFogx;
+    glFogxOES;
+    glFogxv;
+    glFogxvOES;
+    glFramebufferRenderbufferOES;
+    glFramebufferTexture2DMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glFramebufferTexture2DOES;
+    glFrontFace;
+    glFrustumf;
+    glFrustumfOES;
+    glFrustumx;
+    glFrustumxOES;
+    glGenBuffers;
+    glGenFencesNV; # introduced-mips=9 introduced-x86=9
+    glGenFramebuffersOES;
+    glGenRenderbuffersOES;
+    glGenTextures;
+    glGenVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glGenerateMipmapOES;
+    glGetBooleanv;
+    glGetBufferParameteriv;
+    glGetBufferPointervOES;
+    glGetClipPlanef;
+    glGetClipPlanefOES;
+    glGetClipPlanex;
+    glGetClipPlanexOES;
+    glGetDriverControlStringQCOM; # introduced-mips=9 introduced-x86=9
+    glGetDriverControlsQCOM; # introduced-mips=9 introduced-x86=9
+    glGetError;
+    glGetFenceivNV; # introduced-mips=9 introduced-x86=9
+    glGetFixedv;
+    glGetFixedvOES;
+    glGetFloatv;
+    glGetFramebufferAttachmentParameterivOES;
+    glGetIntegerv;
+    glGetLightfv;
+    glGetLightxv;
+    glGetLightxvOES;
+    glGetMaterialfv;
+    glGetMaterialxv;
+    glGetMaterialxvOES;
+    glGetPointerv;
+    glGetRenderbufferParameterivOES;
+    glGetString;
+    glGetTexEnvfv;
+    glGetTexEnviv;
+    glGetTexEnvxv;
+    glGetTexEnvxvOES;
+    glGetTexGenfvOES;
+    glGetTexGenivOES;
+    glGetTexGenxvOES;
+    glGetTexParameterfv;
+    glGetTexParameteriv;
+    glGetTexParameterxv;
+    glGetTexParameterxvOES;
+    glHint;
+    glIsBuffer;
+    glIsEnabled;
+    glIsFenceNV; # introduced-mips=9 introduced-x86=9
+    glIsFramebufferOES;
+    glIsRenderbufferOES;
+    glIsTexture;
+    glIsVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glLightModelf;
+    glLightModelfv;
+    glLightModelx;
+    glLightModelxOES;
+    glLightModelxv;
+    glLightModelxvOES;
+    glLightf;
+    glLightfv;
+    glLightx;
+    glLightxOES;
+    glLightxv;
+    glLightxvOES;
+    glLineWidth;
+    glLineWidthx;
+    glLineWidthxOES;
+    glLoadIdentity;
+    glLoadMatrixf;
+    glLoadMatrixx;
+    glLoadMatrixxOES;
+    glLoadPaletteFromModelViewMatrixOES;
+    glLogicOp;
+    glMapBufferOES;
+    glMaterialf;
+    glMaterialfv;
+    glMaterialx;
+    glMaterialxOES;
+    glMaterialxv;
+    glMaterialxvOES;
+    glMatrixIndexPointerOES;
+    glMatrixIndexPointerOESBounds; # introduced-mips=9 introduced-x86=9
+    glMatrixMode;
+    glMultMatrixf;
+    glMultMatrixx;
+    glMultMatrixxOES;
+    glMultiDrawArraysEXT; # introduced-mips=9 introduced-x86=9
+    glMultiDrawElementsEXT; # introduced-mips=9 introduced-x86=9
+    glMultiTexCoord4f;
+    glMultiTexCoord4x;
+    glMultiTexCoord4xOES;
+    glNormal3f;
+    glNormal3x;
+    glNormal3xOES;
+    glNormalPointer;
+    glNormalPointerBounds;
+    glOrthof;
+    glOrthofOES;
+    glOrthox;
+    glOrthoxOES;
+    glPixelStorei;
+    glPointParameterf;
+    glPointParameterfv;
+    glPointParameterx;
+    glPointParameterxOES;
+    glPointParameterxv;
+    glPointParameterxvOES;
+    glPointSize;
+    glPointSizePointerOES;
+    glPointSizePointerOESBounds; # introduced-mips=9 introduced-x86=9
+    glPointSizex;
+    glPointSizexOES;
+    glPolygonOffset;
+    glPolygonOffsetx;
+    glPolygonOffsetxOES;
+    glPopMatrix;
+    glPushMatrix;
+    glQueryMatrixxOES;
+    glReadPixels;
+    glRenderbufferStorageMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glRenderbufferStorageOES;
+    glRotatef;
+    glRotatex;
+    glRotatexOES;
+    glSampleCoverage;
+    glSampleCoveragex;
+    glSampleCoveragexOES;
+    glScalef;
+    glScalex;
+    glScalexOES;
+    glScissor;
+    glSetFenceNV; # introduced-mips=9 introduced-x86=9
+    glShadeModel;
+    glStartTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glStencilFunc;
+    glStencilMask;
+    glStencilOp;
+    glTestFenceNV; # introduced-mips=9 introduced-x86=9
+    glTexCoordPointer;
+    glTexCoordPointerBounds;
+    glTexEnvf;
+    glTexEnvfv;
+    glTexEnvi;
+    glTexEnviv;
+    glTexEnvx;
+    glTexEnvxOES;
+    glTexEnvxv;
+    glTexEnvxvOES;
+    glTexGenfOES;
+    glTexGenfvOES;
+    glTexGeniOES;
+    glTexGenivOES;
+    glTexGenxOES;
+    glTexGenxvOES;
+    glTexImage2D;
+    glTexParameterf;
+    glTexParameterfv;
+    glTexParameteri;
+    glTexParameteriv;
+    glTexParameterx;
+    glTexParameterxOES;
+    glTexParameterxv;
+    glTexParameterxvOES;
+    glTexSubImage2D;
+    glTranslatef;
+    glTranslatex;
+    glTranslatexOES;
+    glUnmapBufferOES;
+    glVertexPointer;
+    glVertexPointerBounds;
+    glViewport;
+    glWeightPointerOES;
+    glWeightPointerOESBounds; # introduced-mips=9 introduced-x86=9
+  local:
+    *;
+};
diff --git a/opengl/libs/libGLESv2.map.txt b/opengl/libs/libGLESv2.map.txt
new file mode 100644
index 0000000..1b0042a
--- /dev/null
+++ b/opengl/libs/libGLESv2.map.txt
@@ -0,0 +1,207 @@
+LIBGLESV2 {
+  global:
+    glActiveTexture;
+    glAttachShader;
+    glBeginPerfMonitorAMD;
+    glBindAttribLocation;
+    glBindBuffer;
+    glBindFramebuffer;
+    glBindRenderbuffer;
+    glBindTexture;
+    glBindVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glBlendColor;
+    glBlendEquation;
+    glBlendEquationSeparate;
+    glBlendFunc;
+    glBlendFuncSeparate;
+    glBufferData;
+    glBufferSubData;
+    glCheckFramebufferStatus;
+    glClear;
+    glClearColor;
+    glClearDepthf;
+    glClearStencil;
+    glColorMask;
+    glCompileShader;
+    glCompressedTexImage2D;
+    glCompressedTexImage3DOES;
+    glCompressedTexSubImage2D;
+    glCompressedTexSubImage3DOES;
+    glCopyTexImage2D;
+    glCopyTexSubImage2D;
+    glCopyTexSubImage3DOES;
+    glCoverageMaskNV; # introduced-mips=9 introduced-x86=9
+    glCoverageOperationNV; # introduced-mips=9 introduced-x86=9
+    glCreateProgram;
+    glCreateShader;
+    glCullFace;
+    glDeleteBuffers;
+    glDeleteFencesNV;
+    glDeleteFramebuffers;
+    glDeletePerfMonitorsAMD;
+    glDeleteProgram;
+    glDeleteRenderbuffers;
+    glDeleteShader;
+    glDeleteTextures;
+    glDeleteVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glDepthFunc;
+    glDepthMask;
+    glDepthRangef;
+    glDetachShader;
+    glDisable;
+    glDisableDriverControlQCOM;
+    glDisableVertexAttribArray;
+    glDiscardFramebufferEXT; # introduced-mips=9 introduced-x86=9
+    glDrawArrays;
+    glDrawElements;
+    glEGLImageTargetRenderbufferStorageOES;
+    glEGLImageTargetTexture2DOES;
+    glEnable;
+    glEnableDriverControlQCOM;
+    glEnableVertexAttribArray;
+    glEndPerfMonitorAMD;
+    glEndTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBufferPointervQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetBuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetFramebuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramBinarySourceQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetProgramsQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetRenderbuffersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetShadersQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexLevelParameterivQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexSubImageQCOM; # introduced-mips=9 introduced-x86=9
+    glExtGetTexturesQCOM; # introduced-mips=9 introduced-x86=9
+    glExtIsProgramBinaryQCOM; # introduced-mips=9 introduced-x86=9
+    glExtTexObjectStateOverrideiQCOM; # introduced-mips=9 introduced-x86=9
+    glFinish;
+    glFinishFenceNV;
+    glFlush;
+    glFramebufferRenderbuffer;
+    glFramebufferTexture2D;
+    glFramebufferTexture2DMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glFramebufferTexture3DOES;
+    glFrontFace;
+    glGenBuffers;
+    glGenFencesNV;
+    glGenFramebuffers;
+    glGenPerfMonitorsAMD;
+    glGenRenderbuffers;
+    glGenTextures;
+    glGenVertexArraysOES; # introduced-mips=9 introduced-x86=9
+    glGenerateMipmap;
+    glGetActiveAttrib;
+    glGetActiveUniform;
+    glGetAttachedShaders;
+    glGetAttribLocation;
+    glGetBooleanv;
+    glGetBufferParameteriv;
+    glGetBufferPointervOES;
+    glGetDriverControlStringQCOM;
+    glGetDriverControlsQCOM;
+    glGetError;
+    glGetFenceivNV;
+    glGetFloatv;
+    glGetFramebufferAttachmentParameteriv;
+    glGetIntegerv;
+    glGetPerfMonitorCounterDataAMD;
+    glGetPerfMonitorCounterInfoAMD;
+    glGetPerfMonitorCounterStringAMD;
+    glGetPerfMonitorCountersAMD;
+    glGetPerfMonitorGroupStringAMD;
+    glGetPerfMonitorGroupsAMD;
+    glGetProgramBinaryOES;
+    glGetProgramInfoLog;
+    glGetProgramiv;
+    glGetRenderbufferParameteriv;
+    glGetShaderInfoLog;
+    glGetShaderPrecisionFormat;
+    glGetShaderSource;
+    glGetShaderiv;
+    glGetString;
+    glGetTexParameterfv;
+    glGetTexParameteriv;
+    glGetUniformLocation;
+    glGetUniformfv;
+    glGetUniformiv;
+    glGetVertexAttribPointerv;
+    glGetVertexAttribfv;
+    glGetVertexAttribiv;
+    glHint;
+    glIsBuffer;
+    glIsEnabled;
+    glIsFenceNV;
+    glIsFramebuffer;
+    glIsProgram;
+    glIsRenderbuffer;
+    glIsShader;
+    glIsTexture;
+    glIsVertexArrayOES; # introduced-mips=9 introduced-x86=9
+    glLineWidth;
+    glLinkProgram;
+    glMapBufferOES;
+    glMultiDrawArraysEXT; # introduced-mips=9 introduced-x86=9
+    glMultiDrawElementsEXT; # introduced-mips=9 introduced-x86=9
+    glPixelStorei;
+    glPolygonOffset;
+    glProgramBinaryOES;
+    glReadPixels;
+    glReleaseShaderCompiler;
+    glRenderbufferStorage;
+    glRenderbufferStorageMultisampleIMG; # introduced-mips=9 introduced-x86=9
+    glSampleCoverage;
+    glScissor;
+    glSelectPerfMonitorCountersAMD;
+    glSetFenceNV;
+    glShaderBinary;
+    glShaderSource;
+    glStartTilingQCOM; # introduced-mips=9 introduced-x86=9
+    glStencilFunc;
+    glStencilFuncSeparate;
+    glStencilMask;
+    glStencilMaskSeparate;
+    glStencilOp;
+    glStencilOpSeparate;
+    glTestFenceNV;
+    glTexImage2D;
+    glTexImage3DOES;
+    glTexParameterf;
+    glTexParameterfv;
+    glTexParameteri;
+    glTexParameteriv;
+    glTexSubImage2D;
+    glTexSubImage3DOES;
+    glUniform1f;
+    glUniform1fv;
+    glUniform1i;
+    glUniform1iv;
+    glUniform2f;
+    glUniform2fv;
+    glUniform2i;
+    glUniform2iv;
+    glUniform3f;
+    glUniform3fv;
+    glUniform3i;
+    glUniform3iv;
+    glUniform4f;
+    glUniform4fv;
+    glUniform4i;
+    glUniform4iv;
+    glUniformMatrix2fv;
+    glUniformMatrix3fv;
+    glUniformMatrix4fv;
+    glUnmapBufferOES;
+    glUseProgram;
+    glValidateProgram;
+    glVertexAttrib1f;
+    glVertexAttrib1fv;
+    glVertexAttrib2f;
+    glVertexAttrib2fv;
+    glVertexAttrib3f;
+    glVertexAttrib3fv;
+    glVertexAttrib4f;
+    glVertexAttrib4fv;
+    glVertexAttribPointer;
+    glViewport;
+  local:
+    *;
+};
diff --git a/opengl/libs/libGLESv3.map.txt b/opengl/libs/libGLESv3.map.txt
new file mode 100644
index 0000000..21f6cb6
--- /dev/null
+++ b/opengl/libs/libGLESv3.map.txt
@@ -0,0 +1,416 @@
+LIBGLESV3 {
+  global:
+    glActiveShaderProgram; # introduced=21
+    glActiveTexture;
+    glAttachShader;
+    glBeginQuery;
+    glBeginTransformFeedback;
+    glBindAttribLocation;
+    glBindBuffer;
+    glBindBufferBase;
+    glBindBufferRange;
+    glBindFramebuffer;
+    glBindImageTexture; # introduced=21
+    glBindProgramPipeline; # introduced=21
+    glBindRenderbuffer;
+    glBindSampler;
+    glBindTexture;
+    glBindTransformFeedback;
+    glBindVertexArray;
+    glBindVertexArrayOES;
+    glBindVertexBuffer; # introduced=21
+    glBlendBarrier; # introduced=24
+    glBlendBarrierKHR; # introduced=21
+    glBlendColor;
+    glBlendEquation;
+    glBlendEquationSeparate;
+    glBlendEquationSeparatei; # introduced=24
+    glBlendEquationSeparateiEXT; # introduced=21
+    glBlendEquationi; # introduced=24
+    glBlendEquationiEXT; # introduced=21
+    glBlendFunc;
+    glBlendFuncSeparate;
+    glBlendFuncSeparatei; # introduced=24
+    glBlendFuncSeparateiEXT; # introduced=21
+    glBlendFunci; # introduced=24
+    glBlendFunciEXT; # introduced=21
+    glBlitFramebuffer;
+    glBufferData;
+    glBufferSubData;
+    glCheckFramebufferStatus;
+    glClear;
+    glClearBufferfi;
+    glClearBufferfv;
+    glClearBufferiv;
+    glClearBufferuiv;
+    glClearColor;
+    glClearDepthf;
+    glClearStencil;
+    glClientWaitSync;
+    glColorMask;
+    glColorMaski; # introduced=24
+    glColorMaskiEXT; # introduced=21
+    glCompileShader;
+    glCompressedTexImage2D;
+    glCompressedTexImage3D;
+    glCompressedTexImage3DOES;
+    glCompressedTexSubImage2D;
+    glCompressedTexSubImage3D;
+    glCompressedTexSubImage3DOES;
+    glCopyBufferSubData;
+    glCopyImageSubData; # introduced=24
+    glCopyImageSubDataEXT; # introduced=21
+    glCopyTexImage2D;
+    glCopyTexSubImage2D;
+    glCopyTexSubImage3D;
+    glCopyTexSubImage3DOES;
+    glCreateProgram;
+    glCreateShader;
+    glCreateShaderProgramv; # introduced=21
+    glCullFace;
+    glDebugMessageCallback; # introduced=24
+    glDebugMessageCallbackKHR; # introduced=21
+    glDebugMessageControl; # introduced=24
+    glDebugMessageControlKHR; # introduced=21
+    glDebugMessageInsert; # introduced=24
+    glDebugMessageInsertKHR; # introduced=21
+    glDeleteBuffers;
+    glDeleteFramebuffers;
+    glDeleteProgram;
+    glDeleteProgramPipelines; # introduced=21
+    glDeleteQueries;
+    glDeleteRenderbuffers;
+    glDeleteSamplers;
+    glDeleteShader;
+    glDeleteSync;
+    glDeleteTextures;
+    glDeleteTransformFeedbacks;
+    glDeleteVertexArrays;
+    glDeleteVertexArraysOES;
+    glDepthFunc;
+    glDepthMask;
+    glDepthRangef;
+    glDetachShader;
+    glDisable;
+    glDisableVertexAttribArray;
+    glDisablei; # introduced=24
+    glDisableiEXT; # introduced=21
+    glDispatchCompute; # introduced=21
+    glDispatchComputeIndirect; # introduced=21
+    glDrawArrays;
+    glDrawArraysIndirect; # introduced=21
+    glDrawArraysInstanced;
+    glDrawBuffers;
+    glDrawElements;
+    glDrawElementsBaseVertex; # introduced=24
+    glDrawElementsIndirect; # introduced=21
+    glDrawElementsInstanced;
+    glDrawElementsInstancedBaseVertex; # introduced=24
+    glDrawRangeElements;
+    glDrawRangeElementsBaseVertex; # introduced=24
+    glEGLImageTargetRenderbufferStorageOES;
+    glEGLImageTargetTexture2DOES;
+    glEnable;
+    glEnableVertexAttribArray;
+    glEnablei; # introduced=24
+    glEnableiEXT; # introduced=21
+    glEndQuery;
+    glEndTransformFeedback;
+    glFenceSync;
+    glFinish;
+    glFlush;
+    glFlushMappedBufferRange;
+    glFramebufferParameteri; # introduced=21
+    glFramebufferRenderbuffer;
+    glFramebufferTexture; # introduced=24
+    glFramebufferTexture2D;
+    glFramebufferTexture3DOES;
+    glFramebufferTextureEXT; # introduced=21
+    glFramebufferTextureLayer;
+    glFrontFace;
+    glGenBuffers;
+    glGenFramebuffers;
+    glGenProgramPipelines; # introduced=21
+    glGenQueries;
+    glGenRenderbuffers;
+    glGenSamplers;
+    glGenTextures;
+    glGenTransformFeedbacks;
+    glGenVertexArrays;
+    glGenVertexArraysOES;
+    glGenerateMipmap;
+    glGetActiveAttrib;
+    glGetActiveUniform;
+    glGetActiveUniformBlockName;
+    glGetActiveUniformBlockiv;
+    glGetActiveUniformsiv;
+    glGetAttachedShaders;
+    glGetAttribLocation;
+    glGetBooleani_v; # introduced=21
+    glGetBooleanv;
+    glGetBufferParameteri64v;
+    glGetBufferParameteriv;
+    glGetBufferPointerv;
+    glGetBufferPointervOES;
+    glGetDebugMessageLog; # introduced=24
+    glGetDebugMessageLogKHR; # introduced=21
+    glGetError;
+    glGetFloatv;
+    glGetFragDataLocation;
+    glGetFramebufferAttachmentParameteriv;
+    glGetFramebufferParameteriv; # introduced=21
+    glGetGraphicsResetStatus; # introduced=24
+    glGetInteger64i_v;
+    glGetInteger64v;
+    glGetIntegeri_v;
+    glGetIntegerv;
+    glGetInternalformativ;
+    glGetMultisamplefv; # introduced=21
+    glGetObjectLabel; # introduced=24
+    glGetObjectLabelKHR; # introduced=21
+    glGetObjectPtrLabel; # introduced=24
+    glGetObjectPtrLabelKHR; # introduced=21
+    glGetPointerv; # introduced=24
+    glGetPointervKHR; # introduced=21
+    glGetProgramBinary;
+    glGetProgramBinaryOES;
+    glGetProgramInfoLog;
+    glGetProgramInterfaceiv; # introduced=21
+    glGetProgramPipelineInfoLog; # introduced=21
+    glGetProgramPipelineiv; # introduced=21
+    glGetProgramResourceIndex; # introduced=21
+    glGetProgramResourceLocation; # introduced=21
+    glGetProgramResourceName; # introduced=21
+    glGetProgramResourceiv; # introduced=21
+    glGetProgramiv;
+    glGetQueryObjectuiv;
+    glGetQueryiv;
+    glGetRenderbufferParameteriv;
+    glGetSamplerParameterIiv; # introduced=24
+    glGetSamplerParameterIivEXT; # introduced=21
+    glGetSamplerParameterIuiv; # introduced=24
+    glGetSamplerParameterIuivEXT; # introduced=21
+    glGetSamplerParameterfv;
+    glGetSamplerParameteriv;
+    glGetShaderInfoLog;
+    glGetShaderPrecisionFormat;
+    glGetShaderSource;
+    glGetShaderiv;
+    glGetString;
+    glGetStringi;
+    glGetSynciv;
+    glGetTexLevelParameterfv; # introduced=21
+    glGetTexLevelParameteriv; # introduced=21
+    glGetTexParameterIiv; # introduced=24
+    glGetTexParameterIivEXT; # introduced=21
+    glGetTexParameterIuiv; # introduced=24
+    glGetTexParameterIuivEXT; # introduced=21
+    glGetTexParameterfv;
+    glGetTexParameteriv;
+    glGetTransformFeedbackVarying;
+    glGetUniformBlockIndex;
+    glGetUniformIndices;
+    glGetUniformLocation;
+    glGetUniformfv;
+    glGetUniformiv;
+    glGetUniformuiv;
+    glGetVertexAttribIiv;
+    glGetVertexAttribIuiv;
+    glGetVertexAttribPointerv;
+    glGetVertexAttribfv;
+    glGetVertexAttribiv;
+    glGetnUniformfv; # introduced=24
+    glGetnUniformiv; # introduced=24
+    glGetnUniformuiv; # introduced=24
+    glHint;
+    glInvalidateFramebuffer;
+    glInvalidateSubFramebuffer;
+    glIsBuffer;
+    glIsEnabled;
+    glIsEnabledi; # introduced=24
+    glIsEnablediEXT; # introduced=21
+    glIsFramebuffer;
+    glIsProgram;
+    glIsProgramPipeline; # introduced=21
+    glIsQuery;
+    glIsRenderbuffer;
+    glIsSampler;
+    glIsShader;
+    glIsSync;
+    glIsTexture;
+    glIsTransformFeedback;
+    glIsVertexArray;
+    glIsVertexArrayOES;
+    glLineWidth;
+    glLinkProgram;
+    glMapBufferOES;
+    glMapBufferRange;
+    glMemoryBarrier; # introduced=21
+    glMemoryBarrierByRegion; # introduced=21
+    glMinSampleShading; # introduced=24
+    glMinSampleShadingOES; # introduced=21
+    glObjectLabel; # introduced=24
+    glObjectLabelKHR; # introduced=21
+    glObjectPtrLabel; # introduced=24
+    glObjectPtrLabelKHR; # introduced=21
+    glPatchParameteri; # introduced=24
+    glPatchParameteriEXT; # introduced=21
+    glPauseTransformFeedback;
+    glPixelStorei;
+    glPolygonOffset;
+    glPopDebugGroup; # introduced=24
+    glPopDebugGroupKHR; # introduced=21
+    glPrimitiveBoundingBox; # introduced=24
+    glPrimitiveBoundingBoxEXT; # introduced=21
+    glProgramBinary;
+    glProgramBinaryOES;
+    glProgramParameteri;
+    glProgramUniform1f; # introduced=21
+    glProgramUniform1fv; # introduced=21
+    glProgramUniform1i; # introduced=21
+    glProgramUniform1iv; # introduced=21
+    glProgramUniform1ui; # introduced=21
+    glProgramUniform1uiv; # introduced=21
+    glProgramUniform2f; # introduced=21
+    glProgramUniform2fv; # introduced=21
+    glProgramUniform2i; # introduced=21
+    glProgramUniform2iv; # introduced=21
+    glProgramUniform2ui; # introduced=21
+    glProgramUniform2uiv; # introduced=21
+    glProgramUniform3f; # introduced=21
+    glProgramUniform3fv; # introduced=21
+    glProgramUniform3i; # introduced=21
+    glProgramUniform3iv; # introduced=21
+    glProgramUniform3ui; # introduced=21
+    glProgramUniform3uiv; # introduced=21
+    glProgramUniform4f; # introduced=21
+    glProgramUniform4fv; # introduced=21
+    glProgramUniform4i; # introduced=21
+    glProgramUniform4iv; # introduced=21
+    glProgramUniform4ui; # introduced=21
+    glProgramUniform4uiv; # introduced=21
+    glProgramUniformMatrix2fv; # introduced=21
+    glProgramUniformMatrix2x3fv; # introduced=21
+    glProgramUniformMatrix2x4fv; # introduced=21
+    glProgramUniformMatrix3fv; # introduced=21
+    glProgramUniformMatrix3x2fv; # introduced=21
+    glProgramUniformMatrix3x4fv; # introduced=21
+    glProgramUniformMatrix4fv; # introduced=21
+    glProgramUniformMatrix4x2fv; # introduced=21
+    glProgramUniformMatrix4x3fv; # introduced=21
+    glPushDebugGroup; # introduced=24
+    glPushDebugGroupKHR; # introduced=21
+    glReadBuffer;
+    glReadPixels;
+    glReadnPixels; # introduced=24
+    glReleaseShaderCompiler;
+    glRenderbufferStorage;
+    glRenderbufferStorageMultisample;
+    glResumeTransformFeedback;
+    glSampleCoverage;
+    glSampleMaski; # introduced=21
+    glSamplerParameterIiv; # introduced=24
+    glSamplerParameterIivEXT; # introduced=21
+    glSamplerParameterIuiv; # introduced=24
+    glSamplerParameterIuivEXT; # introduced=21
+    glSamplerParameterf;
+    glSamplerParameterfv;
+    glSamplerParameteri;
+    glSamplerParameteriv;
+    glScissor;
+    glShaderBinary;
+    glShaderSource;
+    glStencilFunc;
+    glStencilFuncSeparate;
+    glStencilMask;
+    glStencilMaskSeparate;
+    glStencilOp;
+    glStencilOpSeparate;
+    glTexBuffer; # introduced=24
+    glTexBufferEXT; # introduced=21
+    glTexBufferRange; # introduced=24
+    glTexBufferRangeEXT; # introduced=21
+    glTexImage2D;
+    glTexImage3D;
+    glTexImage3DOES;
+    glTexParameterIiv; # introduced=24
+    glTexParameterIivEXT; # introduced=21
+    glTexParameterIuiv; # introduced=24
+    glTexParameterIuivEXT; # introduced=21
+    glTexParameterf;
+    glTexParameterfv;
+    glTexParameteri;
+    glTexParameteriv;
+    glTexStorage2D;
+    glTexStorage2DMultisample; # introduced=21
+    glTexStorage3D;
+    glTexStorage3DMultisample; # introduced=24
+    glTexStorage3DMultisampleOES; # introduced=21
+    glTexSubImage2D;
+    glTexSubImage3D;
+    glTexSubImage3DOES;
+    glTransformFeedbackVaryings;
+    glUniform1f;
+    glUniform1fv;
+    glUniform1i;
+    glUniform1iv;
+    glUniform1ui;
+    glUniform1uiv;
+    glUniform2f;
+    glUniform2fv;
+    glUniform2i;
+    glUniform2iv;
+    glUniform2ui;
+    glUniform2uiv;
+    glUniform3f;
+    glUniform3fv;
+    glUniform3i;
+    glUniform3iv;
+    glUniform3ui;
+    glUniform3uiv;
+    glUniform4f;
+    glUniform4fv;
+    glUniform4i;
+    glUniform4iv;
+    glUniform4ui;
+    glUniform4uiv;
+    glUniformBlockBinding;
+    glUniformMatrix2fv;
+    glUniformMatrix2x3fv;
+    glUniformMatrix2x4fv;
+    glUniformMatrix3fv;
+    glUniformMatrix3x2fv;
+    glUniformMatrix3x4fv;
+    glUniformMatrix4fv;
+    glUniformMatrix4x2fv;
+    glUniformMatrix4x3fv;
+    glUnmapBuffer;
+    glUnmapBufferOES;
+    glUseProgram;
+    glUseProgramStages; # introduced=21
+    glValidateProgram;
+    glValidateProgramPipeline; # introduced=21
+    glVertexAttrib1f;
+    glVertexAttrib1fv;
+    glVertexAttrib2f;
+    glVertexAttrib2fv;
+    glVertexAttrib3f;
+    glVertexAttrib3fv;
+    glVertexAttrib4f;
+    glVertexAttrib4fv;
+    glVertexAttribBinding; # introduced=21
+    glVertexAttribDivisor;
+    glVertexAttribFormat; # introduced=21
+    glVertexAttribI4i;
+    glVertexAttribI4iv;
+    glVertexAttribI4ui;
+    glVertexAttribI4uiv;
+    glVertexAttribIFormat; # introduced=21
+    glVertexAttribIPointer;
+    glVertexAttribPointer;
+    glVertexBindingDivisor; # introduced=21
+    glViewport;
+    glWaitSync;
+  local:
+    *;
+};
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 39b4528..3253056 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -126,7 +126,6 @@
 endif
 
 LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
-LOCAL_CFLAGS += -std=c++14
 
 LOCAL_STATIC_LIBRARIES := libtrace_proto libvkjson
 LOCAL_SHARED_LIBRARIES := \
@@ -158,7 +157,6 @@
 LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
 LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
-LOCAL_CPPFLAGS := -std=c++14
 
 LOCAL_INIT_RC := surfaceflinger.rc
 
@@ -202,7 +200,6 @@
 LOCAL_CLANG := true
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
-LOCAL_CPPFLAGS := -std=c++14
 
 LOCAL_SRC_FILES := \
     DdmConnection.cpp
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
new file mode 100644
index 0000000..97d99d0
--- /dev/null
+++ b/vulkan/Android.bp
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+ndk_headers {
+    name: "libvulkan_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/vulkan/**/*.h"],
+}
+
+subdirs = [
+    "libvulkan",
+]
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
new file mode 100644
index 0000000..5e3f4dd
--- /dev/null
+++ b/vulkan/libvulkan/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+// Headers module is in frameworks/native/vulkan/Android.bp.
+ndk_library {
+    name: "libvulkan.ndk",
+    symbol_file: "libvulkan.map.txt",
+    first_version: "24",
+}
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
new file mode 100644
index 0000000..1745925
--- /dev/null
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -0,0 +1,153 @@
+LIBVULKAN {
+  global:
+    vkAcquireNextImageKHR;
+    vkAllocateCommandBuffers;
+    vkAllocateDescriptorSets;
+    vkAllocateMemory;
+    vkBeginCommandBuffer;
+    vkBindBufferMemory;
+    vkBindImageMemory;
+    vkCmdBeginQuery;
+    vkCmdBeginRenderPass;
+    vkCmdBindDescriptorSets;
+    vkCmdBindIndexBuffer;
+    vkCmdBindPipeline;
+    vkCmdBindVertexBuffers;
+    vkCmdBlitImage;
+    vkCmdClearAttachments;
+    vkCmdClearColorImage;
+    vkCmdClearDepthStencilImage;
+    vkCmdCopyBuffer;
+    vkCmdCopyBufferToImage;
+    vkCmdCopyImage;
+    vkCmdCopyImageToBuffer;
+    vkCmdCopyQueryPoolResults;
+    vkCmdDispatch;
+    vkCmdDispatchIndirect;
+    vkCmdDraw;
+    vkCmdDrawIndexed;
+    vkCmdDrawIndexedIndirect;
+    vkCmdDrawIndirect;
+    vkCmdEndQuery;
+    vkCmdEndRenderPass;
+    vkCmdExecuteCommands;
+    vkCmdFillBuffer;
+    vkCmdNextSubpass;
+    vkCmdPipelineBarrier;
+    vkCmdPushConstants;
+    vkCmdResetEvent;
+    vkCmdResetQueryPool;
+    vkCmdResolveImage;
+    vkCmdSetBlendConstants;
+    vkCmdSetDepthBias;
+    vkCmdSetDepthBounds;
+    vkCmdSetEvent;
+    vkCmdSetLineWidth;
+    vkCmdSetScissor;
+    vkCmdSetStencilCompareMask;
+    vkCmdSetStencilReference;
+    vkCmdSetStencilWriteMask;
+    vkCmdSetViewport;
+    vkCmdUpdateBuffer;
+    vkCmdWaitEvents;
+    vkCmdWriteTimestamp;
+    vkCreateAndroidSurfaceKHR;
+    vkCreateBuffer;
+    vkCreateBufferView;
+    vkCreateCommandPool;
+    vkCreateComputePipelines;
+    vkCreateDescriptorPool;
+    vkCreateDescriptorSetLayout;
+    vkCreateDevice;
+    vkCreateEvent;
+    vkCreateFence;
+    vkCreateFramebuffer;
+    vkCreateGraphicsPipelines;
+    vkCreateImage;
+    vkCreateImageView;
+    vkCreateInstance;
+    vkCreatePipelineCache;
+    vkCreatePipelineLayout;
+    vkCreateQueryPool;
+    vkCreateRenderPass;
+    vkCreateSampler;
+    vkCreateSemaphore;
+    vkCreateShaderModule;
+    vkCreateSwapchainKHR;
+    vkDestroyBuffer;
+    vkDestroyBufferView;
+    vkDestroyCommandPool;
+    vkDestroyDescriptorPool;
+    vkDestroyDescriptorSetLayout;
+    vkDestroyDevice;
+    vkDestroyEvent;
+    vkDestroyFence;
+    vkDestroyFramebuffer;
+    vkDestroyImage;
+    vkDestroyImageView;
+    vkDestroyInstance;
+    vkDestroyPipeline;
+    vkDestroyPipelineCache;
+    vkDestroyPipelineLayout;
+    vkDestroyQueryPool;
+    vkDestroyRenderPass;
+    vkDestroySampler;
+    vkDestroySemaphore;
+    vkDestroyShaderModule;
+    vkDestroySurfaceKHR;
+    vkDestroySwapchainKHR;
+    vkDeviceWaitIdle;
+    vkEndCommandBuffer;
+    vkEnumerateDeviceExtensionProperties;
+    vkEnumerateDeviceLayerProperties;
+    vkEnumerateInstanceExtensionProperties;
+    vkEnumerateInstanceLayerProperties;
+    vkEnumeratePhysicalDevices;
+    vkFlushMappedMemoryRanges;
+    vkFreeCommandBuffers;
+    vkFreeDescriptorSets;
+    vkFreeMemory;
+    vkGetBufferMemoryRequirements;
+    vkGetDeviceMemoryCommitment;
+    vkGetDeviceProcAddr;
+    vkGetDeviceQueue;
+    vkGetEventStatus;
+    vkGetFenceStatus;
+    vkGetImageMemoryRequirements;
+    vkGetImageSparseMemoryRequirements;
+    vkGetImageSubresourceLayout;
+    vkGetInstanceProcAddr;
+    vkGetPhysicalDeviceFeatures;
+    vkGetPhysicalDeviceFormatProperties;
+    vkGetPhysicalDeviceImageFormatProperties;
+    vkGetPhysicalDeviceMemoryProperties;
+    vkGetPhysicalDeviceProperties;
+    vkGetPhysicalDeviceQueueFamilyProperties;
+    vkGetPhysicalDeviceSparseImageFormatProperties;
+    vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
+    vkGetPhysicalDeviceSurfaceFormatsKHR;
+    vkGetPhysicalDeviceSurfacePresentModesKHR;
+    vkGetPhysicalDeviceSurfaceSupportKHR;
+    vkGetPipelineCacheData;
+    vkGetQueryPoolResults;
+    vkGetRenderAreaGranularity;
+    vkGetSwapchainImagesKHR;
+    vkInvalidateMappedMemoryRanges;
+    vkMapMemory;
+    vkMergePipelineCaches;
+    vkQueueBindSparse;
+    vkQueuePresentKHR;
+    vkQueueSubmit;
+    vkQueueWaitIdle;
+    vkResetCommandBuffer;
+    vkResetCommandPool;
+    vkResetDescriptorPool;
+    vkResetEvent;
+    vkResetFences;
+    vkSetEvent;
+    vkUnmapMemory;
+    vkUpdateDescriptorSets;
+    vkWaitForFences;
+  local:
+    *;
+};