Merge "Fix fd leak in Binder"
diff --git a/Android.bp b/Android.bp
index 086a2c6..cd05b21 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
     from: "include/android",
     to: "android",
     srcs: ["include/android/**/*.h"],
+    license: "NOTICE",
 }
 
 subdirs = [
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 3db41c2..db61661 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -1,4 +1,6 @@
 cc_library_static {
     name: "libdumpstate.default",
     srcs: ["libdumpstate_default.cpp"],
+    // TODO: should use libdumpstateheaders, but if fails with 'undefined'
+    static_libs: ["libbase", "libziparchive", "libz"]
 }
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 695e464..116650d 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -20,6 +20,25 @@
         liblog \
         libselinux
 
+# ====================#
+# libdumpstateheaders #
+# ====================#
+# TODO: this module is necessary so the device-specific libdumpstate implementations do not
+# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
+LOCAL_MODULE := libdumpstateheaders
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+        $(COMMON_SHARED_LIBRARIES)
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+        $(COMMON_ZIP_LIBRARIES)
+# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
+LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
+LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
+
+include $(BUILD_STATIC_LIBRARY)
+
 # ==========#
 # dumpstate #
 # ==========#
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index c33fc1f..b995b80 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -55,6 +55,10 @@
 - `title.txt`: whose value is a single-line summary of the problem.
 - `description.txt`: whose value is a multi-line, detailed description of the problem.
 
+## Android O versions
+On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made:
+- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`).
+
 ## Intermediate versions
 During development, the versions will be suffixed with _-devX_ or
 _-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
@@ -63,8 +67,8 @@
 For example, the initial version during _Android N_ development was
 **1.0-dev1**. When `dumpsys` was split in 2 sections but not all tools were
 ready to parse that format, the version was named **1.0-dev2**,
-which had to be passed do `dumpsys` explicitly (i.e., trhough a
-`-V 1.0-dev2` argument). Once that format became stable and tools
+which had to be passed to `dumpsys` explicitly (by setting the `dumpstate.version` system property).
+Once that format became stable and tools
 knew how to parse it, the default version became **1.0-dev2**.
 
 Similarly, if changes in the file format are made after the initial release of
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a48f112..740eb19 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "dumpstate"
 
 #include <dirent.h>
 #include <errno.h>
@@ -38,17 +39,15 @@
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/properties.h>
 #include <hardware_legacy/power.h>
 
-#include "private/android_filesystem_config.h"
-
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
 
 #include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
 
 #include <openssl/sha.h>
 
@@ -56,8 +55,8 @@
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
 
-// TODO: variables below should be part of dumpstate object
-static std::unique_ptr<ZipWriter> zip_writer;
+// TODO: variables and functions below should be part of dumpstate object
+
 static std::set<std::string> mount_points;
 void add_mountinfo();
 
@@ -108,6 +107,7 @@
 
 static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
 static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
 
 /* 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. */
@@ -119,7 +119,7 @@
                                          O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
         struct stat st;
         if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
-            (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
+            (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
             data[i].fd = fd;
         } else {
             close(fd);
@@ -146,7 +146,7 @@
     if (mount_points.find(linkname) == mount_points.end()) {
         // First time this mount point was found: add it
         snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
-        if (add_zip_entry(ZIP_ROOT_DIR + path, path)) {
+        if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) {
             mount_points.insert(linkname);
         } else {
             MYLOGE("Unable to add mountinfo %s to zip file\n", path);
@@ -155,10 +155,10 @@
 }
 
 void add_mountinfo() {
-    if (!zip_writer) return;
+    if (!ds.IsZipping()) return;
     std::string title = "MOUNT INFO";
     mount_points.clear();
-    DurationReporter durationReporter(title, nullptr);
+    DurationReporter duration_reporter(title, nullptr);
     for_each_pid(do_mountinfo, nullptr);
     MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
 }
@@ -229,8 +229,8 @@
     long long cur_size = 0;
     const char *trace_path = "/data/misc/anrd/";
 
-    if (!zip_writer) {
-        MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+    if (!ds.IsZipping()) {
+        MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n");
         return false;
     }
 
@@ -301,7 +301,7 @@
                 }
             }
             // Add to the zip file.
-            if (!add_zip_entry("anrd_trace.txt", path)) {
+            if (!ds.AddZipEntry("anrd_trace.txt", path)) {
                 MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
             } else {
                 if (remove(path)) {
@@ -317,8 +317,8 @@
 }
 
 static void dump_systrace() {
-    if (!zip_writer) {
-        MYLOGD("Not dumping systrace because zip_writer is not set\n");
+    if (!ds.IsZipping()) {
+        MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
         return;
     }
     std::string systrace_path = ds.GetPath("-systrace.txt");
@@ -348,7 +348,7 @@
         //   MYLOGE("could not stop systrace ");
         // }
     }
-    if (!add_zip_entry("systrace.txt", systrace_path)) {
+    if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
         MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
     } else {
         if (remove(systrace_path.c_str())) {
@@ -362,9 +362,9 @@
         return;
     }
 
-    std::string raft_log_path = ds.GetPath("-raft_log.txt");
-    if (raft_log_path.empty()) {
-        MYLOGD("raft_log_path is empty\n");
+    std::string raft_path = ds.GetPath("-raft_log.txt");
+    if (raft_path.empty()) {
+        MYLOGD("raft_path is empty\n");
         return;
     }
 
@@ -375,22 +375,97 @@
     }
 
     CommandOptions options = CommandOptions::WithTimeout(600).Build();
-    if (!zip_writer) {
-        // Write compressed and encoded raft logs to stdout if not zip_writer.
+    if (!ds.IsZipping()) {
+        // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport.
         RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
         return;
     }
 
-    RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_log_path}, options);
-    if (!add_zip_entry("raft_log.txt", raft_log_path)) {
-        MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+    RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options);
+    if (!ds.AddZipEntry("raft_log.txt", raft_path)) {
+        MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str());
     } else {
-        if (remove(raft_log_path.c_str())) {
-            MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+        if (remove(raft_path.c_str())) {
+            MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno));
         }
     }
 }
 
+/**
+ * Finds the last modified file in the directory dir whose name starts with file_prefix.
+ *
+ * Function returns empty string when it does not find a file
+ */
+static std::string GetLastModifiedFileWithPrefix(const std::string& dir,
+                                                 const std::string& file_prefix) {
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
+    if (d == nullptr) {
+        MYLOGD("Error %d opening %s\n", errno, dir.c_str());
+        return "";
+    }
+
+    // Find the newest file matching the file_prefix in dir
+    struct dirent *de;
+    time_t last_modified_time = 0;
+    std::string last_modified_file = "";
+    struct stat s;
+
+    while ((de = readdir(d.get()))) {
+        std::string file = std::string(de->d_name);
+        if (!file_prefix.empty()) {
+            if (!android::base::StartsWith(file, file_prefix.c_str())) continue;
+        }
+        file = dir + "/" + file;
+        int ret = stat(file.c_str(), &s);
+
+        if ((ret == 0) && (s.st_mtime > last_modified_time)) {
+            last_modified_file = file;
+            last_modified_time = s.st_mtime;
+        }
+    }
+
+    return last_modified_file;
+}
+
+static void DumpModemLogs() {
+    DurationReporter durationReporter("DUMP MODEM LOGS");
+    if (IsUserBuild()) {
+        return;
+    }
+
+    if (!ds.IsZipping()) {
+        MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
+        return;
+    }
+
+    std::string file_prefix = android::base::GetProperty("ro.radio.log_prefix", "");
+
+    if(file_prefix.empty()) {
+        MYLOGD("No modem log : file_prefix is empty\n");
+        return;
+    }
+
+    // TODO: use bugreport_dir_ directly when this function is moved to Dumpstate class
+    std::string bugreport_dir = dirname(ds.GetPath("").c_str());
+    MYLOGD("DumpModemLogs: directory is %s and file_prefix is %s\n",
+           bugreport_dir.c_str(), file_prefix.c_str());
+
+    std::string modem_log_file = GetLastModifiedFileWithPrefix(bugreport_dir, file_prefix);
+
+    struct stat s;
+    if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
+        MYLOGD("Modem log %s does not exist\n", modem_log_file.c_str());
+        return;
+    }
+
+    std::string filename = basename(modem_log_file.c_str());
+    if (!ds.AddZipEntry(filename, modem_log_file)) {
+        MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
+    } else {
+        MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
+    }
+}
+
 static bool skip_not_stat(const char *path) {
     static const char stat[] = "/stat";
     size_t len = strlen(path);
@@ -574,105 +649,15 @@
     return 0;
 }
 
-/* Copied policy from system/core/logd/LogBuffer.cpp */
-
-#define LOG_BUFFER_SIZE (256 * 1024)
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-
-static bool valid_size(unsigned long value) {
-    if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
-        return false;
-    }
-
-    long pages = sysconf(_SC_PHYS_PAGES);
-    if (pages < 1) {
-        return true;
-    }
-
-    long pagesize = sysconf(_SC_PAGESIZE);
-    if (pagesize <= 1) {
-        pagesize = PAGE_SIZE;
-    }
-
-    // maximum memory impact a somewhat arbitrary ~3%
-    pages = (pages + 31) / 32;
-    unsigned long maximum = pages * pagesize;
-
-    if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
-        return true;
-    }
-
-    return value <= maximum;
-}
-
-// TODO: migrate to logd/LogBuffer.cpp or use android::base::GetProperty
-static unsigned long property_get_size(const char *key) {
-    unsigned long value;
-    char *cp, property[PROPERTY_VALUE_MAX];
-
-    property_get(key, property, "");
-    value = strtoul(property, &cp, 10);
-
-    switch(*cp) {
-    case 'm':
-    case 'M':
-        value *= 1024;
-    /* FALLTHRU */
-    case 'k':
-    case 'K':
-        value *= 1024;
-    /* FALLTHRU */
-    case '\0':
-        break;
-
-    default:
-        value = 0;
-    }
-
-    if (!valid_size(value)) {
-        value = 0;
-    }
-
-    return value;
-}
-
 /* timeout in ms */
 static unsigned long logcat_timeout(const char *name) {
-    static const char global_tuneable[] = "persist.logd.size"; // Settings App
-    static const char global_default[] = "ro.logd.size";       // BoardConfig.mk
-    char key[PROP_NAME_MAX];
-    unsigned long property_size, default_size;
-
-    default_size = property_get_size(global_tuneable);
-    if (!default_size) {
-        default_size = property_get_size(global_default);
-    }
-
-    snprintf(key, sizeof(key), "%s.%s", global_tuneable, name);
-    property_size = property_get_size(key);
-
-    if (!property_size) {
-        snprintf(key, sizeof(key), "%s.%s", global_default, name);
-        property_size = property_get_size(key);
-    }
-
-    if (!property_size) {
-        property_size = default_size;
-    }
-
-    if (!property_size) {
-        property_size = LOG_BUFFER_SIZE;
-    }
-
+    log_id_t id = android_name_to_log_id(name);
+    unsigned long property_size = __android_logger_get_buffer_size(id);
     /* Engineering margin is ten-fold our guess */
     return 10 * (property_size + worst_write_perf) / worst_write_perf;
 }
 
-/* End copy from system/core/logd/LogBuffer.cpp */
-
-// TODO: move to utils.cpp
-void Dumpstate::PrintHeader() {
+void Dumpstate::PrintHeader() const {
     std::string build, fingerprint, radio, bootloader, network;
     char date[80];
 
@@ -696,14 +681,18 @@
     printf("Network: %s\n", network.c_str());
 
     printf("Kernel: ");
-    DumpFile("", "/proc/version");
+    JustDumpFile("", "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
     printf("Bugreport format version: %s\n", version_.c_str());
-    printf("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", id_, getpid(),
-           dryRun_, args_.c_str(), extraOptions_.c_str());
+    printf("Dumpstate info: id=%lu pid=%d dry_run=%d args=%s extra_options=%s\n", id_, getpid(),
+           dry_run_, args_.c_str(), extra_options_.c_str());
     printf("\n");
 }
 
+bool Dumpstate::IsZipping() const {
+    return zip_writer_ != nullptr;
+}
+
 // List of file extensions that can cause a zip file attachment to be rejected by some email
 // service providers.
 static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
@@ -712,10 +701,10 @@
       ".shb", ".sys", ".vb",  ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
 };
 
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
-    if (!zip_writer) {
-        MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
-                entry_name.c_str());
+bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
+    if (!IsZipping()) {
+        MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
+               entry_name.c_str());
         return false;
     }
     std::string valid_name = entry_name;
@@ -733,11 +722,11 @@
 
     // Logging statement  below is useful to time how long each entry takes, but it's too verbose.
     // MYLOGD("Adding zip entry %s\n", entry_name.c_str());
-    int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
-                                                 get_mtime(fd, ds.now_));
-    if (err) {
-        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
-                ZipWriter::ErrorCodeString(err));
+    int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+                                                  get_mtime(fd, ds.now_));
+    if (err != 0) {
+        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
+               ZipWriter::ErrorCodeString(err));
         return false;
     }
 
@@ -750,73 +739,72 @@
             MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
             return false;
         }
-        err = zip_writer->WriteBytes(buffer.data(), bytes_read);
+        err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
         if (err) {
-            MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
+            MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
             return false;
         }
     }
 
-    err = zip_writer->FinishEntry();
-    if (err) {
-        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+    err = zip_writer_->FinishEntry();
+    if (err != 0) {
+        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
     return true;
 }
 
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK
-            | O_CLOEXEC)));
+bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
+    android::base::unique_fd fd(
+        TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
     if (fd == -1) {
         MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
         return false;
     }
 
-    return add_zip_entry_from_fd(entry_name, fd.get());
+    return AddZipEntryFromFd(entry_name, fd.get());
 }
 
 /* adds a file to the existing zipped bugreport */
 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;
+    return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
 }
 
-// TODO: move to util.cpp
-void add_dir(const std::string& dir, bool recursive) {
-    if (!zip_writer) {
-        MYLOGD("Not adding dir %s because zip_writer is not set\n", dir.c_str());
+void Dumpstate::AddDir(const std::string& dir, bool recursive) {
+    if (!IsZipping()) {
+        MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str());
         return;
     }
     MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
-    DurationReporter durationReporter(dir, nullptr);
+    DurationReporter duration_reporter(dir, nullptr);
     dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
 }
 
-/* adds a text entry entry to the existing zip file. */
-static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
-    if (!zip_writer) {
-        MYLOGD("Not adding text zip entry %s because zip_writer is not set\n", entry_name.c_str());
+bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
+    if (!IsZipping()) {
+        MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
+               entry_name.c_str());
         return false;
     }
     MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
-    int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
-    if (err) {
-        MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
-                ZipWriter::ErrorCodeString(err));
+    int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+    if (err != 0) {
+        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+               ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    err = zip_writer->WriteBytes(content.c_str(), content.length());
-    if (err) {
-        MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
-                ZipWriter::ErrorCodeString(err));
+    err = zip_writer_->WriteBytes(content.c_str(), content.length());
+    if (err != 0) {
+        MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
+               ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    err = zip_writer->FinishEntry();
-    if (err) {
-        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+    err = zip_writer_->FinishEntry();
+    if (err != 0) {
+        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
@@ -834,8 +822,89 @@
     RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
 }
 
+static void AddAnrTraceFiles() {
+    bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+    std::string dump_traces_dir;
+
+    /* show the traces we collected in main(), if that was done */
+    if (dump_traces_path != nullptr) {
+        if (add_to_zip) {
+            dump_traces_dir = dirname(dump_traces_path);
+            MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+            ds.AddDir(dump_traces_dir, true);
+        } else {
+            MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+                   dump_traces_path);
+            ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+        }
+    }
+
+    std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+    std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+    // Make sure directory is not added twice.
+    // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+    // generated by dump_traces() -  and anr_traces_path - which is retrieved from a system
+    // property - but in reality they're the same path (although the former could be nullptr).
+    // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+    // be revisited.
+    bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+    MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+           dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+    if (anr_traces_path.empty()) {
+        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+    } else {
+        int fd = TEMP_FAILURE_RETRY(
+            open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+        if (fd < 0) {
+            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+                   strerror(errno));
+        } else {
+            if (add_to_zip) {
+                if (!already_dumped) {
+                    MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+                           anr_traces_dir.c_str());
+                    ds.AddDir(anr_traces_dir, true);
+                    already_dumped = true;
+                }
+            } else {
+                MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+                       anr_traces_path.c_str());
+                dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+            }
+        }
+    }
+
+    if (add_to_zip && already_dumped) {
+        MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+        return;
+    }
+
+    /* slow traces for slow operations */
+    struct stat st;
+    if (!anr_traces_path.empty()) {
+        int tail = anr_traces_path.size() - 1;
+        while (tail > 0 && anr_traces_path.at(tail) != '/') {
+            tail--;
+        }
+        int i = 0;
+        while (1) {
+            anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+                              android::base::StringPrintf("slow%02d.txt", i);
+            if (stat(anr_traces_path.c_str(), &st)) {
+                // No traces file at this index, done with the files.
+                break;
+            }
+            ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+            i++;
+        }
+    }
+}
+
 static void dumpstate() {
-    DurationReporter durationReporter("DUMPSTATE");
+    DurationReporter duration_reporter("DUMPSTATE");
     unsigned long timeout;
 
     dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
@@ -863,7 +932,7 @@
     RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10);
 
     RunCommand("PRINTENV", {"printenv"});
-    RunCommand("NETSTAT", {"netstat", "-n"});
+    RunCommand("NETSTAT", {"netstat", "-nW"});
     struct stat s;
     if (stat("/proc/modules", &s) != 0) {
         MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
@@ -879,9 +948,9 @@
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
 
     /* Dump Bluetooth HCI logs */
-    add_dir("/data/misc/bluetooth/logs", true);
+    ds.AddDir("/data/misc/bluetooth/logs", true);
 
-    if (!ds.doEarlyScreenshot_) {
+    if (!ds.do_early_screenshot_) {
         MYLOGI("taking late screenshot\n");
         ds.TakeScreenshot();
     }
@@ -911,44 +980,7 @@
 
     RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
 
-    /* show the traces we collected in main(), if that was done */
-    if (dump_traces_path != NULL) {
-        DumpFile("VM TRACES JUST NOW", dump_traces_path);
-    }
-
-    /* only show ANR traces if they're less than 15 minutes old */
-    struct stat st;
-    std::string anrTracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
-    if (anrTracesPath.empty()) {
-        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
-    } else {
-        int fd = TEMP_FAILURE_RETRY(
-            open(anrTracesPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
-        if (fd < 0) {
-            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anrTracesPath.c_str(), strerror(errno));
-      } else {
-          dump_file_from_fd("VM TRACES AT LAST ANR", anrTracesPath.c_str(), fd);
-      }
-    }
-
-    /* slow traces for slow operations */
-    if (!anrTracesPath.empty()) {
-        int tail = anrTracesPath.size() - 1;
-        while (tail > 0 && anrTracesPath.at(tail) != '/') {
-            tail--;
-        }
-        int i = 0;
-        while (1) {
-            anrTracesPath =
-                anrTracesPath.substr(0, tail + 1) + android::base::StringPrintf("slow%02d.txt", i);
-            if (stat(anrTracesPath.c_str(), &st)) {
-                // No traces file at this index, done with the files.
-                break;
-            }
-            DumpFile("VM TRACES WHEN SLOW", anrTracesPath.c_str());
-            i++;
-        }
-    }
+    AddAnrTraceFiles();
 
     int dumped = 0;
     for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -956,8 +988,8 @@
             const char *name = tombstone_data[i].name;
             int fd = tombstone_data[i].fd;
             dumped = 1;
-            if (zip_writer) {
-                if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) {
+            if (ds.IsZipping()) {
+                if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
                     MYLOGE("Unable to add tombstone %s to zip file\n", name);
                 }
             } else {
@@ -977,6 +1009,7 @@
     DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
     DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
 
+    struct stat st;
     if (!stat(PSTORE_LAST_KMSG, &st)) {
         /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
         DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
@@ -1120,9 +1153,14 @@
 
     RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
 
+    // DumpModemLogs adds the modem logs if available to the bugreport.
+    // Do this at the end to allow for sufficient time for the modem logs to be
+    // collected.
+    DumpModemLogs();
+
     printf("========================================================\n");
     printf("== Final progress (pid %d): %d/%d (originally %d)\n", getpid(), ds.progress_,
-           ds.weightTotal_, WEIGHT_TOTAL);
+           ds.weight_total_, WEIGHT_TOTAL);
     printf("========================================================\n");
     printf("== dumpstate: done (id %lu)\n", ds.id_);
     printf("========================================================\n");
@@ -1147,8 +1185,7 @@
             "progress (requires -o and -B)\n"
             "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
             "shouldn't be used with -P)\n"
-            "  -V: sets the bugreport format version (valid values: %s)\n",
-            VERSION_DEFAULT.c_str());
+            "  -v: prints the dumpstate header and exit\n");
     exit(exitCode);
 }
 
@@ -1182,11 +1219,10 @@
     sigaction(SIGQUIT, &sa, NULL); // quit
 }
 
-/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
-   temporary file.
- */
-static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
-                            const std::string& log_path) {
+bool Dumpstate::FinishZipFile() {
+    std::string entry_name = base_name_ + "-" + name_ + ".txt";
+    MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
+           tmp_path_.c_str());
     // Final timestamp
     char date[80];
     time_t the_real_now_please_stand_up = time(nullptr);
@@ -1194,38 +1230,41 @@
     MYLOGD("dumpstate id %lu finished around %s (%ld s)\n", ds.id_, date,
            the_real_now_please_stand_up - ds.now_);
 
-    if (!add_zip_entry(bugreport_name, bugreport_path)) {
+    if (!ds.AddZipEntry(entry_name, tmp_path_)) {
         MYLOGE("Failed to add text entry to .zip file\n");
         return false;
     }
-    if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+    if (!AddTextZipEntry("main_entry.txt", entry_name)) {
         MYLOGE("Failed to add main_entry.txt to .zip file\n");
         return false;
     }
 
     // Add log file (which contains stderr output) to zip...
     fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
-    if (!add_zip_entry("dumpstate_log.txt", log_path.c_str())) {
+    if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
         MYLOGE("Failed to add dumpstate log to .zip file\n");
         return false;
     }
     // ... and re-opens it for further logging.
-    redirect_to_existing_file(stderr, const_cast<char*>(log_path.c_str()));
+    redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
     fprintf(stderr, "\n");
 
-    int32_t err = zip_writer->Finish();
-    if (err) {
-        MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+    int32_t err = zip_writer_->Finish();
+    if (err != 0) {
+        MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
+    // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+    ds.zip_file.reset(nullptr);
+
     if (IsUserBuild()) {
-        MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
-        if (remove(bugreport_path.c_str())) {
-            ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
+        MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+        if (remove(tmp_path_.c_str()) != 0) {
+            ALOGW("remove(%s): %s\n", tmp_path_.c_str(), strerror(errno));
         }
     } else {
-        MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+        MYLOGD("Keeping temporary file %s on non-user build\n", tmp_path_.c_str())
     }
 
     return true;
@@ -1276,42 +1315,12 @@
     int do_fb = 0;
     int do_broadcast = 0;
     int is_remote_mode = 0;
-
-    MYLOGI("begin\n");
-
-    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
-        MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
-    } else {
-        MYLOGD("Wake lock acquired.\n");
-        atexit(wake_lock_releaser);
-        register_sig_handler();
-    }
-
-    if (ds.IsDryRun()) {
-        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
-    }
-
-    // TODO: use helper function to convert argv into a string
-    for (int i = 0; i < argc; i++) {
-        ds.args_ += argv[i];
-        if (i < argc - 1) {
-            ds.args_ += " ";
-        }
-    }
-
-    ds.extraOptions_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
-    MYLOGI("Dumpstate args: %s (extra options: %s)\n", ds.args_.c_str(), ds.extraOptions_.c_str());
-
-    /* gets the sequential id */
-    int lastId = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
-    ds.id_ = ++lastId;
-    android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(lastId));
-    MYLOGI("dumpstate id: %lu\n", ds.id_);
+    bool show_header_only = false;
 
     /* set as high priority, and protect from OOM killer */
     setpriority(PRIO_PROCESS, 0, -20);
 
-    FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
+    FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
     if (oom_adj) {
         fputs("-1000", oom_adj);
         fclose(oom_adj);
@@ -1329,18 +1338,18 @@
     while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
         switch (c) {
             // clang-format off
-            case 'd': do_add_date = 1;           break;
-            case 'z': do_zip_file = 1;           break;
-            case 'o': use_outfile = optarg;      break;
-            case 's': use_socket = 1;            break;
-            case 'S': use_control_socket = 1;    break;
-            case 'v':                            break;  // compatibility no-op
-            case 'q': do_vibrate = 0;            break;
-            case 'p': do_fb = 1;                 break;
-            case 'P': ds.updateProgress_ = true; break;
-            case 'R': is_remote_mode = 1;        break;
-            case 'B': do_broadcast = 1;          break;
-            case 'V': ds.version_ = optarg;      break;
+            case 'd': do_add_date = 1;            break;
+            case 'z': do_zip_file = 1;            break;
+            case 'o': use_outfile = optarg;       break;
+            case 's': use_socket = 1;             break;
+            case 'S': use_control_socket = 1;     break;
+            case 'v': show_header_only = true;    break;
+            case 'q': do_vibrate = 0;             break;
+            case 'p': do_fb = 1;                  break;
+            case 'P': ds.update_progress_ = true; break;
+            case 'R': is_remote_mode = 1;         break;
+            case 'B': do_broadcast = 1;           break;
+            case 'V':                             break; // compatibility no-op
             case 'h':
                 ShowUsageAndExit(0);
                 break;
@@ -1351,29 +1360,35 @@
         }
     }
 
-    if (!ds.extraOptions_.empty()) {
+    // TODO: use helper function to convert argv into a string
+    for (int i = 0; i < argc; i++) {
+        ds.args_ += argv[i];
+        if (i < argc - 1) {
+            ds.args_ += " ";
+        }
+    }
+
+    ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
+    if (!ds.extra_options_.empty()) {
         // Framework uses a system property to override some command-line args.
         // Currently, it contains the type of the requested bugreport.
-        if (ds.extraOptions_ == "bugreportplus") {
-            MYLOGD("Running as bugreportplus: add -P, remove -p\n");
-            ds.updateProgress_ = true;
+        if (ds.extra_options_ == "bugreportplus") {
+            ds.update_progress_ = true;
             do_fb = 0;
-        } else if (ds.extraOptions_ == "bugreportremote") {
-            MYLOGD("Running as bugreportremote: add -q -R, remove -p\n");
+        } else if (ds.extra_options_ == "bugreportremote") {
             do_vibrate = 0;
             is_remote_mode = 1;
             do_fb = 0;
-        } else if (ds.extraOptions_ == "bugreportwear") {
-            MYLOGD("Running as bugreportwear: add -P\n");
-            ds.updateProgress_ = true;
+        } else if (ds.extra_options_ == "bugreportwear") {
+            ds.update_progress_ = true;
         } else {
-            MYLOGE("Unknown extra option: %s\n", ds.extraOptions_.c_str());
+            MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
         }
         // Reset the property
         android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
     }
 
-    if ((do_zip_file || do_add_date || ds.updateProgress_ || do_broadcast) && !use_outfile) {
+    if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
         ExitOnInvalidArgs();
     }
 
@@ -1381,21 +1396,55 @@
         ExitOnInvalidArgs();
     }
 
-    if (ds.updateProgress_ && !do_broadcast) {
+    if (ds.update_progress_ && !do_broadcast) {
         ExitOnInvalidArgs();
     }
 
-    if (is_remote_mode && (ds.updateProgress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
+    if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
         ExitOnInvalidArgs();
     }
 
-    if (ds.version_ != VERSION_DEFAULT) {
-        ShowUsageAndExit();
+    if (ds.version_ == VERSION_DEFAULT) {
+        ds.version_ = VERSION_CURRENT;
     }
 
+    if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
+        MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
+               ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
+               VERSION_SPLIT_ANR.c_str());
+        exit(1);
+    }
+
+    if (show_header_only) {
+        ds.PrintHeader();
+        exit(0);
+    }
+
+    /* gets the sequential id */
+    int last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+    ds.id_ = ++last_id;
+    android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
+
+    MYLOGI("begin\n");
+
+    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
+        MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
+    } else {
+        MYLOGD("Wake lock acquired.\n");
+        atexit(wake_lock_releaser);
+        register_sig_handler();
+    }
+
+    if (ds.IsDryRun()) {
+        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
+    }
+
+    MYLOGI("dumpstate info: id=%lu, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
+           ds.extra_options_.c_str());
+
     MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
 
-    ds.doEarlyScreenshot_ = ds.updateProgress_;
+    ds.do_early_screenshot_ = ds.update_progress_;
 
     // If we are going to use a socket, do it as early as possible
     // to avoid timeouts from bugreport.
@@ -1405,42 +1454,30 @@
 
     if (use_control_socket) {
         MYLOGD("Opening control socket\n");
-        ds.controlSocketFd_ = open_socket("dumpstate");
-        ds.updateProgress_ = 1;
+        ds.control_socket_fd_ = open_socket("dumpstate");
+        ds.update_progress_ = 1;
     }
 
-    /* full path of the temporary file containing the bugreport */
-    std::string tmp_path;
-
-    /* full path of the file containing the dumpstate logs */
-    std::string log_path;
-
-    /* pointer to the actual path, be it zip or text */
-    std::string path;
-
-    /* pointer to the zipped file */
-    std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
-
     /* redirect output if needed */
     bool is_redirecting = !use_socket && use_outfile;
 
     if (is_redirecting) {
-        ds.bugreportDir_ = dirname(use_outfile);
-        ds.baseName_ = basename(use_outfile);
+        ds.bugreport_dir_ = dirname(use_outfile);
+        ds.base_name_ = basename(use_outfile);
         if (do_add_date) {
             char date[80];
             strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
-            ds.suffix_ = date;
+            ds.name_ = date;
         } else {
-            ds.suffix_ = "undated";
+            ds.name_ = "undated";
         }
         std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
-        ds.baseName_ = ds.baseName_ + "-" + buildId;
+        ds.base_name_ += "-" + buildId;
         if (do_fb) {
-            ds.screenshotPath_ = ds.GetPath(".png");
+            ds.screenshot_path_ = ds.GetPath(".png");
         }
-        tmp_path = ds.GetPath(".tmp");
-        log_path = ds.GetPath("-dumpstate_log-" + std::to_string(getpid()) + ".txt");
+        ds.tmp_path_ = ds.GetPath(".tmp");
+        ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(getpid()) + ".txt");
 
         MYLOGD(
             "Bugreport dir: %s\n"
@@ -1449,29 +1486,29 @@
             "Log path: %s\n"
             "Temporary path: %s\n"
             "Screenshot path: %s\n",
-            ds.bugreportDir_.c_str(), ds.baseName_.c_str(), ds.suffix_.c_str(), log_path.c_str(),
-            tmp_path.c_str(), ds.screenshotPath_.c_str());
+            ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
+            ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
         if (do_zip_file) {
-            path = ds.GetPath(".zip");
-            MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
-            create_parent_dirs(path.c_str());
-            zip_file.reset(fopen(path.c_str(), "wb"));
-            if (!zip_file) {
-                MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
+            ds.path_ = ds.GetPath(".zip");
+            MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+            create_parent_dirs(ds.path_.c_str());
+            ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+            if (ds.zip_file == nullptr) {
+                MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
                 do_zip_file = 0;
             } else {
-                zip_writer.reset(new ZipWriter(zip_file.get()));
+                ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
             }
-            add_text_zip_entry("version.txt", ds.version_);
+            ds.AddTextZipEntry("version.txt", ds.version_);
         }
 
-        if (ds.updateProgress_) {
+        if (ds.update_progress_) {
             if (do_broadcast) {
                 // clang-format off
                 std::vector<std::string> am_args = {
                      "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
-                     "--es", "android.intent.extra.NAME", ds.suffix_,
+                     "--es", "android.intent.extra.NAME", ds.name_,
                      "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
                      "--ei", "android.intent.extra.PID", std::to_string(getpid()),
                      "--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
@@ -1480,7 +1517,7 @@
                 send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
             }
             if (use_control_socket) {
-                dprintf(ds.controlSocketFd_, "BEGIN:%s\n", path.c_str());
+                dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
             }
         }
     }
@@ -1501,8 +1538,8 @@
         }
     }
 
-    if (do_fb && ds.doEarlyScreenshot_) {
-        if (ds.screenshotPath_.empty()) {
+    if (do_fb && ds.do_early_screenshot_) {
+        if (ds.screenshot_path_.empty()) {
             // should not have happened
             MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
         } else {
@@ -1512,24 +1549,25 @@
     }
 
     if (do_zip_file) {
-        if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
-            MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
+        if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
+            MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
+                   strerror(errno));
         }
     }
 
     if (is_redirecting) {
-        redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
-        if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
+        redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+        if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
-                    log_path.c_str(), strerror(errno));
+                   ds.log_path_.c_str(), strerror(errno));
         }
         /* TODO: rather than generating a text file now and zipping it later,
            it would be more efficient to redirect stdout to the zip entry
            directly, but the libziparchive doesn't support that option yet. */
-        redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
-        if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
+        redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
+        if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
             MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
-                    tmp_path.c_str(), strerror(errno));
+                   ds.tmp_path_.c_str(), strerror(errno));
         }
     }
     // NOTE: there should be no stdout output until now, otherwise it would break the header.
@@ -1559,12 +1597,12 @@
 
     /* Run some operations that require root. */
     get_tombstone_fds(tombstone_data);
-    add_dir(RECOVERY_DIR, true);
-    add_dir(RECOVERY_DATA_DIR, true);
-    add_dir(LOGPERSIST_DATA_DIR, false);
+    ds.AddDir(RECOVERY_DIR, true);
+    ds.AddDir(RECOVERY_DATA_DIR, true);
+    ds.AddDir(LOGPERSIST_DATA_DIR, false);
     if (!IsUserBuild()) {
-        add_dir(PROFILE_DATA_DIR_CUR, true);
-        add_dir(PROFILE_DATA_DIR_REF, true);
+        ds.AddDir(PROFILE_DATA_DIR_CUR, true);
+        ds.AddDir(PROFILE_DATA_DIR_REF, true);
     }
     add_mountinfo();
     dump_iptables();
@@ -1604,57 +1642,57 @@
             }
         }
         if (change_suffix) {
-            MYLOGI("changing suffix from %s to %s\n", ds.suffix_.c_str(), name.c_str());
-            ds.suffix_ = name;
-            if (!ds.screenshotPath_.empty()) {
-                std::string newScreenshotPath = ds.GetPath(".png");
-                if (rename(ds.screenshotPath_.c_str(), newScreenshotPath.c_str())) {
-                    MYLOGE("rename(%s, %s): %s\n", ds.screenshotPath_.c_str(),
-                           newScreenshotPath.c_str(), strerror(errno));
+            MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
+            ds.name_ = name;
+            if (!ds.screenshot_path_.empty()) {
+                std::string new_screenshot_path = ds.GetPath(".png");
+                if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
+                    MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
+                           new_screenshot_path.c_str(), strerror(errno));
                 } else {
-                    ds.screenshotPath_ = newScreenshotPath;
+                    ds.screenshot_path_ = new_screenshot_path;
                 }
             }
         }
 
         bool do_text_file = true;
         if (do_zip_file) {
-            std::string entry_name = ds.baseName_ + "-" + ds.suffix_ + ".txt";
-            MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
-            if (!finish_zip_file(entry_name, tmp_path, log_path)) {
+            if (!ds.FinishZipFile()) {
                 MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
                 do_text_file = true;
             } else {
                 do_text_file = false;
                 // Since zip file is already created, it needs to be renamed.
                 std::string new_path = ds.GetPath(".zip");
-                if (path != new_path) {
-                    MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
-                    if (rename(path.c_str(), new_path.c_str())) {
-                        MYLOGE("rename(%s, %s): %s\n", path.c_str(),
-                                new_path.c_str(), strerror(errno));
+                if (ds.path_ != new_path) {
+                    MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+                    if (rename(ds.path_.c_str(), new_path.c_str())) {
+                        MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+                               strerror(errno));
                     } else {
-                        path = new_path;
+                        ds.path_ = new_path;
                     }
                 }
             }
         }
         if (do_text_file) {
-            path = ds.GetPath(".txt");
-            MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
-            if (rename(tmp_path.c_str(), path.c_str())) {
-                MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
-                path.clear();
+            ds.path_ = ds.GetPath(".txt");
+            MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
+                   ds.tmp_path_.c_str());
+            if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
+                MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
+                       strerror(errno));
+                ds.path_.clear();
             }
         }
         if (use_control_socket) {
             if (do_text_file) {
-                dprintf(ds.controlSocketFd_,
+                dprintf(ds.control_socket_fd_,
                         "FAIL:could not create zip file, check %s "
                         "for more details\n",
-                        log_path.c_str());
+                        ds.log_path_.c_str());
             } else {
-                dprintf(ds.controlSocketFd_, "OK:%s\n", path.c_str());
+                dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
             }
         }
     }
@@ -1669,27 +1707,27 @@
 
     /* tell activity manager we're done */
     if (do_broadcast) {
-        if (!path.empty()) {
-            MYLOGI("Final bugreport path: %s\n", path.c_str());
+        if (!ds.path_.empty()) {
+            MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
             // clang-format off
             std::vector<std::string> am_args = {
                  "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
                  "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
                  "--ei", "android.intent.extra.PID", std::to_string(getpid()),
-                 "--ei", "android.intent.extra.MAX", std::to_string(ds.weightTotal_),
-                 "--es", "android.intent.extra.BUGREPORT", path,
-                 "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
+                 "--ei", "android.intent.extra.MAX", std::to_string(ds.weight_total_),
+                 "--es", "android.intent.extra.BUGREPORT", ds.path_,
+                 "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
             };
             // clang-format on
             if (do_fb) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.SCREENSHOT");
-                am_args.push_back(ds.screenshotPath_);
+                am_args.push_back(ds.screenshot_path_);
             }
             if (is_remote_mode) {
                 am_args.push_back("--es");
                 am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
-                am_args.push_back(SHA256_file_hash(path));
+                am_args.push_back(SHA256_file_hash(ds.path_));
                 send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
             } else {
                 send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
@@ -1699,16 +1737,16 @@
         }
     }
 
-    MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weightTotal_, WEIGHT_TOTAL);
+    MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weight_total_, WEIGHT_TOTAL);
     MYLOGI("done (id %lu)\n", ds.id_);
 
     if (is_redirecting) {
         fclose(stderr);
     }
 
-    if (use_control_socket && ds.controlSocketFd_ != -1) {
+    if (use_control_socket && ds.control_socket_fd_ != -1) {
         MYLOGD("Closing control socket\n");
-        close(ds.controlSocketFd_);
+        close(ds.control_socket_fd_);
     }
 
     return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 54d91f7..950f185 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -37,6 +37,9 @@
 #include <string>
 #include <vector>
 
+#include <android-base/macros.h>
+#include <ziparchive/zip_writer.h>
+
 // Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
 // std::vector<std::string>
 // TODO: remove once not used
@@ -92,6 +95,8 @@
     std::string title_;
     FILE* out_;
     uint64_t started_;
+
+    DISALLOW_COPY_AND_ASSIGN(DurationReporter);
 };
 
 /*
@@ -119,9 +124,9 @@
 
         long timeout_;
         bool always_;
-        RootMode rootMode_;
-        StdoutMode stdoutMode_;
-        std::string loggingMessage_;
+        RootMode root_mode_;
+        StdoutMode stdout_mode_;
+        std::string logging_message_;
 
         friend class CommandOptions;
         friend class CommandOptionsBuilder;
@@ -129,7 +134,7 @@
 
     CommandOptions(const CommandOptionsValues& values);
 
-    const CommandOptionsValues values_;
+    const CommandOptionsValues values;
 
   public:
     class CommandOptionsBuilder {
@@ -150,7 +155,7 @@
 
       private:
         CommandOptionsBuilder(long timeout);
-        CommandOptionsValues values_;
+        CommandOptionsValues values;
         friend class CommandOptions;
     };
 
@@ -197,7 +202,18 @@
  *
  * See bugreport-format.md for more info.
  */
-static std::string VERSION_DEFAULT = "1.0";
+static std::string VERSION_CURRENT = "1.0";
+
+/*
+ * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version
+ * will be bumped to 2.0-dev-1.
+ */
+static std::string VERSION_SPLIT_ANR = "2.0-dev-1";
+
+/*
+ * "Alias" for the current version.
+ */
+static std::string VERSION_DEFAULT = "default";
 
 /*
  * Main class driving a bugreport generation.
@@ -218,12 +234,15 @@
      *
      * Dry-run mode is enabled by setting the system property dumpstate.dry_run to true.
      */
-    bool IsDryRun();
+    bool IsDryRun() const;
 
     /*
      * Gets whether device is running a `user` build.
      */
-    bool IsUserBuild();
+    bool IsUserBuild() const;
+
+    /* Checkes whether dumpstate is generating a zipped bugreport. */
+    bool IsZipping() const;
 
     /*
      * Forks a command, waits for it to finish, and returns its status.
@@ -246,12 +265,12 @@
      * description).
      * |dumpsys_args| `dumpsys` arguments (except `-t`).
      * |options| optional argument defining the command's behavior.
-     * |dumpsysTimeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
+     * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
      * timeout from `options`)
      */
-    void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+    void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
                     const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
-                    long dumpsysTimeout = 0);
+                    long dumpsys_timeout = 0);
 
     /*
      * Prints the contents of a file.
@@ -263,6 +282,26 @@
     int DumpFile(const std::string& title, const std::string& path);
 
     /*
+     * Adds a new entry to the existing zip file.
+     * */
+    bool AddZipEntry(const std::string& entry_name, const std::string& entry_path);
+
+    /*
+     * Adds a new entry to the existing zip file.
+     */
+    bool AddZipEntryFromFd(const std::string& entry_name, int fd);
+
+    /*
+     * Adds a text entry entry to the existing zip file.
+     */
+    bool AddTextZipEntry(const std::string& entry_name, const std::string& content);
+
+    /*
+     * Adds all files from a directory to the zipped bugreport file.
+     */
+    void AddDir(const std::string& dir, bool recursive);
+
+    /*
      * Takes a screenshot and save it to the given `path`.
      *
      * If `path` is empty, uses a standard path based on the bugreport name.
@@ -277,10 +316,16 @@
     void UpdateProgress(int delta);
 
     /* Prints the dumpstate header on `stdout`. */
-    void PrintHeader();
+    void PrintHeader() const;
+
+    /*
+     * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the
+     * temporary file.
+     */
+    bool FinishZipFile();
 
     /* Gets the path of a bugreport file with the given suffix. */
-    std::string GetPath(const std::string& suffix);
+    std::string GetPath(const std::string& suffix) const;
 
     // TODO: initialize fields on constructor
 
@@ -288,53 +333,79 @@
     unsigned long id_;
 
     // Whether progress updates should be published.
-    bool updateProgress_ = false;
+    bool update_progress_ = false;
 
     // Whether it should take an screenshot earlier in the process.
-    bool doEarlyScreenshot_ = false;
+    bool do_early_screenshot_ = false;
 
     // Currrent progress.
     int progress_ = 0;
 
     // Total estimated progress.
-    int weightTotal_ = WEIGHT_TOTAL;
+    int weight_total_ = WEIGHT_TOTAL;
 
     // When set, defines a socket file-descriptor use to report progress to bugreportz.
-    int controlSocketFd_ = -1;
+    int control_socket_fd_ = -1;
 
     // Bugreport format version;
-    std::string version_ = VERSION_DEFAULT;
+    std::string version_ = VERSION_CURRENT;
 
     // Command-line arguments as string
     std::string args_;
 
     // Extra options passed as system property.
-    std::string extraOptions_;
+    std::string extra_options_;
 
     // Full path of the directory where the bugreport files will be written.
-    std::string bugreportDir_;
+    std::string bugreport_dir_;
 
     // Full path of the temporary file containing the screenshot (when requested).
-    std::string screenshotPath_;
+    std::string screenshot_path_;
 
     time_t now_;
 
-    // Suffix of the bugreport files - it's typically the date (when invoked with -d),
-    // although it could be changed by the user using a system property.
-    std::string suffix_;
+    // Base name (without suffix or extensions) of the bugreport files, typically
+    // `bugreport-BUILD_ID`.
+    std::string base_name_;
 
-    // Base name (without suffix or extensions) of the bugreport files.
-    std::string baseName_;
+    // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
+    // `-d`), but it could be changed by the user..
+    std::string name_;
+
+    // Full path of the temporary file containing the bugreport.
+    std::string tmp_path_;
+
+    // Full path of the file containing the dumpstate logs.
+    std::string log_path_;
+
+    // Pointer to the actual path, be it zip or text.
+    std::string path_;
+
+    // Pointer to the zipped file.
+    std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose};
+
+    // Pointer to the zip structure.
+    std::unique_ptr<ZipWriter> zip_writer_;
 
   private:
     // Used by GetInstance() only.
-    Dumpstate(bool dryRun = false, const std::string& buildType = "user");
+    Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false,
+              const std::string& build_type = "user");
+
+    // Internal version of RunCommand that just runs it, without updating progress.
+    int JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
+                       const CommandOptions& options) const;
+
+    // Internal version of RunCommand that just dumps it, without updating progress.
+    int JustDumpFile(const std::string& title, const std::string& path) const;
 
     // Whether this is a dry run.
-    bool dryRun_;
+    bool dry_run_;
 
     // Build type (such as 'user' or 'eng').
-    std::string buildType_;
+    std::string build_type_;
+
+    DISALLOW_COPY_AND_ASSIGN(Dumpstate);
 };
 
 // for_each_pid_func = void (*)(int, const char*);
@@ -343,15 +414,6 @@
 typedef void(for_each_pid_func)(int, const char*);
 typedef void(for_each_tid_func)(int, int, const char*);
 
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
-
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
-
-/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const std::string& dir, bool recursive);
-
 /* saves the the contents of a file as a long */
 int read_file_as_long(const char *path, long int *output);
 
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/libdumpstate_default.cpp
index fd840df..f3997ab 100644
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ b/cmds/dumpstate/libdumpstate_default.cpp
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include "dumpstate.h"
 
 void dumpstate_board(void)
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 8d70704..4073c3c 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -52,15 +52,15 @@
     void SetUp() {
         SetDryRun(false);
         SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
-        ds.updateProgress_ = false;
+        ds.update_progress_ = false;
     }
 
     // Runs a command and capture `stdout` and `stderr`.
-    int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+    int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                    const CommandOptions& options = CommandOptions::DEFAULT) {
         CaptureStdout();
         CaptureStderr();
-        int status = ds.RunCommand(title, fullCommand, options);
+        int status = ds.RunCommand(title, full_command, options);
         out = GetCapturedStdout();
         err = GetCapturedStderr();
         return status;
@@ -76,14 +76,14 @@
         return status;
     }
 
-    void SetDryRun(bool dryRun) {
-        ALOGD("Setting dryRun_ to %s\n", dryRun ? "true" : "false");
-        ds.dryRun_ = dryRun;
+    void SetDryRun(bool dry_run) {
+        ALOGD("Setting dry_run_ to %s\n", dry_run ? "true" : "false");
+        ds.dry_run_ = dry_run;
     }
 
-    void SetBuildType(const std::string& buildType) {
-        ALOGD("Setting buildType_ to '%s'\n", buildType.c_str());
-        ds.buildType_ = buildType;
+    void SetBuildType(const std::string& build_type) {
+        ALOGD("Setting build_type_ to '%s'\n", build_type.c_str());
+        ds.build_type_ = build_type;
     }
 
     bool IsUserBuild() {
@@ -97,41 +97,41 @@
     }
 
     // TODO: remove when progress is set by Binder callbacks.
-    void AssertSystemProperty(const std::string& key, const std::string& expectedValue) {
+    void AssertSystemProperty(const std::string& key, const std::string& expected_value) {
         std::string actualValue = android::base::GetProperty(key, "not set");
-        EXPECT_THAT(expectedValue, StrEq(actualValue)) << "invalid value for property " << key;
+        EXPECT_THAT(expected_value, StrEq(actualValue)) << "invalid value for property " << key;
     }
 
-    std::string GetProgressMessage(int progress, int weightTotal, int oldWeightTotal = 0) {
+    std::string GetProgressMessage(int progress, int weigh_total, int old_weigh_total = 0) {
         EXPECT_EQ(progress, ds.progress_) << "invalid progress";
-        EXPECT_EQ(weightTotal, ds.weightTotal_) << "invalid weightTotal";
+        EXPECT_EQ(weigh_total, ds.weight_total_) << "invalid weigh_total";
 
         AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
                              std::to_string(progress));
 
-        bool maxIncreased = oldWeightTotal > 0;
+        bool max_increased = old_weigh_total > 0;
 
-        std::string adjustmentMessage = "";
-        if (maxIncreased) {
+        std::string adjustment_message = "";
+        if (max_increased) {
             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);
+                                 std::to_string(weigh_total));
+            adjustment_message = android::base::StringPrintf(
+                "Adjusting total weight from %d to %d\n", old_weigh_total, weigh_total);
         }
 
         return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
-                                           adjustmentMessage.c_str(), getpid(), progress,
-                                           weightTotal);
+                                           adjustment_message.c_str(), getpid(), progress,
+                                           weigh_total);
     }
 
     // `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";
+    std::string test_path = dirname(android::base::GetExecutablePath().c_str());
+    std::string fixtures_path = test_path + "/../dumpstate_test_fixture/";
+    std::string test_data_path = fixtures_path + "/testdata/";
+    std::string simple_command = fixtures_path + "dumpstate_test_fixture";
+    std::string echo_command = "/system/bin/echo";
 
     Dumpstate& ds = Dumpstate::GetInstance();
 };
