Merge "Document FlattenableHelper design decision"
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 388b506..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
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 29589cd..740eb19 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -39,6 +39,7 @@
 #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>
@@ -47,7 +48,6 @@
 #include <private/android_logger.h>
 
 #include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
 
 #include <openssl/sha.h>
 
@@ -57,10 +57,6 @@
 
 // TODO: variables and functions below should be part of dumpstate object
 
-// TODO: Can't be added to dumpstate.h because including "ziparchive/zip_writer.h" would not work.
-// That's probably because of the dumpstate -> libdumpstate -> device implementation setup, which
-// might be changed anyways - let's keep it here and wait
-static std::unique_ptr<ZipWriter> zip_writer;
 static std::set<std::string> mount_points;
 void add_mountinfo();
 
@@ -395,6 +391,81 @@
     }
 }
 
+/**
+ * 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);
@@ -619,7 +690,7 @@
 }
 
 bool Dumpstate::IsZipping() const {
-    return zip_writer != nullptr;
+    return zip_writer_ != nullptr;
 }
 
 // List of file extensions that can cause a zip file attachment to be rejected by some email
@@ -651,10 +722,10 @@
 
     // 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_));
+    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(),
+        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
                ZipWriter::ErrorCodeString(err));
         return false;
     }
@@ -668,16 +739,16 @@
             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();
+    err = zip_writer_->FinishEntry();
     if (err != 0) {
-        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
@@ -717,23 +788,23 @@
         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_);
+    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(),
+        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());
+    err = zip_writer_->WriteBytes(content.c_str(), content.length());
     if (err != 0) {
-        MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
+        MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
                ZipWriter::ErrorCodeString(err));
         return false;
     }
 
-    err = zip_writer->FinishEntry();
+    err = zip_writer_->FinishEntry();
     if (err != 0) {
-        MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
@@ -751,6 +822,87 @@
     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 duration_reporter("DUMPSTATE");
     unsigned long timeout;
@@ -780,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");
@@ -828,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++) {
@@ -894,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);
@@ -1037,6 +1153,11 @@
 
     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.weight_total_, WEIGHT_TOTAL);
@@ -1128,9 +1249,9 @@
     redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
     fprintf(stderr, "\n");
 
-    int32_t err = zip_writer->Finish();
+    int32_t err = zip_writer_->Finish();
     if (err != 0) {
-        MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+        MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
         return false;
     }
 
@@ -1287,9 +1408,10 @@
         ds.version_ = VERSION_CURRENT;
     }
 
-    if (ds.version_ != VERSION_CURRENT) {
-        MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s')\n",
-               ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str());
+    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);
     }
 
@@ -1376,7 +1498,7 @@
                 MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
                 do_zip_file = 0;
             } else {
-                zip_writer.reset(new ZipWriter(ds.zip_file.get()));
+                ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
             }
             ds.AddTextZipEntry("version.txt", ds.version_);
         }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 568256a..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);
 };
 
 /*
@@ -200,6 +205,12 @@
 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";
@@ -373,6 +384,9 @@
     // 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(const std::string& version = VERSION_CURRENT, bool dry_run = false,
@@ -390,6 +404,8 @@
 
     // Build type (such as 'user' or 'eng').
     std::string build_type_;
+
+    DISALLOW_COPY_AND_ASSIGN(Dumpstate);
 };
 
 // for_each_pid_func = void (*)(int, const char*);
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/utils.cpp b/cmds/dumpstate/utils.cpp
index 31c8697..c41cca4 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -17,24 +17,25 @@
 #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"
 
@@ -1137,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;
     }
@@ -1185,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;
     }
 
@@ -1271,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);
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/include/binder/TextOutput.h b/include/binder/TextOutput.h
index 0e9975a..851e01f 100644
--- a/include/binder/TextOutput.h
+++ b/include/binder/TextOutput.h
@@ -18,6 +18,7 @@
 #define ANDROID_TEXTOUTPUT_H
 
 #include <utils/Errors.h>
+#include <utils/String8.h>
 
 #include <stdint.h>
 #include <string.h>
@@ -26,9 +27,6 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
-class String8;
-class String16;
-
 class TextOutput
 {
 public:
@@ -121,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.
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 601df46..470fc59 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2094,11 +2094,15 @@
 
     status_t err = NO_ERROR;
     for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
-        fds[i] = dup(this->readFileDescriptor());
-        if (fds[i] < 0) {
+        int fd = this->readFileDescriptor();
+        if (fd < 0 || ((fds[i] = dup(fd)) < 0)) {
             err = BAD_VALUE;
             ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
-                i, fds[i], fd_count, strerror(errno));
+                  i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno));
+            // Close all the file descriptors that were dup-ed.
+            for (size_t j=0; j<i ;j++) {
+                close(fds[j]);
+            }
         }
     }
 
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/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/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;
 }