@@ -141,52 +141,52 @@
 }
 
 TEST_F(DumpstateTest, RunCommandNoTitle) {
-    EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+    EXPECT_EQ(0, RunCommand("", {simple_command}));
     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_EQ(0, RunCommand("I AM GROOT", {simple_command}));
     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------"));
+                StartsWith("------ I AM GROOT (" + simple_command + ") ------\nstdout\n------"));
     EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
     EXPECT_EQ(
-        0, RunCommand("", {simpleCommand},
+        0, RunCommand("", {simple_command},
                       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},
+    EXPECT_EQ(0, RunCommand("", {simple_command},
                             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_EQ(0, RunCommand("", {echo_command, "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_EQ(0, RunCommand("", {echo_command, "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}));
+    EXPECT_EQ(0, RunCommand("I AM GROOT", {simple_command}));
     // We don't know the exact duration, so we check the prefix and suffix
-    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simpleCommand +
+    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simple_command +
                                 ") ------\n\t(skipped on dry run)\n------"));
     EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
     EXPECT_THAT(err, IsEmpty());
@@ -194,14 +194,14 @@
 
 TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
     SetDryRun(true);
-    EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+    EXPECT_EQ(0, RunCommand("", {simple_command}));
     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_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Always().Build()));
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n"));
 }
@@ -213,28 +213,28 @@
 }
 
 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"));
+    EXPECT_EQ(42, RunCommand("", {simple_command, "--exit", "42"}));
+    EXPECT_THAT(out, StrEq("stdout\n*** command '" + simple_command +
+                           " --exit 42' failed: exit code 42\n"));
+    EXPECT_THAT(err, StrEq("stderr\n*** command '" + simple_command +
+                           " --exit 42' failed: exit code 42\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandCrashes) {
-    EXPECT_NE(0, RunCommand("", {simpleCommand, "--crash"}));
+    EXPECT_NE(0, RunCommand("", {simple_command, "--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"));
+        out, StartsWith("stdout\n*** command '" + simple_command + " --crash' failed: exit code"));
     EXPECT_THAT(
-        err, StartsWith("stderr\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+        err, StartsWith("stderr\n*** command '" + simple_command + " --crash' failed: exit code"));
 }
 
 TEST_F(DumpstateTest, RunCommandTimesout) {
-    EXPECT_EQ(-1, RunCommand("", {simpleCommand, "--sleep", "2"},
+    EXPECT_EQ(-1, RunCommand("", {simple_command, "--sleep", "2"},
                              CommandOptions::WithTimeout(1).Build()));
-    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simpleCommand +
+    EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simple_command +
                                 " --sleep 2' timed out after 1"));
-    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simpleCommand +
+    EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simple_command +
                                 " --sleep 2' timed out after 1"));
 }
 
@@ -243,7 +243,7 @@
     CaptureStderr();
 
     std::thread t([=]() {
-        EXPECT_EQ(SIGTERM, ds.RunCommand("", {simpleCommand, "--pid", "--sleep", "20"},
+        EXPECT_EQ(SIGTERM, ds.RunCommand("", {simple_command, "--pid", "--sleep", "20"},
                                          CommandOptions::WithTimeout(100).Always().Build()));
     });
 
@@ -270,53 +270,53 @@
     out = GetCapturedStdout();
     err = GetCapturedStderr();
 
-    EXPECT_THAT(out, StrEq("*** command '" + simpleCommand +
+    EXPECT_THAT(out, StrEq("*** command '" + simple_command +
                            " --pid --sleep 20' failed: killed by signal 15\n"));
-    EXPECT_THAT(err, StrEq("*** command '" + simpleCommand +
+    EXPECT_THAT(err, StrEq("*** command '" + simple_command +
                            " --pid --sleep 20' failed: killed by signal 15\n"));
 }
 
 TEST_F(DumpstateTest, RunCommandProgress) {
-    ds.updateProgress_ = true;
+    ds.update_progress_ = true;
     ds.progress_ = 0;
-    ds.weightTotal_ = 30;
+    ds.weight_total_ = 30;
 
-    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(20).Build()));
-    std::string progressMessage = GetProgressMessage(20, 30);
+    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
+    std::string progress_message = GetProgressMessage(20, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
-    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(10).Build()));
-    progressMessage = GetProgressMessage(30, 30);
+    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
+    progress_message = GetProgressMessage(30, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     // 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_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
+    progress_message = GetProgressMessage(31, 36, 30);  // 20% increase
     EXPECT_THAT(out, StrEq("stdout\n"));
-    EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+    EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
-    // Make sure command ran while in dryRun is counted.
+    // Make sure command ran while in dry_run is counted.
     SetDryRun(true);
-    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(4).Build()));
-    progressMessage = GetProgressMessage(35, 36);
+    EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
+    progress_message = GetProgressMessage(35, 36);
     EXPECT_THAT(out, IsEmpty());
-    EXPECT_THAT(err, StrEq(progressMessage));
+    EXPECT_THAT(err, StrEq(progress_message));
 }
 
 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_EQ(0, RunCommand("", {simple_command, "--uid"}));
         EXPECT_THAT(out, StrEq("0\nstdout\n"));
         EXPECT_THAT(err, StrEq("stderr\n"));
         return;
     }
     // Then drop root.
 
-    EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"},
+    EXPECT_EQ(0, RunCommand("", {simple_command, "--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"));
@@ -330,11 +330,11 @@
 
     DropRoot();
 
-    EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+    EXPECT_EQ(0, RunCommand("", {simple_command}, 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(out, EndsWith("root " + simple_command + "' on user build.\n"));
     EXPECT_THAT(err, IsEmpty());
 }
 
@@ -346,7 +346,7 @@
 
     DropRoot();
 
-    EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"},
+    EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"},
                             CommandOptions::WithTimeout(1).AsRoot().Build()));
 
     EXPECT_THAT(out, StrEq("0\nstdout\n"));
@@ -370,55 +370,55 @@
 }
 
 TEST_F(DumpstateTest, DumpFileSingleLine) {
-    EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+    EXPECT_EQ(0, DumpFile("", test_data_path + "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_EQ(0, DumpFile("", test_data_path + "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_EQ(0, DumpFile("", test_data_path + "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_EQ(0, DumpFile("", test_data_path + "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_EQ(0, DumpFile("", test_data_path + "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_EQ(0, DumpFile("Might as well dump. Dump!", test_data_path + "single-line.txt"));
     EXPECT_THAT(err, IsEmpty());
-    EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + testDataPath +
+    EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + test_data_path +
                                 "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.update_progress_ = true;
     ds.progress_ = 0;
-    ds.weightTotal_ = 30;
+    ds.weight_total_ = 30;
 
-    EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+    EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
 
-    std::string progressMessage = GetProgressMessage(5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
+    std::string progress_message = GetProgressMessage(5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
 
-    EXPECT_THAT(err, StrEq(progressMessage));
+    EXPECT_THAT(err, StrEq(progress_message));
     EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
 }
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 83fcf21..c41cca4 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -17,29 +17,31 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <libgen.h>
 #include <limits.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string>
 #include <string.h>
 #include <sys/capability.h>
 #include <sys/inotify.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/wait.h>
-#include <sys/klog.h>
 #include <time.h>
 #include <unistd.h>
+#include <string>
 #include <vector>
-#include <sys/prctl.h>
 
 #define LOG_TAG "dumpstate"
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <cutils/debugger.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
@@ -58,9 +60,9 @@
 
 // TODO: temporary variables and functions used during C++ refactoring
 static Dumpstate& ds = Dumpstate::GetInstance();
-static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                       const CommandOptions& options = CommandOptions::DEFAULT) {
-    return ds.RunCommand(title, fullCommand, options);
+    return ds.RunCommand(title, full_command, options);
 }
 static bool IsDryRun() {
     return Dumpstate::GetInstance().IsDryRun();
@@ -94,82 +96,83 @@
 CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
 CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
 
-CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values_(timeout) {
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values(timeout) {
 }
 
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
-    values_.always_ = true;
+    values.always_ = true;
     return *this;
 }
 
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
-    values_.rootMode_ = SU_ROOT;
+    values.root_mode_ = SU_ROOT;
     return *this;
 }
 
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
-    values_.rootMode_ = DROP_ROOT;
+    values.root_mode_ = DROP_ROOT;
     return *this;
 }
 
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
-    values_.stdoutMode_ = REDIRECT_TO_STDERR;
+    values.stdout_mode_ = REDIRECT_TO_STDERR;
     return *this;
 }
 
 CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
     const std::string& message) {
-    values_.loggingMessage_ = message;
+    values.logging_message_ = message;
     return *this;
 }
 
 CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
-    return CommandOptions(values_);
+    return CommandOptions(values);
 }
 
 CommandOptions::CommandOptionsValues::CommandOptionsValues(long timeout)
     : timeout_(timeout),
       always_(false),
-      rootMode_(DONT_DROP_ROOT),
-      stdoutMode_(NORMAL_STDOUT),
-      loggingMessage_("") {
+      root_mode_(DONT_DROP_ROOT),
+      stdout_mode_(NORMAL_STDOUT),
+      logging_message_("") {
 }
 
-CommandOptions::CommandOptions(const CommandOptionsValues& values) : values_(values) {
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
 }
 
 long CommandOptions::Timeout() const {
-    return values_.timeout_;
+    return values.timeout_;
 }
 
 bool CommandOptions::Always() const {
-    return values_.always_;
+    return values.always_;
 }
 
 RootMode CommandOptions::RootMode() const {
-    return values_.rootMode_;
+    return values.root_mode_;
 }
 
 StdoutMode CommandOptions::StdoutMode() const {
-    return values_.stdoutMode_;
+    return values.stdout_mode_;
 }
 
 std::string CommandOptions::LoggingMessage() const {
-    return values_.loggingMessage_;
+    return values.logging_message_;
 }
 
 CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
     return CommandOptions::CommandOptionsBuilder(timeout);
 }
 
-Dumpstate::Dumpstate(bool dryRun, const std::string& buildType)
-    : now_(time(nullptr)), dryRun_(dryRun), buildType_(buildType) {
+Dumpstate::Dumpstate(const std::string& version, bool dry_run, const std::string& build_type)
+    : version_(version), now_(time(nullptr)), dry_run_(dry_run), build_type_(build_type) {
 }
 
 Dumpstate& Dumpstate::GetInstance() {
-    static Dumpstate sSingleton(android::base::GetBoolProperty("dumpstate.dry_run", false),
+    static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT),
+                                android::base::GetBoolProperty("dumpstate.dry_run", false),
                                 android::base::GetProperty("ro.build.type", "(unknown)"));
-    return sSingleton;
+    return singleton_;
 }
 
 DurationReporter::DurationReporter(const std::string& title) : DurationReporter(title, stdout) {
@@ -200,16 +203,17 @@
     return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
 }
 
-bool Dumpstate::IsDryRun() {
-    return dryRun_;
+bool Dumpstate::IsDryRun() const {
+    return dry_run_;
 }
 
-bool Dumpstate::IsUserBuild() {
-    return "user" == buildType_;
+bool Dumpstate::IsUserBuild() const {
+    return "user" == build_type_;
 }
 
-std::string Dumpstate::GetPath(const std::string& suffix) {
-    return bugreportDir_ + "/" + baseName_ + "-" + suffix_ + suffix;
+std::string Dumpstate::GetPath(const std::string& suffix) const {
+    return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(),
+                                       name_.c_str(), suffix.c_str());
 }
 
 void for_each_userid(void (*func)(int), const char *header) {
@@ -525,6 +529,7 @@
     RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT_10);
 }
 
+// TODO: when converted to a Dumpstate function, it should be const
 static int _dump_file_from_fd(const std::string& title, const char* path, int fd) {
     if (!title.empty()) {
         printf("------ %s (%s", title.c_str(), path);
@@ -543,11 +548,6 @@
         }
         printf(") ------\n");
     }
-    if (IsDryRun()) {
-        UpdateProgress(WEIGHT_FILE);
-        close(fd);
-        return 0;
-    }
 
     bool newline = false;
     fd_set read_set;
@@ -594,7 +594,7 @@
 }
 
 int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
-    DurationReporter durationReporter(title);
+    DurationReporter duration_reporter(title);
     if (IsDryRun()) {
         if (!title.empty()) {
             printf("------ %s (%s) ------\n", title.c_str(), path.c_str());
@@ -603,7 +603,10 @@
         UpdateProgress(WEIGHT_FILE);
         return 0;
     }
+    return JustDumpFile(title, path);
+}
 
+int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) const {
     int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
     if (fd < 0) {
         int err = errno;
@@ -772,41 +775,41 @@
     return true;
 }
 
-int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
                           const CommandOptions& options) {
-    if (fullCommand.empty()) {
+    if (full_command.empty()) {
         MYLOGE("No arguments on command '%s'\n", title.c_str());
         return -1;
     }
 
-    int size = fullCommand.size() + 1;  // null terminated
-    int startingIndex = 0;
+    int size = full_command.size() + 1;  // null terminated
+    int starting_index = 0;
     if (options.RootMode() == SU_ROOT) {
-        startingIndex = 2;  // "su" "root"
-        size += startingIndex;
+        starting_index = 2;  // "su" "root"
+        size += starting_index;
     }
 
     std::vector<const char*> args;
     args.resize(size);
 
-    std::string commandString;
+    std::string command_string;
     if (options.RootMode() == SU_ROOT) {
         args[0] = SU_PATH;
-        commandString += SU_PATH;
+        command_string += SU_PATH;
         args[1] = "root";
-        commandString += " root ";
+        command_string += " root ";
     }
-    int i = startingIndex;
-    for (auto arg = fullCommand.begin(); arg != fullCommand.end(); ++arg) {
+    int i = starting_index;
+    for (auto arg = full_command.begin(); arg != full_command.end(); ++arg) {
         args[i++] = arg->c_str();
-        commandString += arg->c_str();
-        if (arg != fullCommand.end() - 1) {
-            commandString += " ";
+        command_string += arg->c_str();
+        if (arg != full_command.end() - 1) {
+            command_string += " ";
         }
     }
     args[i] = nullptr;
     const char* path = args[0];
-    const char* command = commandString.c_str();
+    const char* command = command_string.c_str();
 
     if (options.RootMode() == SU_ROOT && ds.IsUserBuild()) {
         printf("Skipping '%s' on user build.\n", command);
@@ -818,11 +821,11 @@
     }
 
     fflush(stdout);
-    DurationReporter durationReporter(title);
+    DurationReporter duration_reporter(title);
 
-    const std::string& loggingMessage = options.LoggingMessage();
-    if (!loggingMessage.empty()) {
-        MYLOGI(loggingMessage.c_str(), commandString.c_str());
+    const std::string& logging_message = options.LoggingMessage();
+    if (!logging_message.empty()) {
+        MYLOGI(logging_message.c_str(), command_string.c_str());
     }
 
     if (IsDryRun() && !options.Always()) {
@@ -833,7 +836,7 @@
         return 0;
     }
 
-    bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
+    int status = JustRunCommand(command, path, args, options);
 
     /* TODO: for now we're simplifying the progress calculation by using the
      * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -841,6 +844,17 @@
      * Ideally, it should use a options.EstimatedDuration() instead...*/
     int weight = options.Timeout();
 
+    if (weight > 0) {
+        UpdateProgress(weight);
+    }
+
+    return status;
+}
+
+int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
+                              const CommandOptions& options) const {
+    bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
+
     uint64_t start = DurationReporter::Nanotime();
     pid_t pid = fork();
 
@@ -925,17 +939,14 @@
         MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
     }
 
-    if (weight > 0) {
-        UpdateProgress(weight);
-    }
     return status;
 }
 
-void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
                            const CommandOptions& options, long dumpsysTimeout) {
     long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout();
     std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)};
-    dumpsys.insert(dumpsys.end(), dumpsysArgs.begin(), dumpsysArgs.end());
+    dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
     RunCommand(title, dumpsys, options);
 }
 
@@ -1127,32 +1138,31 @@
 
 /* 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 (IsDryRun()) return nullptr;
+    DurationReporter duration_reporter("DUMP TRACES");
 
     const char* result = nullptr;
 
-    std::string tracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
-    if (tracesPath.empty()) return nullptr;
+    std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+    if (traces_path.empty()) return nullptr;
 
     /* move the old traces.txt (if any) out of the way temporarily */
-    std::string anrTracesPath = tracesPath + ".anr";
-    if (rename(tracesPath.c_str(), anrTracesPath.c_str()) && errno != ENOENT) {
-        MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), anrTracesPath.c_str(), strerror(errno));
+    std::string anrtraces_path = traces_path + ".anr";
+    if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
+        MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno));
         return nullptr;  // Can't rename old traces.txt -- no permission? -- leave it alone instead
     }
 
     /* create a new, empty traces.txt file to receive stack dumps */
-    int fd = TEMP_FAILURE_RETRY(open(tracesPath.c_str(),
+    int fd = TEMP_FAILURE_RETRY(open(traces_path.c_str(),
                                      O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
                                      0666)); /* -rw-rw-rw- */
     if (fd < 0) {
-        MYLOGE("%s: %s\n", tracesPath.c_str(), strerror(errno));
+        MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
         return nullptr;
     }
     int chmod_ret = fchmod(fd, 0666);
     if (chmod_ret < 0) {
-        MYLOGE("fchmod on %s failed: %s\n", tracesPath.c_str(), strerror(errno));
+        MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno));
         close(fd);
         return nullptr;
     }
@@ -1175,9 +1185,9 @@
         goto error_close_fd;
     }
 
-    wfd = inotify_add_watch(ifd, tracesPath.c_str(), IN_CLOSE_WRITE);
+    wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
     if (wfd < 0) {
-        MYLOGE("inotify_add_watch(%s): %s\n", tracesPath.c_str(), strerror(errno));
+        MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
         goto error_close_ifd;
     }
 
@@ -1261,15 +1271,17 @@
         MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
     }
 
-    static std::string dumpTracesPath = tracesPath + ".bugreport";
-    if (rename(tracesPath.c_str(), dumpTracesPath.c_str())) {
-        MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), dumpTracesPath.c_str(), strerror(errno));
+    static std::string dumptraces_path = android::base::StringPrintf(
+        "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+    if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+        MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+               strerror(errno));
         goto error_close_ifd;
     }
-    result = dumpTracesPath.c_str();
+    result = dumptraces_path.c_str();
 
     /* replace the saved [ANR] traces.txt file */
-    rename(anrTracesPath.c_str(), tracesPath.c_str());
+    rename(anrtraces_path.c_str(), traces_path.c_str());
 
 error_close_ifd:
     close(ifd);
@@ -1301,7 +1313,7 @@
 
 // TODO: make this function thread safe if sections are generated in parallel.
 void Dumpstate::UpdateProgress(int delta) {
-    if (!updateProgress_) return;
+    if (!update_progress_) return;
 
     progress_ += delta;
 
@@ -1309,12 +1321,12 @@
     char value[PROPERTY_VALUE_MAX];
 
     // adjusts max on the fly
-    if (progress_ > weightTotal_) {
-        int newTotal = weightTotal_ * 1.2;
-        MYLOGD("Adjusting total weight from %d to %d\n", weightTotal_, newTotal);
-        weightTotal_ = newTotal;
+    if (progress_ > weight_total_) {
+        int new_total = weight_total_ * 1.2;
+        MYLOGD("Adjusting total weight from %d to %d\n", weight_total_, new_total);
+        weight_total_ = new_total;
         snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
-        snprintf(value, sizeof(value), "%d", weightTotal_);
+        snprintf(value, sizeof(value), "%d", weight_total_);
         int status = property_set(key, value);
         if (status != 0) {
             MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
@@ -1327,16 +1339,16 @@
 
     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, weightTotal_);
+        MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total_);
     } else {
         // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
         // directly for debuggging.
-        fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weightTotal_);
+        fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total_);
     }
 
-    if (controlSocketFd_ >= 0) {
-        dprintf(controlSocketFd_, "PROGRESS:%d/%d\n", progress_, weightTotal_);
-        fsync(controlSocketFd_);
+    if (control_socket_fd_ >= 0) {
+        dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress_, weight_total_);
+        fsync(control_socket_fd_);
     }
 
     int status = property_set(key, value);
@@ -1347,14 +1359,14 @@
 }
 
 void Dumpstate::TakeScreenshot(const std::string& path) {
-    const std::string& realPath = path.empty() ? screenshotPath_ : path;
+    const std::string& real_path = path.empty() ? screenshot_path_ : path;
     int status =
-        RunCommand("", {"/system/bin/screencap", "-p", realPath},
+        RunCommand("", {"/system/bin/screencap", "-p", real_path},
                    CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
     if (status == 0) {
-        MYLOGD("Screenshot saved on %s\n", realPath.c_str());
+        MYLOGD("Screenshot saved on %s\n", real_path.c_str());
     } else {
-        MYLOGE("Failed to take screenshot on %s\n", realPath.c_str());
+        MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
     }
 }
 
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 2df48aa..078db26 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -104,7 +104,7 @@
  * if the label of that top-level file actually changed.  This can save us
  * significant time by avoiding no-op traversals of large filesystem trees.
  */
-static int restorecon_app_data_lazy(const char* path, const char* seinfo, uid_t uid) {
+static int restorecon_app_data_lazy(const std::string& path, const char* seinfo, uid_t uid) {
     int res = 0;
     char* before = nullptr;
     char* after = nullptr;
@@ -112,15 +112,15 @@
     // Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
     // libselinux. Not needed here.
 
-    if (lgetfilecon(path, &before) < 0) {
+    if (lgetfilecon(path.c_str(), &before) < 0) {
         PLOG(ERROR) << "Failed before getfilecon for " << path;
         goto fail;
     }
-    if (selinux_android_restorecon_pkgdir(path, seinfo, uid, 0) < 0) {
+    if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, 0) < 0) {
         PLOG(ERROR) << "Failed top-level restorecon for " << path;
         goto fail;
     }
-    if (lgetfilecon(path, &after) < 0) {
+    if (lgetfilecon(path.c_str(), &after) < 0) {
         PLOG(ERROR) << "Failed after getfilecon for " << path;
         goto fail;
     }
@@ -130,7 +130,7 @@
     if (strcmp(before, after)) {
         LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path
                 << "; running recursive restorecon";
-        if (selinux_android_restorecon_pkgdir(path, seinfo, uid,
+        if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid,
                 SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
             PLOG(ERROR) << "Failed recursive restorecon for " << path;
             goto fail;
@@ -146,6 +146,11 @@
     return res;
 }
 
+static int restorecon_app_data_lazy(const std::string& parent, const char* name, const char* seinfo,
+        uid_t uid) {
+    return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seinfo, uid);
+}
+
 static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
     if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
         PLOG(ERROR) << "Failed to prepare " << path;
@@ -172,7 +177,9 @@
         }
 
         // Consider restorecon over contents if label changed
-        if (restorecon_app_data_lazy(path.c_str(), seinfo, uid)) {
+        if (restorecon_app_data_lazy(path, seinfo, uid) ||
+                restorecon_app_data_lazy(path, "cache", seinfo, uid) ||
+                restorecon_app_data_lazy(path, "code_cache", seinfo, uid)) {
             return -1;
         }
 
@@ -191,7 +198,7 @@
         }
 
         // Consider restorecon over contents if label changed
-        if (restorecon_app_data_lazy(path.c_str(), seinfo, uid)) {
+        if (restorecon_app_data_lazy(path, seinfo, uid)) {
             return -1;
         }
 
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
index b87d34f..3cf1148 100644
--- a/cmds/surfacereplayer/proto/Android.mk
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -18,10 +18,7 @@
 
 LOCAL_SRC_FILES := $(call all-proto-files-under, src)
 
-LOCAL_SHARED_LIBRARIES := \
-    libprotobuf-cpp-full
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
 
 LOCAL_MODULE := libtrace_proto
 LOCAL_MODULE_CLASS := STATIC_LIBRARIES
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 45060af..0bc08a9 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -1,4 +1,5 @@
 syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
 
 message Trace {
     repeated Increment increment = 1;
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
index dac4314..3ec3178 100644
--- a/cmds/surfacereplayer/replayer/Android.mk
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -40,7 +40,8 @@
     libgui \
     libui \
     libutils \
-    libprotobuf-cpp-full \
+    libprotobuf-cpp-lite \
+    libbase \
 
 LOCAL_STATIC_LIBRARIES := \
     libtrace_proto \
@@ -57,7 +58,7 @@
     Main.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
-    libprotobuf-cpp-full \
+    libprotobuf-cpp-lite \
     libsurfacereplayer \
     libutils \
 
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index ace10d1..a49da37 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -20,6 +20,8 @@
 
 #include <android/native_window.h>
 
+#include <android-base/file.h>
+
 #include <binder/IMemory.h>
 
 #include <gui/BufferQueue.h>
@@ -61,14 +63,18 @@
         mStopTimeStamp(stopHere) {
     srand(RAND_COLOR_SEED);
 
-    std::fstream input(filename, std::ios::in | std::ios::binary);
-
-    mLoaded = mTrace.ParseFromIstream(&input);
-    if (!mLoaded) {
+    std::string input;
+    if (!android::base::ReadFileToString(filename, &input, true)) {
         std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
         abort();
     }
 
+    mLoaded = mTrace.ParseFromString(input);
+    if (!mLoaded) {
+        std::cerr << "Trace did not load." << std::endl;
+        abort();
+    }
+
     mCurrentTime = mTrace.increment(0).time_stamp();
 
     sReplayingManually.store(replayManually);
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f10cbc3..467e0fd 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -51,8 +51,8 @@
     <!-- Feature to specify if the device support managed users. -->
     <feature name="android.software.managed_users" />
 
-    <!-- Feature to specify if the device supports a VR mode. -->
-    <feature name="android.software.vr.mode" />
+    <!-- Feature to specify if the device supports a VR mode.
+         feature name="android.software.vr.mode" -->
     <!-- Devices with all optimizations required to be a "VR Ready" device that
          pass all CTS tests for this feature must include feature
          android.hardware.vr.high_performance -->
diff --git a/include/binder/TextOutput.h b/include/binder/TextOutput.h
index 974a194..851e01f 100644
--- a/include/binder/TextOutput.h
+++ b/include/binder/TextOutput.h
@@ -18,16 +18,15 @@
 #define ANDROID_TEXTOUTPUT_H
 
 #include <utils/Errors.h>
+#include <utils/String8.h>
 
 #include <stdint.h>
 #include <string.h>
+#include <sstream>
 
 // ---------------------------------------------------------------------------
 namespace android {
 
-class String8;
-class String16;
-
 class TextOutput
 {
 public:
@@ -66,30 +65,26 @@
 TextOutput& indent(TextOutput& to);
 TextOutput& dedent(TextOutput& to);
 
-TextOutput& operator<<(TextOutput& to, const char* str);
-TextOutput& operator<<(TextOutput& to, char);     // writes raw character
-TextOutput& operator<<(TextOutput& to, bool);
-TextOutput& operator<<(TextOutput& to, int);
-TextOutput& operator<<(TextOutput& to, long);
-TextOutput& operator<<(TextOutput& to, unsigned int);
-TextOutput& operator<<(TextOutput& to, unsigned long);
-TextOutput& operator<<(TextOutput& to, long long);
-TextOutput& operator<<(TextOutput& to, unsigned long long);
-TextOutput& operator<<(TextOutput& to, float);
-TextOutput& operator<<(TextOutput& to, double);
-TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
-TextOutput& operator<<(TextOutput& to, const void*);
-TextOutput& operator<<(TextOutput& to, const String8& val);
-TextOutput& operator<<(TextOutput& to, const String16& val);
+template<typename T>
+TextOutput& operator<<(TextOutput& to, const T& val)
+{
+    std::stringstream strbuf;
+    strbuf << val;
+    std::string str = strbuf.str();
+    to.print(str.c_str(), str.size());
+    return to;
+}
 
-class TypeCode 
+TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
+
+class TypeCode
 {
 public:
     inline TypeCode(uint32_t code);
     inline ~TypeCode();
 
     inline uint32_t typeCode() const;
-    
+
 private:
     uint32_t mCode;
 };
@@ -124,6 +119,32 @@
 };
 
 TextOutput& operator<<(TextOutput& to, const HexDump& val);
+inline TextOutput& operator<<(TextOutput& to,
+                              decltype(std::endl<char,
+                                       std::char_traits<char>>)
+                              /*val*/) {
+    endl(to);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const char &c)
+{
+    to.print(&c, 1);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const bool &val)
+{
+    if (val) to.print("true", 4);
+    else to.print("false", 5);
+    return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const String16& val)
+{
+    to << String8(val).string();
+    return to;
+}
 
 // ---------------------------------------------------------------------------
 // No user servicable parts below.
@@ -146,18 +167,6 @@
     return to;
 }
 
-inline TextOutput& operator<<(TextOutput& to, const char* str)
-{
-    to.print(str, strlen(str));
-    return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, char c)
-{
-    to.print(&c, 1);
-    return to;
-}
-
 inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func)
 {
     return (*func)(to);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 9b5f0d7..c9f688f 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -64,10 +64,6 @@
 
 namespace android {
 
-static const char* getReturnString(size_t idx);
-static const void* printReturnCommand(TextOutput& out, const void* _cmd);
-static const void* printCommand(TextOutput& out, const void* _cmd);
-
 // Static const and functions will be optimized out if not used,
 // when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
 static const char *kReturnStrings[] = {
@@ -111,8 +107,9 @@
     "BC_DEAD_BINDER_DONE"
 };
 
-static const char* getReturnString(size_t idx)
+static const char* getReturnString(uint32_t cmd)
 {
+    size_t idx = cmd & 0xff;
     if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
         return kReturnStrings[idx];
     else
@@ -526,8 +523,8 @@
         }
     } while (result != -ECONNREFUSED && result != -EBADF);
 
-    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
-        (void*)pthread_self(), getpid(), (void*)result);
+    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
+        (void*)pthread_self(), getpid(), result);
     
     mOut.writeInt32(BC_EXIT_LOOPER);
     talkWithDriver(false);
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 2ed5188..101eba3 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -29,111 +29,14 @@
 
 // ---------------------------------------------------------------------------
 
-TextOutput::TextOutput() { 
+TextOutput::TextOutput() {
 }
 
-TextOutput::~TextOutput() { 
+TextOutput::~TextOutput() {
 }
 
 // ---------------------------------------------------------------------------
 
-TextOutput& operator<<(TextOutput& to, bool val)
-{
-    if (val) to.print("true", 4);
-    else to.print("false", 5);
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, int val)
-{
-    char buf[16];
-    sprintf(buf, "%d", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long val)
-{
-    char buf[16];
-    sprintf(buf, "%ld", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned int val)
-{
-    char buf[16];
-    sprintf(buf, "%u", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long val)
-{
-    char buf[16];
-    sprintf(buf, "%lu", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long long val)
-{
-    char buf[32];
-    sprintf(buf, "%Ld", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long long val)
-{
-    char buf[32];
-    sprintf(buf, "%Lu", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-static TextOutput& print_float(TextOutput& to, double value)
-{
-    char buf[64];
-    sprintf(buf, "%g", value);
-    if( !strchr(buf, '.') && !strchr(buf, 'e') &&
-        !strchr(buf, 'E') ) {
-        strncat(buf, ".0", sizeof(buf)-1);
-    }
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, float val)
-{
-    return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, double val)
-{
-    return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, const void* val)
-{
-    char buf[32];
-    snprintf(buf, sizeof(buf), "%p", val);
-    to.print(buf, strlen(buf));
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String8& val)
-{
-    to << val.string();
-    return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String16& val)
-{
-    to << String8(val).string();
-    return to;
-}
-
 static void textOutputPrinter(void* cookie, const char* txt)
 {
     ((TextOutput*)cookie)->print(txt, strlen(txt));
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 4f77eed..2152206 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -51,3 +51,13 @@
         "-O3",
     ],
 }
+
+cc_test {
+    name: "binderTextOutputTest",
+    srcs: ["binderTextOutputTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libbase",
+    ],
+}
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index e02844e..ff5912f 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -229,7 +229,9 @@
             .sender_euid = 0,
             .data_size = 0,
             .offsets_size = 0,
-            .data = {0, 0},
+            .data = {
+                .ptr = {0, 0},
+            },
         },
     };
     struct {
@@ -350,4 +352,3 @@
 
     return RUN_ALL_TESTS();
 }
-
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index cb0e965..757291c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -34,6 +34,7 @@
 
 static testing::Environment* binder_env;
 static char *binderservername;
+static char *binderserversuffix;
 static char binderserverarg[] = "--binderserver";
 
 static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -72,6 +73,7 @@
         binderserverarg,
         stri,
         strpipefd1,
+        binderserversuffix,
         NULL
     };
 
@@ -969,6 +971,8 @@
 
 int run_server(int index, int readypipefd)
 {
+    binderLibTestServiceName += String16(binderserversuffix);
+
     status_t ret;
     sp<IServiceManager> sm = defaultServiceManager();
     {
@@ -999,15 +1003,19 @@
 int main(int argc, char **argv) {
     int ret;
 
-    if (argc == 3 && !strcmp(argv[1], "--servername")) {
+    if (argc == 4 && !strcmp(argv[1], "--servername")) {
         binderservername = argv[2];
     } else {
         binderservername = argv[0];
     }
 
-    if (argc == 4 && !strcmp(argv[1], binderserverarg)) {
+    if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
+        binderserversuffix = argv[4];
         return run_server(atoi(argv[2]), atoi(argv[3]));
     }
+    binderserversuffix = new char[16];
+    snprintf(binderserversuffix, 16, "%d", getpid());
+    binderLibTestServiceName += String16(binderserversuffix);
 
     ::testing::InitGoogleTest(&argc, argv);
     binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv());
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
new file mode 100644
index 0000000..f6dd22d
--- /dev/null
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include <binder/Debug.h>
+
+static void CheckMessage(const CapturedStderr& cap,
+                         const char* expected,
+                         bool singleline) {
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    if (singleline)
+        output.erase(std::remove(output.begin(), output.end(), '\n'));
+    ASSERT_STREQ(output.c_str(), expected);
+}
+
+#define CHECK_LOG_(input, expect, singleline)    \
+{                                                \
+    CapturedStderr cap;                          \
+    android::aerr << input << android::endl;     \
+    CheckMessage(cap, expect, singleline);       \
+}                                                \
+
+#define CHECK_VAL_(val, singleline)              \
+{                                                \
+    std::stringstream ss;                        \
+    ss << val;                                   \
+    std::string s = ss.str();                    \
+    CHECK_LOG_(val, s.c_str(), singleline);      \
+}                                                \
+
+#define CHECK_LOG(input, expect) CHECK_LOG_(input, expect, true)
+#define CHECK_VAL(val) CHECK_VAL_(val, true)
+
+TEST(TextOutput, HandlesStdEndl) {
+    CapturedStderr cap;
+    android::aerr << "foobar" << std::endl;
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesCEndl) {
+    CapturedStderr cap;
+    android::aerr << "foobar" << "\n";
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesAndroidEndl) {
+    CapturedStderr cap;
+    android::aerr << "foobar" << android::endl;
+    std::string output;
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+    android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandleEmptyString) {
+    CHECK_LOG("", "");
+}
+
+TEST(TextOutput, HandleString) {
+    CHECK_LOG("foobar", "foobar");
+}
+
+TEST(TextOutput, HandleNum) {
+    CHECK_LOG(12345, "12345");
+}
+
+TEST(TextOutput, HandleBool) {
+    CHECK_LOG(false, "false");
+}
+
+TEST(TextOutput, HandleChar) {
+    CHECK_LOG('T', "T");
+}
+
+TEST(TextOutput, HandleParcel) {
+    android::Parcel val;
+    CHECK_LOG(val, "Parcel(NULL)");
+}
+
+TEST(TextOutput, HandleHexDump) {
+    const char buf[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+    android::HexDump val(buf, sizeof(buf));
+    CHECK_LOG(val, "03020100 07060504 0b0a0908 0f0e0d0c '................'");
+}
+
+TEST(TextOutput, HandleHexDumpCustom) {
+    const char buf[4] = {0x11,0x22,0x33,0x44};
+    android::HexDump val(buf, sizeof(buf), 4);
+    CHECK_LOG(val, "11 22 33 44 '.\"3D'");
+}
+
+TEST(TextOutput, HandleTypeCode) {
+    android::TypeCode val(1234);
+    CHECK_LOG(val, "'\\x04\\xd2'");
+}
+
+TEST(TextOutput, HandleCookie) {
+    int32_t val = 321; //0x141
+    CHECK_LOG((void*)(long)val, "0x141");
+}
+
+TEST(TextOutput, HandleString8) {
+    android::String8 val("foobar");
+    CHECK_LOG(val, "foobar");
+}
+
+TEST(TextOutput, HandleString16) {
+    android::String16 val("foobar");
+    CHECK_LOG(val, "foobar");
+}
+
+template <typename T>
+class TextTest : public testing::Test {};
+
+typedef testing::Types<short, unsigned short,
+                       int, unsigned int,
+                       long, unsigned long,
+                       long long, unsigned long long,
+                       float, double, long double> TestTypes;
+TYPED_TEST_CASE(TextTest, TestTypes);
+
+TYPED_TEST(TextTest, TextMax)
+{
+    TypeParam max = std::numeric_limits<TypeParam>::max();
+    CHECK_VAL(max);
+}
+
+TYPED_TEST(TextTest, TestMin)
+{
+    TypeParam min = std::numeric_limits<TypeParam>::min();
+    CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestDenom)
+{
+    TypeParam min = std::numeric_limits<TypeParam>::denorm_min();
+    CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestEpsilon)
+{
+    TypeParam eps = std::numeric_limits<TypeParam>::epsilon();
+    CHECK_VAL(eps);
+}
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index aa0db45..10e999c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -1228,14 +1228,19 @@
         EGL_NONE,
     };
     if (!crop.isValid()) {
-        // No crop rect to set, so terminate the attrib array before the crop.
-        attrs[2] = EGL_NONE;
+        // No crop rect to set, so leave the crop out of the attrib array. Make
+        // sure to propagate the protected content attrs if they are set.
+        attrs[2] = attrs[10];
+        attrs[3] = attrs[11];
+        attrs[4] = EGL_NONE;
     } else if (!isEglImageCroppable(crop)) {
         // The crop rect is not at the origin, so we can't set the crop on the
         // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
         // extension.  In the future we can add a layered extension that
         // removes this restriction if there is hardware that can support it.
-        attrs[2] = EGL_NONE;
+        attrs[2] = attrs[10];
+        attrs[3] = attrs[11];
+        attrs[4] = EGL_NONE;
     }
     eglInitialize(dpy, 0, 0);
     EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 164be20..c520bda 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -17,6 +17,7 @@
     from: "include",
     to: "",
     srcs: ["include/EGL/**/*.h"],
+    license: "include/EGL/NOTICE",
 }
 
 ndk_headers {
@@ -24,6 +25,7 @@
     from: "include",
     to: "",
     srcs: ["include/GLES/**/*.h"],
+    license: "include/GLES/NOTICE",
 }
 
 ndk_headers {
@@ -31,6 +33,7 @@
     from: "include",
     to: "",
     srcs: ["include/GLES2/**/*.h"],
+    license: "include/GLES2/NOTICE",
 }
 
 ndk_headers {
@@ -38,6 +41,7 @@
     from: "include",
     to: "",
     srcs: ["include/GLES3/**/*.h"],
+    license: "include/GLES3/NOTICE",
 }
 
 ndk_headers {
@@ -45,6 +49,7 @@
     from: "include",
     to: "",
     srcs: ["include/KHR/**/*.h"],
+    license: "include/KHR/NOTICE",
 }
 
 subdirs = [
diff --git a/opengl/include/EGL/NOTICE b/opengl/include/EGL/NOTICE
new file mode 100644
index 0000000..55f5efa
--- /dev/null
+++ b/opengl/include/EGL/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2007-2009 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/GLES/NOTICE b/opengl/include/GLES/NOTICE
new file mode 100644
index 0000000..4dc6614
--- /dev/null
+++ b/opengl/include/GLES/NOTICE
@@ -0,0 +1,2 @@
+This document is licensed under the SGI Free Software B License Version 2.0.
+For details, see http://oss.sgi.com/projects/FreeB/ .
diff --git a/opengl/include/GLES2/NOTICE b/opengl/include/GLES2/NOTICE
new file mode 100644
index 0000000..7a94373
--- /dev/null
+++ b/opengl/include/GLES2/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2013-2015 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/GLES3/NOTICE b/opengl/include/GLES3/NOTICE
new file mode 100644
index 0000000..7a94373
--- /dev/null
+++ b/opengl/include/GLES3/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2013-2015 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/KHR/NOTICE b/opengl/include/KHR/NOTICE
new file mode 100644
index 0000000..36796e8
--- /dev/null
+++ b/opengl/include/KHR/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2009 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f695edb..ba036dd 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -145,7 +145,7 @@
     libgui \
     libpowermanager \
     libvulkan \
-    libprotobuf-cpp-full \
+    libprotobuf-cpp-lite \
     libhidl \
     libhwbinder \
     libbase \
diff --git a/services/surfaceflinger/DdmConnection.h b/services/surfaceflinger/DdmConnection.h
index b6b088b..938d14b 100644
--- a/services/surfaceflinger/DdmConnection.h
+++ b/services/surfaceflinger/DdmConnection.h
@@ -24,6 +24,9 @@
 
 class DdmConnection {
 public:
+    // Creates a JVM and registers all handlers to DDMS.
+    // This allows tools relying on DDMS to find surfaceflinger
+    // (e.g: Memory Leak finder, heap analyzer, ...)
     static void start(const char* name);
 };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e5b57f5..a0e040b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1836,12 +1836,9 @@
 
 void SurfaceFlinger::commitTransaction()
 {
-    sp<const DisplayDevice> hw = getDefaultDisplayDevice();
-
-    if (!mLayersPendingRemoval.isEmpty() && hw->isDisplayOn()) {
+    if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
-            mCurrentState.layersSortedByZ.remove(mLayersPendingRemoval[i]);
             recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
                     mLayersPendingRemoval[i]->getOccupancyHistory(true));
             mLayersPendingRemoval[i]->onRemoved();
@@ -2288,10 +2285,14 @@
         return NO_ERROR;
     }
 
-    mLayersPendingRemoval.push(layer);
-    mLayersRemoved = true;
-    setTransactionFlags(eTransactionNeeded);
-    return NO_ERROR;
+    ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
+    if (index >= 0) {
+        mLayersPendingRemoval.push(layer);
+        mLayersRemoved = true;
+        setTransactionFlags(eTransactionNeeded);
+        return NO_ERROR;
+    }
+    return status_t(index);
 }
 
 uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 4ae3580..a12276a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -20,6 +20,9 @@
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceInterceptor.h"
+
+#include <android-base/file.h>
+
 #include <cutils/log.h>
 
 #include <utils/Trace.h>
@@ -119,14 +122,18 @@
 
 status_t SurfaceInterceptor::writeProtoFileLocked() {
     ATRACE_CALL();
-    std::ofstream output(mOutputFileName, std::ios::out | std::ios::trunc | std::ios::binary);
-    // SerializeToOstream returns false when it's missing required data or when it could not write
+    std::string output;
+
     if (!mTrace.IsInitialized()) {
         return NOT_ENOUGH_DATA;
     }
-    if (!mTrace.SerializeToOstream(&output)) {
+    if (!mTrace.SerializeToString(&output)) {
         return PERMISSION_DENIED;
     }
+    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
+        return PERMISSION_DENIED;
+    }
+
     return NO_ERROR;
 }
 
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index 97d99d0..d97cf5e 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -17,6 +17,7 @@
     from: "include",
     to: "",
     srcs: ["include/vulkan/**/*.h"],
+    license: "include/vulkan/NOTICE",
 }
 
 subdirs = [
diff --git a/vulkan/include/vulkan/NOTICE b/vulkan/include/vulkan/NOTICE
new file mode 100644
index 0000000..c958fba
--- /dev/null
+++ b/vulkan/include/vulkan/NOTICE
@@ -0,0 +1,13 @@
+Copyright (c) 2015-2016 The Khronos Group Inc.
+
+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.