Merge "fix misuse of formatting specifiers"
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 0531cf9..bc5ba38 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -425,6 +425,7 @@
 // Used to pass multiple values to the stdin read thread.
 struct StdinReadArgs {
     int stdin_fd, write_fd;
+    bool raw_stdin;
     std::unique_ptr<ShellProtocol> protocol;
 };
 
@@ -452,26 +453,42 @@
         D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd);
         int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
         D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd);
-        if (r <= 0) break;
-        for (int n = 0; n < r; n++){
-            switch(buffer_ptr[n]) {
-            case '\n':
-                state = 1;
-                break;
-            case '\r':
-                state = 1;
-                break;
-            case '~':
-                if(state == 1) state++;
-                break;
-            case '.':
-                if(state == 2) {
-                    fprintf(stderr,"\n* disconnect *\n");
-                    stdin_raw_restore(args->stdin_fd);
-                    exit(0);
+        if (r <= 0) {
+            // Only devices using the shell protocol know to close subprocess
+            // stdin. For older devices we want to just leave the connection
+            // open, otherwise an unpredictable amount of return data could
+            // be lost due to the FD closing before all data has been received.
+            if (args->protocol) {
+                args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
+            }
+            break;
+        }
+        // If we made stdin raw, check input for the "~." escape sequence. In
+        // this situation signals like Ctrl+C are sent remotely rather than
+        // interpreted locally so this provides an emergency out if the remote
+        // process starts ignoring the signal. SSH also does this, see the
+        // "escape characters" section on the ssh man page for more info.
+        if (args->raw_stdin) {
+            for (int n = 0; n < r; n++){
+                switch(buffer_ptr[n]) {
+                case '\n':
+                    state = 1;
+                    break;
+                case '\r':
+                    state = 1;
+                    break;
+                case '~':
+                    if(state == 1) state++;
+                    break;
+                case '.':
+                    if(state == 2) {
+                        stdin_raw_restore(args->stdin_fd);
+                        fprintf(stderr,"\n* disconnect *\n");
+                        exit(0);
+                    }
+                default:
+                    state = 0;
                 }
-            default:
-                state = 0;
             }
         }
         if (args->protocol) {
@@ -488,8 +505,44 @@
     return nullptr;
 }
 
-static int interactive_shell(const std::string& service_string,
-                             bool use_shell_protocol) {
+// Returns a shell service string with the indicated arguments and command.
+static std::string ShellServiceString(bool use_shell_protocol,
+                                      const std::string& type_arg,
+                                      const std::string& command) {
+    std::vector<std::string> args;
+    if (use_shell_protocol) {
+        args.push_back(kShellServiceArgShellProtocol);
+    }
+    if (!type_arg.empty()) {
+        args.push_back(type_arg);
+    }
+
+    // Shell service string can look like: shell[,arg1,arg2,...]:[command].
+    return android::base::StringPrintf("shell%s%s:%s",
+                                       args.empty() ? "" : ",",
+                                       android::base::Join(args, ',').c_str(),
+                                       command.c_str());
+}
+
+// Connects to a shell on the device and read/writes data.
+//
+// Note: currently this function doesn't properly clean up resources; the
+// FD connected to the adb server is never closed and the stdin read thread
+// may never exit.
+//
+// On success returns the remote exit code if |use_shell_protocol| is true,
+// 0 otherwise. On failure returns 1.
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+                       const std::string& command) {
+    std::string service_string = ShellServiceString(use_shell_protocol,
+                                                    type_arg, command);
+
+    // Make local stdin raw if the device allocates a PTY, which happens if:
+    //   1. We are explicitly asking for a PTY shell, or
+    //   2. We don't specify shell type and are starting an interactive session.
+    bool raw_stdin = (type_arg == kShellServiceArgPty ||
+                      (type_arg.empty() && command.empty()));
+
     std::string error;
     int fd = adb_connect(service_string, &error);
     if (fd < 0) {
@@ -502,13 +555,16 @@
         LOG(ERROR) << "couldn't allocate StdinReadArgs object";
         return 1;
     }
-    args->stdin_fd = 0;
+    args->stdin_fd = STDIN_FILENO;
     args->write_fd = fd;
+    args->raw_stdin = raw_stdin;
     if (use_shell_protocol) {
         args->protocol.reset(new ShellProtocol(args->write_fd));
     }
 
-    stdin_raw_init(args->stdin_fd);
+    if (raw_stdin) {
+        stdin_raw_init(STDIN_FILENO);
+    }
 
     int exit_code = 0;
     if (!adb_thread_create(stdin_read_thread, args)) {
@@ -519,7 +575,12 @@
         exit_code = read_and_dump(fd, use_shell_protocol);
     }
 
-    stdin_raw_restore(args->stdin_fd);
+    if (raw_stdin) {
+        stdin_raw_restore(STDIN_FILENO);
+    }
+
+    // TODO(dpursell): properly exit stdin_read_thread and close |fd|.
+
     return exit_code;
 }
 
@@ -795,25 +856,6 @@
     return adb_command(cmd);
 }
 
-// Returns a shell service string with the indicated arguments and command.
-static std::string ShellServiceString(bool use_shell_protocol,
-                                      const std::string& type_arg,
-                                      const std::string& command) {
-    std::vector<std::string> args;
-    if (use_shell_protocol) {
-        args.push_back(kShellServiceArgShellProtocol);
-    }
-    if (!type_arg.empty()) {
-        args.push_back(type_arg);
-    }
-
-    // Shell service string can look like: shell[,arg1,arg2,...]:[command].
-    return android::base::StringPrintf("shell%s%s:%s",
-                                       args.empty() ? "" : ",",
-                                       android::base::Join(args, ',').c_str(),
-                                       command.c_str());
-}
-
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
 static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1320,51 +1362,26 @@
             }
         }
 
+        std::string command;
+        if (argc) {
+            // We don't escape here, just like ssh(1). http://b/20564385.
+            command = android::base::Join(
+                    std::vector<const char*>(argv, argv + argc), ' ');
+        }
+
         if (h) {
             printf("\x1b[41;33m");
             fflush(stdout);
         }
 
-        if (!argc) {
-            D("starting interactive shell");
-            std::string service_string =
-                    ShellServiceString(use_shell_protocol, shell_type_arg, "");
-            r = interactive_shell(service_string, use_shell_protocol);
-            if (h) {
-                printf("\x1b[0m");
-                fflush(stdout);
-            }
-            return r;
+        r = RemoteShell(use_shell_protocol, shell_type_arg, command);
+
+        if (h) {
+            printf("\x1b[0m");
+            fflush(stdout);
         }
 
-        // We don't escape here, just like ssh(1). http://b/20564385.
-        std::string command = android::base::Join(
-                std::vector<const char*>(argv, argv + argc), ' ');
-        std::string service_string =
-                ShellServiceString(use_shell_protocol, shell_type_arg, command);
-
-        while (true) {
-            D("non-interactive shell loop. cmd=%s", service_string.c_str());
-            std::string error;
-            int fd = adb_connect(service_string, &error);
-            int r;
-            if (fd >= 0) {
-                D("about to read_and_dump(fd=%d)", fd);
-                r = read_and_dump(fd, use_shell_protocol);
-                D("read_and_dump() done.");
-                adb_close(fd);
-            } else {
-                fprintf(stderr,"error: %s\n", error.c_str());
-                r = -1;
-            }
-
-            if (h) {
-                printf("\x1b[0m");
-                fflush(stdout);
-            }
-            D("non-interactive shell loop. return r=%d", r);
-            return r;
-        }
+        return r;
     }
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 8aeea81..544afce 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -382,7 +382,7 @@
     subprocess->PassDataStreams();
     subprocess->WaitForExit();
 
-    D("deleting Subprocess");
+    D("deleting Subprocess for PID %d", subprocess->pid());
     delete subprocess;
 
     return nullptr;
@@ -501,11 +501,31 @@
             return &protocol_sfd_;
         }
 
-        // We only care about stdin packets.
-        if (stdinout_sfd_.valid() && input_->id() == ShellProtocol::kIdStdin) {
-            input_bytes_left_ = input_->data_length();
-        } else {
-            input_bytes_left_ = 0;
+        if (stdinout_sfd_.valid()) {
+            switch (input_->id()) {
+                case ShellProtocol::kIdStdin:
+                    input_bytes_left_ = input_->data_length();
+                    break;
+                case ShellProtocol::kIdCloseStdin:
+                    if (type_ == SubprocessType::kRaw) {
+                        if (adb_shutdown(stdinout_sfd_.fd(), SHUT_WR) == 0) {
+                            return nullptr;
+                        }
+                        PLOG(ERROR) << "failed to shutdown writes to FD "
+                                    << stdinout_sfd_.fd();
+                        return &stdinout_sfd_;
+                    } else {
+                        // PTYs can't close just input, so rather than close the
+                        // FD and risk losing subprocess output, leave it open.
+                        // This only happens if the client starts a PTY shell
+                        // non-interactively which is rare and unsupported.
+                        // If necessary, the client can manually close the shell
+                        // with `exit` or by killing the adb client process.
+                        D("can't close input for PTY FD %d",
+                          stdinout_sfd_.fd());
+                    }
+                    break;
+            }
         }
     }
 
@@ -532,7 +552,9 @@
 ScopedFd* Subprocess::PassOutput(ScopedFd* sfd, ShellProtocol::Id id) {
     int bytes = adb_read(sfd->fd(), output_->data(), output_->data_capacity());
     if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
-        if (bytes < 0) {
+        // read() returns EIO if a PTY closes; don't report this as an error,
+        // it just means the subprocess completed.
+        if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
             PLOG(ERROR) << "error reading output FD " << sfd->fd();
         }
         return sfd;
diff --git a/adb/shell_service.h b/adb/shell_service.h
index 8868f10..01410a9 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -50,11 +50,12 @@
   public:
     // This is an unscoped enum to make it easier to compare against raw bytes.
     enum Id : uint8_t {
-        kIdStdin  = 0,
+        kIdStdin = 0,
         kIdStdout = 1,
         kIdStderr = 2,
-        kIdExit   = 3,
-        kIdInvalid  = 255,  // Indicates an invalid or unknown packet.
+        kIdExit = 3,
+        kIdCloseStdin = 4,  // Close subprocess stdin if possible.
+        kIdInvalid = 255,  // Indicates an invalid or unknown packet.
     };
 
     // ShellPackets will probably be too large to allocate on the stack so they
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
index 20efd84..e18f905 100644
--- a/adb/shell_service_test.cpp
+++ b/adb/shell_service_test.cpp
@@ -245,6 +245,25 @@
     EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
 }
 
+// Tests closing raw subprocess stdin.
+TEST_F(ShellServiceTest, CloseClientStdin) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "cat; echo TEST_DONE",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string input = "foo\nbar";
+    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+    memcpy(protocol->data(), input.data(), input.length());
+    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
+    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
+    delete protocol;
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
+    ExpectLinesEqual(stderr, {});
+}
+
 // Tests that nothing breaks when the stdin/stdout pipe closes.
 TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
     ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 5918a94..501a75a 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -488,6 +488,10 @@
 {
     return shutdown(fd, SHUT_RDWR);
 }
+static __inline__ int  adb_shutdown(int fd, int direction)
+{
+    return shutdown(fd, direction);
+}
 #undef   shutdown
 #define  shutdown   ____xxx_shutdown
 
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 42f6d9b..994b851 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -3265,6 +3265,15 @@
         // terminal.
         return _console_read(_console_handle, buf, len);
     } else {
+        // On older versions of Windows (definitely 7, definitely not 10),
+        // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
+        // we need to limit the read size. This may also catch devices like NUL,
+        // but that is OK as we just want to avoid capping pipes and files which
+        // don't need size limiting. This isatty() test is very simple and quick
+        // and doesn't call the OS.
+        if (isatty(fd) && len > 4096) {
+            len = 4096;
+        }
         // Just call into C Runtime which can read from pipes/files and which
         // can do LF/CR translation (which is overridable with _setmode()).
         // Undefine the macro that is set in sysdeps.h which bans calls to
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 817917e..3495a71 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -88,8 +88,11 @@
     __le32 fs_count;
     __le32 hs_count;
     __le32 ss_count;
+    __le32 os_count;
     struct func_desc fs_descs, hs_descs;
     struct ss_func_desc ss_descs;
+    struct usb_os_desc_header os_header;
+    struct usb_ext_compat_desc os_desc;
 } __attribute__((packed));
 
 static struct func_desc fs_descriptors = {
@@ -181,6 +184,24 @@
     },
 };
 
+struct usb_ext_compat_desc os_desc_compat = {
+    .bFirstInterfaceNumber = 0,
+    .Reserved1 = cpu_to_le32(1),
+    .CompatibleID = {0},
+    .SubCompatibleID = {0},
+    .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+    .interface = cpu_to_le32(1),
+    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+    .bcdVersion = cpu_to_le32(1),
+    .wIndex = cpu_to_le32(4),
+    .bCount = cpu_to_le32(1),
+    .Reserved = cpu_to_le32(0),
+};
+
+
 #define STR_INTERFACE_ "ADB Interface"
 
 static const struct {
@@ -332,13 +353,16 @@
     v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
     v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
     v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
-                                 FUNCTIONFS_HAS_SS_DESC;
+                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
     v2_descriptor.fs_count = 3;
     v2_descriptor.hs_count = 3;
     v2_descriptor.ss_count = 5;
+    v2_descriptor.os_count = 1;
     v2_descriptor.fs_descs = fs_descriptors;
     v2_descriptor.hs_descs = hs_descriptors;
     v2_descriptor.ss_descs = ss_descriptors;
+    v2_descriptor.os_header = os_desc_header;
+    v2_descriptor.os_desc = os_desc_compat;
 
     if (h->control < 0) { // might have already done this before
         D("OPENING %s", USB_FFS_ADB_EP0);
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 8756956..11dfcbc 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -60,6 +60,7 @@
 LOCAL_REQUIRED_MODULES := core2md \
     crash_reporter_logs.conf \
     crash_sender \
+    crash_server \
     dbus-send
 LOCAL_INIT_RC := crash_reporter.rc
 LOCAL_RTTI_FLAG := -frtti
@@ -92,6 +93,22 @@
 LOCAL_SRC_FILES := $(warn_collector_src)
 include $(BUILD_EXECUTABLE)
 
+# /etc/os-release.d/crash_server configuration file.
+# ========================================================
+ifdef OSRELEASED_DIRECTORY
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_server
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)
+include $(BUILD_SYSTEM)/base_rules.mk
+
+# If the crash server isn't set, use a blank value.  crash_sender
+# will log it as a configuration error.
+$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= ""
+$(LOCAL_BUILT_MODULE):
+	echo $(BRILLO_CRASH_SERVER) > $@
+endif
+
 # Crash reporter logs conf file.
 # ========================================================
 include $(CLEAR_VARS)
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index ae56b4c..2a9d1d3 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -35,8 +35,8 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/key_value_store.h>
-#include <chromeos/process.h>
+#include <brillo/key_value_store.h>
+#include <brillo/process.h>
 
 namespace {
 
@@ -340,7 +340,7 @@
 bool CrashCollector::GetLogContents(const FilePath &config_path,
                                     const std::string &exec_name,
                                     const FilePath &output_file) {
-  chromeos::KeyValueStore store;
+  brillo::KeyValueStore store;
   if (!store.Load(config_path)) {
     LOG(INFO) << "Unable to read log configuration file "
               << config_path.value();
@@ -351,7 +351,7 @@
   if (!store.GetString(exec_name, &command))
     return false;
 
-  chromeos::ProcessImpl diag_process;
+  brillo::ProcessImpl diag_process;
   diag_process.AddArg(kShellPath);
   diag_process.AddStringOption("-c", command);
   diag_process.RedirectOutput(output_file.value());
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 32cbe9f..d00a5b5 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -22,14 +22,14 @@
 #include <base/files/file_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
 #include <gtest/gtest.h>
 
 #include "crash_collector.h"
 
 using base::FilePath;
 using base::StringPrintf;
-using chromeos::FindLog;
+using brillo::FindLog;
 using ::testing::Invoke;
 using ::testing::Return;
 
@@ -54,7 +54,7 @@
     collector_.Initialize(CountCrash, IsMetrics);
     test_dir_ = FilePath("test");
     base::CreateDirectory(test_dir_);
-    chromeos::ClearLog();
+    brillo::ClearLog();
   }
 
   void TearDown() {
@@ -208,7 +208,7 @@
             symlink(kMetaFileBasename,
                     meta_symlink_path.value().c_str()));
   ASSERT_TRUE(base::PathExists(meta_symlink_path));
-  chromeos::ClearLog();
+  brillo::ClearLog();
   collector_.WriteCrashMetaData(meta_symlink_path,
                                 "kernel",
                                 payload_file.value());
@@ -221,7 +221,7 @@
   // Test target of dangling symlink is not created.
   base::DeleteFile(meta_file, false);
   ASSERT_FALSE(base::PathExists(meta_file));
-  chromeos::ClearLog();
+  brillo::ClearLog();
   collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
                                 payload_file.value());
   EXPECT_FALSE(base::PathExists(meta_file));
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 7872f7b..3955fe5 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -25,9 +25,9 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/flag_helper.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
 #include <metrics/metrics_library.h>
 
 #include "kernel_collector.h"
@@ -102,7 +102,7 @@
   // Note: This will mean that the dbus-send process will become a zombie and
   // reparent to init for reaping, but that's OK -- see above.
 
-  chromeos::ProcessImpl dbus_send;
+  brillo::ProcessImpl dbus_send;
   dbus_send.AddArg("/system/bin/dbus-send");
   dbus_send.AddArg("--type=signal");
   dbus_send.AddArg("--system");
@@ -183,10 +183,10 @@
   }
 
   // Accumulate logs to help in diagnosing failures during user collection.
-  chromeos::LogToString(true);
+  brillo::LogToString(true);
   // Handle the crash, get the name of the process from procfs.
   bool handled = user_collector->HandleCrash(user, nullptr);
-  chromeos::LogToString(false);
+  brillo::LogToString(false);
   if (!handled)
     return 1;
   return 0;
@@ -198,9 +198,9 @@
   CHECK(!udev_event.empty()) << "--udev= must be set";
 
   // Accumulate logs to help in diagnosing failures during user collection.
-  chromeos::LogToString(true);
+  brillo::LogToString(true);
   bool handled = udev_collector->HandleCrash(udev_event);
-  chromeos::LogToString(false);
+  brillo::LogToString(false);
   if (!handled)
     return 1;
   return 0;
@@ -209,9 +209,9 @@
 static int HandleKernelWarning(KernelWarningCollector
                                *kernel_warning_collector) {
   // Accumulate logs to help in diagnosing failures during collection.
-  chromeos::LogToString(true);
+  brillo::LogToString(true);
   bool handled = kernel_warning_collector->Collect();
-  chromeos::LogToString(false);
+  brillo::LogToString(false);
   if (!handled)
     return 1;
   return 0;
@@ -278,9 +278,9 @@
   OpenStandardFileDescriptors();
   FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
   s_metrics_lib.Init();
-  chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
-  chromeos::OpenLog(my_path.BaseName().value().c_str(), true);
-  chromeos::InitLog(chromeos::kLogToSyslog);
+  brillo::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
+  brillo::OpenLog(my_path.BaseName().value().c_str(), true);
+  brillo::InitLog(brillo::kLogToSyslog);
 
   KernelCollector kernel_collector;
   kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
diff --git a/crash_reporter/crash_reporter.rc b/crash_reporter/crash_reporter.rc
index 7bfe0c2..57c1d40 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -5,7 +5,7 @@
 on property:crash_reporter.coredump.enabled=0
     write /proc/sys/kernel/core_pattern "core"
 
-on boot
+on post-fs-data
     # Allow catching multiple unrelated concurrent crashes, but use a finite
     # number to prevent infinitely recursing on crash handling.
     write /proc/sys/kernel/core_pipe_limit 4
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
index c9ca02d..e778002 100644
--- a/crash_reporter/crash_reporter_logs_test.cc
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -17,7 +17,7 @@
 #include <string>
 
 #include <base/files/file_path.h>
-#include <chromeos/key_value_store.h>
+#include <brillo/key_value_store.h>
 #include <gtest/gtest.h>
 
 namespace {
@@ -32,7 +32,7 @@
 
 // Tests that the config file is parsable and that Chrome is listed.
 TEST(CrashReporterLogsTest, ReadConfig) {
-  chromeos::KeyValueStore store;
+  brillo::KeyValueStore store;
   ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
   std::string command;
   EXPECT_TRUE(store.GetString(kChromeExecName, &command));
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
index 8a422dd..5b859a8 100755
--- a/crash_reporter/crash_sender
+++ b/crash_reporter/crash_sender
@@ -78,6 +78,9 @@
 # The weave configuration file.
 WEAVE_CONF_FILE="/etc/weaved/weaved.conf"
 
+# The os-release.d folder.
+OSRELEASED_FOLDER="/etc/os-release.d"
+
 # The syslog tag for all logging we emit.
 TAG="$(basename $0)[$$]"
 
@@ -256,7 +259,12 @@
 get_key_value() {
   local file="$1" key="$2" value
 
-  if [ -f "${file}" ]; then
+  if [ -f "${file}/${key}" ]; then
+    # Get the value from a folder where each key is its own file.  The key
+    # file's entire contents is the value.
+    value=$(cat "${file}/${key}")
+  elif [ -f "${file}" ]; then
+    # Get the value from a file that has multiple key=value combinations.
     # Return the first entry.  There shouldn't be more than one anyways.
     # Substr at length($1) + 2 skips past the key and following = sign (awk
     # uses 1-based indexes), but preserves embedded = characters.
@@ -291,15 +299,15 @@
   local report_payload="$(get_key_value "${meta_path}" "payload")"
   local kind="$(get_kind "${meta_path}")"
   local exec_name="$(get_key_value "${meta_path}" "exec_name")"
-  local url="$(getprop crash_reporter.server)"
-  local brillo_version="$(get_key_value "${meta_path}" "ver")"
+  local url="$(get_key_value "${OSRELEASED_FOLDER}" "crash_server")"
+  local bdk_version="$(get_key_value "${meta_path}" "bdk_version")"
   local hwclass="$(get_hardware_class)"
   local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
   local log="$(get_key_value "${meta_path}" "log")"
   local sig="$(get_key_value "${meta_path}" "sig")"
   local send_payload_size="$(stat -c "%s" "${report_payload}" 2>/dev/null)"
-  local product="$(get_key_value "${meta_path}" "upload_var_prod")"
-  local version="$(get_key_value "${meta_path}" "upload_var_ver")"
+  local product="$(get_key_value "${meta_path}" "product_id")"
+  local version="$(get_key_value "${meta_path}" "product_version")"
   local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
   local guid
   local model_manifest_id="$(get_key_value "${WEAVE_CONF_FILE}" "model_id")"
@@ -350,32 +358,13 @@
     esac
   done
 
-  # When uploading Chrome reports we need to report the right product and
-  # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
-  # as the product and GOOGLE_CRASH_VERSION_ID as the version.
-  if [ "${product}" = "undefined" ]; then
-    product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
-  fi
-  if [ "${version}" = "undefined" ]; then
-    version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
-  fi
-
-  # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
-  # /etc/os-release.
-  if [ "${product}" = "undefined" ]; then
-    product="$(get_key_value /etc/os-release 'ID')"
-  fi
-  if [ "${version}" = "undefined" ]; then
-    version="$(get_key_value /etc/os-release 'VERSION_ID')"
-  fi
-
   # If ID or VERSION_ID is undefined, we use the default product name
-  # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
+  # and bdk_version from /etc/os-release.d.
   if [ "${product}" = "undefined" ]; then
     product="${BRILLO_PRODUCT}"
   fi
   if [ "${version}" = "undefined" ]; then
-    version="${brillo_version}"
+    version="${bdk_version}"
   fi
 
   local image_type
@@ -408,6 +397,7 @@
   lecho "  Metadata: ${meta_path} (${kind})"
   lecho "  Payload: ${report_payload}"
   lecho "  Version: ${version}"
+  lecho "  Bdk Version: ${bdk_version}"
   [ -n "${image_type}" ] && lecho "  Image type: ${image_type}"
   [ -n "${boot_mode}" ] && lecho "  Boot mode: ${boot_mode}"
   if is_mock; then
@@ -460,6 +450,7 @@
     --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
     -F "prod=${product}" \
     -F "ver=${version}" \
+    -F "bdk_version=${bdk_version}" \
     -F "hwclass=${hwclass}" \
     -F "exec_name=${exec_name}" \
     -F "model_manifest_id=${model_manifest_id}" \
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index e690b77..cdb0ae7 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -22,13 +22,13 @@
 #include <base/files/scoped_temp_dir.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
 #include <gtest/gtest.h>
 
 using base::FilePath;
 using base::StringPrintf;
-using chromeos::FindLog;
-using chromeos::GetLog;
+using brillo::FindLog;
+using brillo::GetLog;
 
 namespace {
 
@@ -78,7 +78,7 @@
 
     test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
     ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
-    chromeos::ClearLog();
+    brillo::ClearLog();
   }
 
   FilePath test_kcrash_;
@@ -282,7 +282,7 @@
   ASSERT_EQ(1, s_crashes);
   ASSERT_TRUE(FindLog("(handling)"));
   static const char kNamePrefix[] = "Stored kcrash to ";
-  std::string log = chromeos::GetLog();
+  std::string log = brillo::GetLog();
   size_t pos = log.find(kNamePrefix);
   ASSERT_NE(std::string::npos, pos)
       << "Did not find string \"" << kNamePrefix << "\" in log: {\n"
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
index a39441d..d445557 100644
--- a/crash_reporter/list_proxies.cc
+++ b/crash_reporter/list_proxies.cc
@@ -28,8 +28,8 @@
 #include <base/strings/string_tokenizer.h>
 #include <base/strings/string_util.h>
 #include <base/values.h>
-#include <chromeos/daemons/dbus_daemon.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <brillo/syslog_logging.h>
 
 #include "libcrosservice/dbus-proxies.h"
 
@@ -111,7 +111,7 @@
 // must be called, which blocks on the D-Bus call to Chrome.  The call returns
 // after either the timeout or the proxy has been resolved.  The resolved
 // proxies can then be accessed through the proxies() function.
-class ProxyResolver : public chromeos::DBusDaemon {
+class ProxyResolver : public brillo::DBusDaemon {
  public:
   ProxyResolver(const std::string& source_url,
                 const std::string& signal_interface,
@@ -138,7 +138,7 @@
         timeout_callback_.callback(),
         timeout_);
 
-    return chromeos::DBusDaemon::Run();
+    return brillo::DBusDaemon::Run();
   }
 
  protected:
@@ -162,7 +162,7 @@
       return;
     }
 
-    chromeos::ErrorPtr error;
+    brillo::ErrorPtr error;
     call_proxy_->ResolveNetworkProxy(source_url_,
                                      signal_interface_,
                                      signal_name_,
@@ -189,7 +189,7 @@
   }
 
   int OnInit() override {
-    int return_code = chromeos::DBusDaemon::OnInit();
+    int return_code = brillo::DBusDaemon::OnInit();
     if (return_code != EX_OK)
       return return_code;
 
@@ -276,13 +276,13 @@
   }
 
   // Default to logging to syslog.
-  int init_flags = chromeos::kLogToSyslog;
+  int init_flags = brillo::kLogToSyslog;
   // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
   // was passed.
 
   if ((!quiet && isatty(STDERR_FILENO)) || verbose)
-    init_flags |= chromeos::kLogToStderr;
-  chromeos::InitLog(init_flags);
+    init_flags |= brillo::kLogToStderr;
+  brillo::InitLog(init_flags);
 
   std::string url;
   base::CommandLine::StringVector urls = cl->GetArgs();
diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
index a8c717e..744cf10 100644
--- a/crash_reporter/testrunner.cc
+++ b/crash_reporter/testrunner.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <chromeos/test_helpers.h>
+#include <brillo/test_helpers.h>
 #include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
index 576fdbd..1e018db 100644
--- a/crash_reporter/udev_collector.cc
+++ b/crash_reporter/udev_collector.cc
@@ -27,7 +27,7 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
+#include <brillo/process.h>
 
 using base::FilePath;
 
@@ -120,7 +120,7 @@
   }
 
   // Compress the output using gzip.
-  chromeos::ProcessImpl gzip_process;
+  brillo::ProcessImpl gzip_process;
   gzip_process.AddArg(kGzipPath);
   gzip_process.AddArg(crash_path.value());
   int process_result = gzip_process.Run();
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
index a6643fb..5474f48 100644
--- a/crash_reporter/udev_collector_test.cc
+++ b/crash_reporter/udev_collector_test.cc
@@ -18,7 +18,7 @@
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -141,7 +141,7 @@
                               kLogConfigFileContents,
                               strlen(kLogConfigFileContents)));
 
-    chromeos::ClearLog();
+    brillo::ClearLog();
   }
 
   UdevCollectorMock collector_;
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index c5c0662..3bdeca1 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -20,12 +20,12 @@
 
 #include <base/files/file_util.h>
 #include <base/strings/string_util.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 using base::FilePath;
-using ::chromeos::FindLog;
+using ::brillo::FindLog;
 
 namespace {
 
@@ -65,7 +65,7 @@
     base::DeleteFile(test_unclean_, true);
     // Set up an alternate power manager state file as well
     collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
-    chromeos::ClearLog();
+    brillo::ClearLog();
   }
 
  protected:
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index c4f02af..56e7bb9 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -37,8 +37,9 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/osrelease_reader.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
 #include <cutils/properties.h>
 #include <private/android_filesystem_config.h>
 
@@ -58,11 +59,10 @@
 const char *UserCollector::kUserId = "Uid:\t";
 const char *UserCollector::kGroupId = "Gid:\t";
 
-// The property containing the OS version.
-const char kVersionProperty[] = "ro.build.id";
-
-// The property containing the product id.
-const char kProductIDProperty[] = "ro.product.product_id";
+// Product information keys in the /etc/os-release.d folder.
+static const char kBdkVersionKey[] = "bdk_version";
+static const char kProductIDKey[] = "product_id";
+static const char kProductVersionKey[] = "product_version";
 
 
 using base::FilePath;
@@ -187,7 +187,7 @@
   AddCrashMetaData("sig", kCollectionErrorSignature);
   AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
   std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
-  std::string error_log = chromeos::GetLog();
+  std::string error_log = brillo::GetLog();
   FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
   if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
                      diag_log_path)) {
@@ -367,7 +367,7 @@
                                       const FilePath &minidump_path,
                                       const FilePath &temp_directory) {
   FilePath output_path = temp_directory.Append("output");
-  chromeos::ProcessImpl core2md;
+  brillo::ProcessImpl core2md;
   core2md.RedirectOutput(output_path.value());
   core2md.AddArg(kCoreToMinidumpConverterPath);
   core2md.AddArg(core_path.value());
@@ -505,11 +505,28 @@
   if (GetLogContents(FilePath(log_config_path_), exec, log_path))
     AddCrashMetaData("log", log_path.value());
 
-  char value[PROPERTY_VALUE_MAX];
-  property_get(kVersionProperty, value, "undefined");
-  AddCrashMetaUploadData("ver", value);
-  property_get(kProductIDProperty, value, "undefined");
-  AddCrashMetaUploadData("prod", value);
+  brillo::OsReleaseReader reader;
+  reader.Load();
+  std::string value = "undefined";
+  if (!reader.GetString(kBdkVersionKey, &value)) {
+    LOG(ERROR) << "Could not read " << kBdkVersionKey
+               << " from /etc/os-release.d/";
+  }
+  AddCrashMetaData(kBdkVersionKey, value);
+
+  value = "undefined";
+  if (!reader.GetString(kProductIDKey, &value)) {
+    LOG(ERROR) << "Could not read " << kProductIDKey
+               << " from /etc/os-release.d/";
+  }
+  AddCrashMetaData(kProductIDKey, value);
+
+  value = "undefined";
+  if (!reader.GetString(kProductVersionKey, &value)) {
+    LOG(ERROR) << "Could not read " << kProductVersionKey
+               << " from /etc/os-release.d/";
+  }
+  AddCrashMetaData(kProductVersionKey, value);
 
   ErrorType error_type =
       ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 1dfac2a..72e61e6 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -23,12 +23,12 @@
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
 #include <base/strings/string_split.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 using base::FilePath;
-using chromeos::FindLog;
+using brillo::FindLog;
 
 namespace {
 
@@ -73,7 +73,7 @@
     base::DeleteFile(FilePath("test"), true);
     mkdir("test", 0777);
     pid_ = getpid();
-    chromeos::ClearLog();
+    brillo::ClearLog();
   }
 
  protected:
@@ -222,7 +222,7 @@
       "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
   EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
 
-  chromeos::ClearLog();
+  brillo::ClearLog();
   pid_t my_pid = getpid();
   EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
   EXPECT_FALSE(FindLog("Readlink failed"));
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index b47199f..28fff3f 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -1,49 +1,59 @@
 # Copyright 2011 The Android Open Source Project
 
 LOCAL_PATH:= $(call my-dir)
+
+common_static_libraries := \
+    liblogwrap \
+    libfec \
+    libfec_rs \
+    libbase \
+    libmincrypt \
+    libcrypto_static \
+    libext4_utils_static \
+    libsquashfs_utils
+
 include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.cpp fs_mgr_fstab.c
-LOCAL_SRC_FILES += fs_mgr_format.c fs_mgr_slotselect.c
-
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= \
+    fs_mgr.c \
+    fs_mgr_format.c \
+    fs_mgr_fstab.c \
+    fs_mgr_slotselect.c \
+    fs_mgr_verity.cpp
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
     system/vold \
     system/extras/ext4_utils \
-    external/openssl/include
-
+    external/openssl/include \
+    bootable/recovery
 LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils libbase
-LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils \
-	bootable/recovery
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Werror
-
 ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
 LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
 endif
-
 include $(BUILD_STATIC_LIBRARY)
 
-
-
 include $(CLEAR_VARS)
-
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 LOCAL_SRC_FILES:= fs_mgr_main.c
-
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
 LOCAL_MODULE:= fs_mgr
-
 LOCAL_MODULE_TAGS := optional
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils libbase
-LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux
+LOCAL_STATIC_LIBRARIES := libfs_mgr \
+    $(common_static_libraries) \
+    libcutils \
+    liblog \
+    libc \
+    libsparse_static \
+    libz \
+    libselinux
 LOCAL_CXX_STL := libc++_static
-
 LOCAL_CFLAGS := -Werror
-
 include $(BUILD_EXECUTABLE)
-
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8f7c6a2..c358982 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -38,8 +38,7 @@
 #include "mincrypt/sha.h"
 #include "mincrypt/sha256.h"
 
-#include "ext4_sb.h"
-#include "squashfs_utils.h"
+#include "fec/io.h"
 
 #include "fs_mgr.h"
 #include "fs_mgr_priv.h"
@@ -47,11 +46,19 @@
 
 #define FSTAB_PREFIX "/fstab."
 
-#define VERITY_METADATA_SIZE 32768
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
 #define VERITY_TABLE_SALT_IDX 9
 
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+    "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
+    " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
 #define METADATA_MAGIC 0x01564c54
 #define METADATA_TAG_MAX_LENGTH 63
 #define METADATA_EOD "eod"
@@ -92,7 +99,7 @@
     }
 
     if (!fread(key, sizeof(*key), 1, f)) {
-        ERROR("Could not read key!");
+        ERROR("Could not read key!\n");
         fclose(f);
         free(key);
         return NULL;
@@ -109,7 +116,8 @@
     return key;
 }
 
-static int verify_table(char *signature, char *table, int table_length)
+static int verify_table(const uint8_t *signature, const char *table,
+        uint32_t table_length)
 {
     RSAPublicKey *key;
     uint8_t hash_buf[SHA256_DIGEST_SIZE];
@@ -121,17 +129,17 @@
     // Now get the public key from the keyfile
     key = load_key(VERITY_TABLE_RSA_KEY);
     if (!key) {
-        ERROR("Couldn't load verity keys");
+        ERROR("Couldn't load verity keys\n");
         goto out;
     }
 
     // verify the result
     if (!RSA_verify(key,
-                    (uint8_t*) signature,
+                    signature,
                     RSANUMBYTES,
                     (uint8_t*) hash_buf,
                     SHA256_DIGEST_SIZE)) {
-        ERROR("Couldn't verify table.");
+        ERROR("Couldn't verify table\n");
         goto out;
     }
 
@@ -142,11 +150,11 @@
     return retval;
 }
 
-static int invalidate_table(char *table, int table_length)
+static int invalidate_table(char *table, size_t table_length)
 {
-    int n = 0;
-    int idx = 0;
-    int cleared = 0;
+    size_t n = 0;
+    size_t idx = 0;
+    size_t cleared = 0;
 
     while (n < table_length) {
         if (table[n++] == ' ') {
@@ -169,177 +177,6 @@
     return -1;
 }
 
-static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
-    struct squashfs_info sq_info;
-
-    if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
-        *device_size = sq_info.bytes_used_4K_padded;
-        return 0;
-    } else {
-        return -1;
-    }
-}
-
-static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
-    int data_device;
-    struct ext4_super_block sb;
-    struct fs_info info;
-
-    info.len = 0;  /* Only len is set to 0 to ask the device for real size. */
-
-    data_device = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
-    if (data_device == -1) {
-        ERROR("Error opening block device (%s)", strerror(errno));
-        return -1;
-    }
-
-    if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
-        ERROR("Error seeking to superblock");
-        close(data_device);
-        return -1;
-    }
-
-    if (!android::base::ReadFully(data_device, &sb, sizeof(sb))) {
-        ERROR("Error reading superblock");
-        close(data_device);
-        return -1;
-    }
-
-    ext4_parse_sb(&sb, &info);
-    *device_size = info.len;
-
-    close(data_device);
-    return 0;
-}
-
-static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
-    if (!strcmp(fs_type, "ext4")) {
-        if (ext4_get_target_device_size(blk_device, device_size) < 0) {
-            ERROR("Failed to get ext4 fs size on %s.", blk_device);
-            return -1;
-        }
-    } else if (!strcmp(fs_type, "squashfs")) {
-        if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
-            ERROR("Failed to get squashfs fs size on %s.", blk_device);
-            return -1;
-        }
-    } else {
-        ERROR("%s: Unsupported filesystem for verity.", fs_type);
-        return -1;
-    }
-    return 0;
-}
-
-static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
-        char **table)
-{
-    unsigned magic_number;
-    unsigned table_length;
-    int protocol_version;
-    int device;
-    int retval = FS_MGR_SETUP_VERITY_FAIL;
-
-    *signature = NULL;
-
-    if (table) {
-        *table = NULL;
-    }
-
-    device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
-    if (device == -1) {
-        ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
-        ERROR("Could not seek to start of verity metadata block.\n");
-        goto out;
-    }
-    // check the magic number
-    if (!android::base::ReadFully(device, &magic_number, sizeof(magic_number))) {
-        ERROR("Couldn't read magic number!\n");
-        goto out;
-    }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
-    if (magic_number == VERITY_METADATA_MAGIC_DISABLE) {
-        retval = FS_MGR_SETUP_VERITY_DISABLED;
-        INFO("Attempt to cleanly disable verity - only works in USERDEBUG");
-        goto out;
-    }
-#endif
-
-    if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
-        ERROR("Couldn't find verity metadata at offset %" PRIu64 "!\n", device_size);
-        goto out;
-    }
-
-    // check the protocol version
-    if (!android::base::ReadFully(device, &protocol_version,
-            sizeof(protocol_version))) {
-        ERROR("Couldn't read verity metadata protocol version!\n");
-        goto out;
-    }
-    if (protocol_version != 0) {
-        ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
-        goto out;
-    }
-
-    // get the signature
-    *signature = (char*) malloc(RSANUMBYTES);
-    if (!*signature) {
-        ERROR("Couldn't allocate memory for signature!\n");
-        goto out;
-    }
-    if (!android::base::ReadFully(device, *signature, RSANUMBYTES)) {
-        ERROR("Couldn't read signature from verity metadata!\n");
-        goto out;
-    }
-
-    if (!table) {
-        retval = FS_MGR_SETUP_VERITY_SUCCESS;
-        goto out;
-    }
-
-    // get the size of the table
-    if (!android::base::ReadFully(device, &table_length, sizeof(table_length))) {
-        ERROR("Couldn't get the size of the verity table from metadata!\n");
-        goto out;
-    }
-
-    // get the table + null terminator
-    *table = static_cast<char*>(malloc(table_length + 1));
-    if (!*table) {
-        ERROR("Couldn't allocate memory for verity table!\n");
-        goto out;
-    }
-    if (!android::base::ReadFully(device, *table, table_length)) {
-        ERROR("Couldn't read the verity table from metadata!\n");
-        goto out;
-    }
-
-    (*table)[table_length] = 0;
-    retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
-    if (device != -1)
-        close(device);
-
-    if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
-        free(*signature);
-        *signature = NULL;
-
-        if (table) {
-            free(*table);
-            *table = NULL;
-        }
-    }
-
-    return retval;
-}
-
 static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
 {
     memset(io, 0, DM_BUF_SIZE);
@@ -379,8 +216,76 @@
     return 0;
 }
 
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
-        int mode)
+struct verity_table_params {
+    const char *table;
+    int mode;
+    struct fec_ecc_metadata ecc;
+    const char *ecc_dev;
+};
+
+typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
+        const struct verity_table_params *params);
+
+static bool format_verity_table(char *buf, const size_t bufsize,
+        const struct verity_table_params *params)
+{
+    const char *mode_flag = NULL;
+    int res = -1;
+
+    if (params->mode == VERITY_MODE_RESTART) {
+        mode_flag = VERITY_TABLE_OPT_RESTART;
+    } else if (params->mode == VERITY_MODE_LOGGING) {
+        mode_flag = VERITY_TABLE_OPT_LOGGING;
+    }
+
+    if (params->ecc.valid) {
+        if (mode_flag) {
+            res = snprintf(buf, bufsize,
+                    "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+                    params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
+                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+        } else {
+            res = snprintf(buf, bufsize,
+                    "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
+                    params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
+                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+        }
+    } else if (mode_flag) {
+        res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
+                    mode_flag);
+    } else {
+        res = strlcpy(buf, params->table, bufsize);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        ERROR("Error building verity table; insufficient buffer size?\n");
+        return false;
+    }
+
+    return true;
+}
+
+static bool format_legacy_verity_table(char *buf, const size_t bufsize,
+        const struct verity_table_params *params)
+{
+    int res;
+
+    if (params->mode == VERITY_MODE_EIO) {
+        res = strlcpy(buf, params->table, bufsize);
+    } else {
+        res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        ERROR("Error building verity table; insufficient buffer size?\n");
+        return false;
+    }
+
+    return true;
+}
+
+static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd,
+        const struct verity_table_params *params, format_verity_table_func format)
 {
     char *verity_params;
     char *buffer = (char*) io;
@@ -390,35 +295,32 @@
 
     struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
 
-    // set tgt arguments here
+    // set tgt arguments
     io->target_count = 1;
-    tgt->status=0;
-    tgt->sector_start=0;
-    tgt->length=device_size/512;
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = device_size / 512;
     strcpy(tgt->target_type, "verity");
 
-    // build the verity params here
+    // build the verity params
     verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
     bufsize = DM_BUF_SIZE - (verity_params - buffer);
 
-    if (mode == VERITY_MODE_EIO) {
-        // allow operation with older dm-verity drivers that are unaware
-        // of the mode parameter by omitting it; this also means that we
-        // cannot use logging mode with these drivers, they always cause
-        // an I/O error for corrupted blocks
-        strcpy(verity_params, table);
-    } else if (snprintf(verity_params, bufsize, "%s %d", table, mode) < 0) {
+    if (!format(verity_params, bufsize, params)) {
+        ERROR("Failed to format verity parameters\n");
         return -1;
     }
 
+    INFO("loading verity table: '%s'", verity_params);
+
     // set next target boundary
     verity_params += strlen(verity_params) + 1;
-    verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
+    verity_params = (char*)(((unsigned long)verity_params + 7) & ~8);
     tgt->next = verity_params - buffer;
 
     // send the ioctl to load the verity table
     if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        ERROR("Error loading verity table (%s)", strerror(errno));
+        ERROR("Error loading verity table (%s)\n", strerror(errno));
         return -1;
     }
 
@@ -703,28 +605,31 @@
 static int compare_last_signature(struct fstab_rec *fstab, int *match)
 {
     char tag[METADATA_TAG_MAX_LENGTH + 1];
-    char *signature = NULL;
     int fd = -1;
     int rc = -1;
+    off64_t offset = 0;
+    struct fec_handle *f = NULL;
+    struct fec_verity_metadata verity;
     uint8_t curr[SHA256_DIGEST_SIZE];
     uint8_t prev[SHA256_DIGEST_SIZE];
-    off64_t offset = 0;
-    uint64_t device_size;
 
     *match = 1;
 
-    // get verity filesystem size
-    if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
-        ERROR("Failed to get filesystem size\n");
+    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+            FEC_DEFAULT_ROOTS) == -1) {
+        ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
+        return rc;
+    }
+
+    // read verity metadata
+    if (fec_verity_get_metadata(f, &verity) == -1) {
+        ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
         goto out;
     }
 
-    if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
-        ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
-        goto out;
-    }
-
-    SHA256_hash(signature, RSANUMBYTES, curr);
+    SHA256_hash(verity.signature, RSANUMBYTES, curr);
 
     if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
             basename(fstab->mount_point)) >= (int)sizeof(tag)) {
@@ -766,12 +671,7 @@
     rc = 0;
 
 out:
-    free(signature);
-
-    if (fd != -1) {
-        close(fd);
-    }
-
+    fec_close(f);
     return rc;
 }
 
@@ -884,6 +784,7 @@
 {
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     bool use_state = true;
+    bool use_state_for_device = true;
     char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
     char *mount_point;
     char propbuf[PROPERTY_VALUE_MAX];
@@ -928,10 +829,12 @@
             continue;
         }
 
+        use_state_for_device = use_state;
+
         if (use_state) {
             if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
                 read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
-                continue;
+                use_state_for_device = false;
             }
         }
 
@@ -946,7 +849,7 @@
 
         status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
 
-        if (use_state && *status == 'C') {
+        if (use_state_for_device && *status == 'C') {
             if (write_verity_state(fstab->recs[i].verity_loc, offset,
                     VERITY_MODE_LOGGING) < 0) {
                 continue;
@@ -972,87 +875,105 @@
     return rc;
 }
 
-int fs_mgr_setup_verity(struct fstab_rec *fstab) {
-
+int fs_mgr_setup_verity(struct fstab_rec *fstab)
+{
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
-    int mode;
-
-    char *verity_blk_name = 0;
-    char *verity_table = 0;
-    char *verity_table_signature = 0;
-    int verity_table_length = 0;
-    uint64_t device_size = 0;
+    char *invalid_table = NULL;
+    char *verity_blk_name = NULL;
+    struct fec_handle *f = NULL;
+    struct fec_verity_metadata verity;
+    struct verity_table_params params;
 
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     char *mount_point = basename(fstab->mount_point);
 
-    // get verity filesystem size
-    if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+            FEC_DEFAULT_ROOTS) < 0) {
+        ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
         return retval;
     }
 
-    // read the verity block at the end of the block device
-    // send error code up the chain so we can detect attempts to disable verity
-    retval = read_verity_metadata(device_size,
-                                  fstab->blk_device,
-                                  &verity_table_signature,
-                                  &verity_table);
-    if (retval < 0) {
+    // read verity metadata
+    if (fec_verity_get_metadata(f, &verity) < 0) {
+        ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
         goto out;
     }
 
-    retval = FS_MGR_SETUP_VERITY_FAIL;
-    verity_table_length = strlen(verity_table);
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+    if (verity.disabled) {
+        retval = FS_MGR_SETUP_VERITY_DISABLED;
+        INFO("Attempt to cleanly disable verity - only works in USERDEBUG\n");
+        goto out;
+    }
+#endif
+
+    // read ecc metadata
+    if (fec_ecc_get_metadata(f, &params.ecc) < 0) {
+        params.ecc.valid = false;
+    }
+
+    params.ecc_dev = fstab->blk_device;
 
     // get the device mapper fd
     if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        ERROR("Error opening device mapper (%s)", strerror(errno));
+        ERROR("Error opening device mapper (%s)\n", strerror(errno));
         goto out;
     }
 
     // create the device
     if (create_verity_device(io, mount_point, fd) < 0) {
-        ERROR("Couldn't create verity device!");
+        ERROR("Couldn't create verity device!\n");
         goto out;
     }
 
     // get the name of the device file
     if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
-        ERROR("Couldn't get verity device number!");
+        ERROR("Couldn't get verity device number!\n");
         goto out;
     }
 
-    if (load_verity_state(fstab, &mode) < 0) {
+    if (load_verity_state(fstab, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
          * restart loop, and no corrupted data will be exposed to userspace
          * without a warning. */
-        mode = VERITY_MODE_EIO;
+        params.mode = VERITY_MODE_EIO;
     }
 
     // verify the signature on the table
-    if (verify_table(verity_table_signature,
-                            verity_table,
-                            verity_table_length) < 0) {
-        if (mode == VERITY_MODE_LOGGING) {
+    if (verify_table(verity.signature, verity.table,
+            verity.table_length) < 0) {
+        if (params.mode == VERITY_MODE_LOGGING) {
             // the user has been warned, allow mounting without dm-verity
             retval = FS_MGR_SETUP_VERITY_SUCCESS;
             goto out;
         }
 
         // invalidate root hash and salt to trigger device-specific recovery
-        if (invalidate_table(verity_table, verity_table_length) < 0) {
+        invalid_table = strdup(verity.table);
+
+        if (!invalid_table ||
+                invalidate_table(invalid_table, verity.table_length) < 0) {
             goto out;
         }
+
+        params.table = invalid_table;
+    } else {
+        params.table = verity.table;
     }
 
-    INFO("Enabling dm-verity for %s (mode %d)\n",  mount_point, mode);
+    INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, params.mode);
 
     // load the verity mapping table
-    if (load_verity_table(io, mount_point, device_size, fd, verity_table,
-            mode) < 0) {
+    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
+            format_verity_table) < 0 &&
+        // try the legacy format for backwards compatibility
+        load_verity_table(io, mount_point, verity.data_size, fd, &params,
+            format_legacy_verity_table) < 0) {
         goto out;
     }
 
@@ -1081,8 +1002,8 @@
         close(fd);
     }
 
-    free(verity_table);
-    free(verity_table_signature);
+    fec_close(f);
+    free(invalid_table);
     free(verity_blk_name);
 
     return retval;
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
new file mode 100644
index 0000000..9ee4abe
--- /dev/null
+++ b/include/ziparchive/zip_writer.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
+#define LIBZIPARCHIVE_ZIPWRITER_H_
+
+#include "base/macros.h"
+#include <utils/Compat.h>
+
+#include <cstdio>
+#include <string>
+#include <ctime>
+#include <vector>
+
+/**
+ * Writes a Zip file via a stateful interface.
+ *
+ * Example:
+ *
+ *   FILE* file = fopen("path/to/zip.zip", "wb");
+ *
+ *   ZipWriter writer(file);
+ *
+ *   writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
+ *   writer.WriteBytes(buffer, bufferLen);
+ *   writer.WriteBytes(buffer2, bufferLen2);
+ *   writer.FinishEntry();
+ *
+ *   writer.StartEntry("empty.txt", 0);
+ *   writer.FinishEntry();
+ *
+ *   writer.Finish();
+ *
+ *   fclose(file);
+ */
+class ZipWriter {
+public:
+  enum {
+    /**
+     * Flag to compress the zip entry using deflate.
+     */
+    kCompress = 0x01,
+
+    /**
+     * Flag to align the zip entry data on a 32bit boundary. Useful for
+     * mmapping the data at runtime.
+     */
+    kAlign32 = 0x02,
+  };
+
+  static const char* ErrorCodeString(int32_t error_code);
+
+  /**
+   * Create a ZipWriter that will write into a FILE stream. The file should be opened with
+   * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
+   * caller is responsible for closing the file.
+   */
+  explicit ZipWriter(FILE* f);
+
+  // Move constructor.
+  ZipWriter(ZipWriter&& zipWriter);
+
+  // Move assignment.
+  ZipWriter& operator=(ZipWriter&& zipWriter);
+
+  /**
+   * Starts a new zip entry with the given path and flags.
+   * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
+   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t StartEntry(const char* path, size_t flags);
+
+  /**
+   * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
+   */
+  int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+
+  /**
+   * Writes bytes to the zip file for the previously started zip entry.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t WriteBytes(const void* data, size_t len);
+
+  /**
+   * Finish a zip entry started with StartEntry(const char*, size_t) or
+   * StartEntryWithTime(const char*, size_t, time_t). This must be called before
+   * any new zip entries are started, or before Finish() is called.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t FinishEntry();
+
+  /**
+   * Writes the Central Directory Headers and flushes the zip file stream.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t Finish();
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(ZipWriter);
+
+  int32_t HandleError(int32_t error_code);
+
+  struct FileInfo {
+    std::string path;
+    uint16_t compression_method;
+    uint32_t crc32;
+    uint32_t compressed_size;
+    uint32_t uncompressed_size;
+    uint16_t last_mod_time;
+    uint16_t last_mod_date;
+    uint32_t local_file_header_offset;
+  };
+
+  enum class State {
+    kWritingZip,
+    kWritingEntry,
+    kDone,
+    kError,
+  };
+
+  FILE* file_;
+  off64_t current_offset_;
+  State state_;
+  std::vector<FileInfo> files_;
+};
+
+#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/init/Android.mk b/init/Android.mk
index c1cdc1a..8e45a7a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -84,6 +84,8 @@
 LOCAL_STATIC_LIBRARIES := \
     libinit \
     libfs_mgr \
+    libfec \
+    libfec_rs \
     libsquashfs_utils \
     liblogwrap \
     libcutils \
@@ -94,6 +96,7 @@
     libc \
     libselinux \
     libmincrypt \
+    libcrypto_static \
     libc++_static \
     libdl \
     libsparse_static \
diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/tests/MemsetTest.cpp
index 45efc51..a98485f 100644
--- a/libcutils/tests/MemsetTest.cpp
+++ b/libcutils/tests/MemsetTest.cpp
@@ -20,6 +20,8 @@
 #include <sys/mman.h>
 #include <sys/types.h>
 
+#include <memory>
+
 #include <cutils/memory.h>
 #include <gtest/gtest.h>
 
@@ -127,14 +129,14 @@
     min_incr = 2;
     value |= value << 16;
   }
-  uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)];
+  std::unique_ptr<uint32_t[]> expected_buf(new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)]);
   for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) {
     expected_buf[i] = value;
   }
 
   // Allocate one large buffer with lots of extra space so that we can
   // guarantee that all possible alignments will fit.
-  uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE];
+  std::unique_ptr<uint8_t[]> buf(new uint8_t[3*MAX_TEST_SIZE]);
   uint8_t *buf_align;
   for (size_t i = 0; i < num_aligns; i++) {
     size_t incr = min_incr;
@@ -142,7 +144,7 @@
       incr = GetIncrement(len, min_incr);
 
       buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
-          buf+FENCEPOST_LENGTH, align[i][0], align[i][1]));
+          buf.get()+FENCEPOST_LENGTH, align[i][0], align[i][1]));
 
       SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
       SetFencepost(&buf_align[len]);
@@ -153,15 +155,13 @@
       } else {
         android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len);
       }
-      ASSERT_EQ(0, memcmp(expected_buf, buf_align, len))
+      ASSERT_EQ(0, memcmp(expected_buf.get(), buf_align, len))
           << "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n";
 
       VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
       VerifyFencepost(&buf_align[len]);
     }
   }
-  delete expected_buf;
-  delete buf;
 }
 
 TEST(libcutils, android_memset16_non_zero) {
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 608ff1c..8ff94d4 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -15,7 +15,12 @@
 
 LOCAL_PATH := $(call my-dir)
 
-source_files := zip_archive.cc
+source_files := zip_archive.cc zip_writer.cc
+test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
+
+# Incorrectly warns when C++11 empty brace {} initializer is used.
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
+common_cpp_flags := -Wno-missing-field-initializers
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
@@ -24,7 +29,7 @@
 LOCAL_SHARED_LIBRARIES := libutils libbase
 LOCAL_MODULE:= libziparchive
 LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast
+LOCAL_CPPFLAGS := -Wold-style-cast $(common_cpp_flags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -34,6 +39,8 @@
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
 LOCAL_CFLAGS_windows := -mno-ms-bitfields
+LOCAL_CPPFLAGS := $(common_cpp_flags)
+
 LOCAL_MULTILIB := both
 LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
@@ -45,6 +52,7 @@
 LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
+LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -53,7 +61,8 @@
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_CFLAGS := -Werror
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_CPPFLAGS := $(common_cpp_flags)
+LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
 include $(BUILD_NATIVE_TEST)
@@ -61,10 +70,9 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests-host
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += \
-    -Werror \
-    -Wno-unnamed-type-template-args
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_CFLAGS := -Werror
+LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
+LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
 LOCAL_STATIC_LIBRARIES := \
     libz \
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3716343..f1e13a7 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -39,6 +39,7 @@
 #include "zlib.h"
 
 #include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
 #include "ziparchive/zip_archive.h"
 
 using android::base::get_unaligned;
@@ -49,161 +50,6 @@
 #define O_BINARY 0
 #endif
 
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
-  static const uint32_t kSignature = 0x06054b50;
-
-  // End of central directory signature, should always be
-  // |kSignature|.
-  uint32_t eocd_signature;
-  // The number of the current "disk", i.e, the "disk" that this
-  // central directory is on.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that disk_num == 1.
-  uint16_t disk_num;
-  // The disk where the central directory starts.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that cd_start_disk == 1.
-  uint16_t cd_start_disk;
-  // The number of central directory records on this disk.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that num_records_on_disk == num_records.
-  uint16_t num_records_on_disk;
-  // The total number of central directory records.
-  uint16_t num_records;
-  // The size of the central directory (in bytes).
-  uint32_t cd_size;
-  // The offset of the start of the central directory, relative
-  // to the start of the file.
-  uint32_t cd_start_offset;
-  // Length of the central directory comment.
-  uint16_t comment_length;
- private:
-  EocdRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
-  static const uint32_t kSignature = 0x02014b50;
-
-  // The start of record signature. Must be |kSignature|.
-  uint32_t record_signature;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_made_by;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_needed;
-  // The "general purpose bit flags" for this entry. The only
-  // flag value that we currently check for is the "data descriptor"
-  // flag.
-  uint16_t gpb_flags;
-  // The compression method for this entry, one of |kCompressStored|
-  // and |kCompressDeflated|.
-  uint16_t compression_method;
-  // The file modification time and date for this entry.
-  uint16_t last_mod_time;
-  uint16_t last_mod_date;
-  // The CRC-32 checksum for this entry.
-  uint32_t crc32;
-  // The compressed size (in bytes) of this entry.
-  uint32_t compressed_size;
-  // The uncompressed size (in bytes) of this entry.
-  uint32_t uncompressed_size;
-  // The length of the entry file name in bytes. The file name
-  // will appear immediately after this record.
-  uint16_t file_name_length;
-  // The length of the extra field info (in bytes). This data
-  // will appear immediately after the entry file name.
-  uint16_t extra_field_length;
-  // The length of the entry comment (in bytes). This data will
-  // appear immediately after the extra field.
-  uint16_t comment_length;
-  // The start disk for this entry. Ignored by this implementation).
-  uint16_t file_start_disk;
-  // File attributes. Ignored by this implementation.
-  uint16_t internal_file_attributes;
-  // File attributes. Ignored by this implementation.
-  uint32_t external_file_attributes;
-  // The offset to the local file header for this entry, from the
-  // beginning of this archive.
-  uint32_t local_file_header_offset;
- private:
-  CentralDirectoryRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
-  static const uint32_t kSignature = 0x04034b50;
-
-  // The local file header signature, must be |kSignature|.
-  uint32_t lfh_signature;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_needed;
-  // The "general purpose bit flags" for this entry. The only
-  // flag value that we currently check for is the "data descriptor"
-  // flag.
-  uint16_t gpb_flags;
-  // The compression method for this entry, one of |kCompressStored|
-  // and |kCompressDeflated|.
-  uint16_t compression_method;
-  // The file modification time and date for this entry.
-  uint16_t last_mod_time;
-  uint16_t last_mod_date;
-  // The CRC-32 checksum for this entry.
-  uint32_t crc32;
-  // The compressed size (in bytes) of this entry.
-  uint32_t compressed_size;
-  // The uncompressed size (in bytes) of this entry.
-  uint32_t uncompressed_size;
-  // The length of the entry file name in bytes. The file name
-  // will appear immediately after this record.
-  uint16_t file_name_length;
-  // The length of the extra field info (in bytes). This data
-  // will appear immediately after the entry file name.
-  uint16_t extra_field_length;
- private:
-  LocalFileHeader() = default;
-  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
-  // The *optional* data descriptor start signature.
-  static const uint32_t kOptSignature = 0x08074b50;
-
-  // CRC-32 checksum of the entry.
-  uint32_t crc32;
-  // Compressed size of the entry.
-  uint32_t compressed_size;
-  // Uncompressed size of the entry.
-  uint32_t uncompressed_size;
- private:
-  DataDescriptor() = default;
-  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-} __attribute__((packed));
-
-
-static const uint32_t kGPBDDFlagMask = 0x0008;         // mask value that signifies that the entry has a DD
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
new file mode 100644
index 0000000..7f20d51
--- /dev/null
+++ b/libziparchive/zip_archive_common.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+
+#include "base/macros.h"
+
+#include <inttypes.h>
+
+// The "end of central directory" (EOCD) record. Each archive
+// contains exactly once such record which appears at the end of
+// the archive. It contains archive wide information like the
+// number of entries in the archive and the offset to the central
+// directory of the offset.
+struct EocdRecord {
+  static const uint32_t kSignature = 0x06054b50;
+
+  // End of central directory signature, should always be
+  // |kSignature|.
+  uint32_t eocd_signature;
+  // The number of the current "disk", i.e, the "disk" that this
+  // central directory is on.
+  //
+  // This implementation assumes that each archive spans a single
+  // disk only. i.e, that disk_num == 1.
+  uint16_t disk_num;
+  // The disk where the central directory starts.
+  //
+  // This implementation assumes that each archive spans a single
+  // disk only. i.e, that cd_start_disk == 1.
+  uint16_t cd_start_disk;
+  // The number of central directory records on this disk.
+  //
+  // This implementation assumes that each archive spans a single
+  // disk only. i.e, that num_records_on_disk == num_records.
+  uint16_t num_records_on_disk;
+  // The total number of central directory records.
+  uint16_t num_records;
+  // The size of the central directory (in bytes).
+  uint32_t cd_size;
+  // The offset of the start of the central directory, relative
+  // to the start of the file.
+  uint32_t cd_start_offset;
+  // Length of the central directory comment.
+  uint16_t comment_length;
+ private:
+  EocdRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
+} __attribute__((packed));
+
+// A structure representing the fixed length fields for a single
+// record in the central directory of the archive. In addition to
+// the fixed length fields listed here, each central directory
+// record contains a variable length "file_name" and "extra_field"
+// whose lengths are given by |file_name_length| and |extra_field_length|
+// respectively.
+struct CentralDirectoryRecord {
+  static const uint32_t kSignature = 0x02014b50;
+
+  // The start of record signature. Must be |kSignature|.
+  uint32_t record_signature;
+  // Tool version. Ignored by this implementation.
+  uint16_t version_made_by;
+  // Tool version. Ignored by this implementation.
+  uint16_t version_needed;
+  // The "general purpose bit flags" for this entry. The only
+  // flag value that we currently check for is the "data descriptor"
+  // flag.
+  uint16_t gpb_flags;
+  // The compression method for this entry, one of |kCompressStored|
+  // and |kCompressDeflated|.
+  uint16_t compression_method;
+  // The file modification time and date for this entry.
+  uint16_t last_mod_time;
+  uint16_t last_mod_date;
+  // The CRC-32 checksum for this entry.
+  uint32_t crc32;
+  // The compressed size (in bytes) of this entry.
+  uint32_t compressed_size;
+  // The uncompressed size (in bytes) of this entry.
+  uint32_t uncompressed_size;
+  // The length of the entry file name in bytes. The file name
+  // will appear immediately after this record.
+  uint16_t file_name_length;
+  // The length of the extra field info (in bytes). This data
+  // will appear immediately after the entry file name.
+  uint16_t extra_field_length;
+  // The length of the entry comment (in bytes). This data will
+  // appear immediately after the extra field.
+  uint16_t comment_length;
+  // The start disk for this entry. Ignored by this implementation).
+  uint16_t file_start_disk;
+  // File attributes. Ignored by this implementation.
+  uint16_t internal_file_attributes;
+  // File attributes. Ignored by this implementation.
+  uint32_t external_file_attributes;
+  // The offset to the local file header for this entry, from the
+  // beginning of this archive.
+  uint32_t local_file_header_offset;
+ private:
+  CentralDirectoryRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+} __attribute__((packed));
+
+// The local file header for a given entry. This duplicates information
+// present in the central directory of the archive. It is an error for
+// the information here to be different from the central directory
+// information for a given entry.
+struct LocalFileHeader {
+  static const uint32_t kSignature = 0x04034b50;
+
+  // The local file header signature, must be |kSignature|.
+  uint32_t lfh_signature;
+  // Tool version. Ignored by this implementation.
+  uint16_t version_needed;
+  // The "general purpose bit flags" for this entry. The only
+  // flag value that we currently check for is the "data descriptor"
+  // flag.
+  uint16_t gpb_flags;
+  // The compression method for this entry, one of |kCompressStored|
+  // and |kCompressDeflated|.
+  uint16_t compression_method;
+  // The file modification time and date for this entry.
+  uint16_t last_mod_time;
+  uint16_t last_mod_date;
+  // The CRC-32 checksum for this entry.
+  uint32_t crc32;
+  // The compressed size (in bytes) of this entry.
+  uint32_t compressed_size;
+  // The uncompressed size (in bytes) of this entry.
+  uint32_t uncompressed_size;
+  // The length of the entry file name in bytes. The file name
+  // will appear immediately after this record.
+  uint16_t file_name_length;
+  // The length of the extra field info (in bytes). This data
+  // will appear immediately after the entry file name.
+  uint16_t extra_field_length;
+ private:
+  LocalFileHeader() = default;
+  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+} __attribute__((packed));
+
+struct DataDescriptor {
+  // The *optional* data descriptor start signature.
+  static const uint32_t kOptSignature = 0x08074b50;
+
+  // CRC-32 checksum of the entry.
+  uint32_t crc32;
+  // Compressed size of the entry.
+  uint32_t compressed_size;
+  // Uncompressed size of the entry.
+  uint32_t uncompressed_size;
+ private:
+  DataDescriptor() = default;
+  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+} __attribute__((packed));
+
+// mask value that signifies that the entry has a DD
+static const uint32_t kGPBDDFlagMask = 0x0008;
+
+// The maximum size of a central directory or a file
+// comment in bytes.
+static const uint32_t kMaxCommentLen = 65535;
+
+#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
new file mode 100644
index 0000000..de75d1e
--- /dev/null
+++ b/libziparchive/zip_writer.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+#include "ziparchive/zip_writer.h"
+
+#include <cassert>
+#include <cstdio>
+#include <memory>
+#include <zlib.h>
+
+/* Zip compression methods we support */
+enum {
+  kCompressStored     = 0,        // no compression
+  kCompressDeflated   = 8,        // standard deflate
+};
+
+// No error, operation completed successfully.
+static const int32_t kNoError = 0;
+
+// The ZipWriter is in a bad state.
+static const int32_t kInvalidState = -1;
+
+// There was an IO error while writing to disk.
+static const int32_t kIoError = -2;
+
+// The zip entry name was invalid.
+static const int32_t kInvalidEntryName = -3;
+
+static const char* sErrorCodes[] = {
+    "Invalid state",
+    "IO error",
+    "Invalid entry name",
+};
+
+const char* ZipWriter::ErrorCodeString(int32_t error_code) {
+  if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
+    return sErrorCodes[-error_code];
+  }
+  return nullptr;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip) {
+}
+
+ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+                                           current_offset_(writer.current_offset_),
+                                           state_(writer.state_),
+                                           files_(std::move(writer.files_)) {
+  writer.file_ = nullptr;
+  writer.state_ = State::kError;
+}
+
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+  file_ = writer.file_;
+  current_offset_ = writer.current_offset_;
+  state_ = writer.state_;
+  files_ = std::move(writer.files_);
+  writer.file_ = nullptr;
+  writer.state_ = State::kError;
+  return *this;
+}
+
+int32_t ZipWriter::HandleError(int32_t error_code) {
+  state_ = State::kError;
+  return error_code;
+}
+
+int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+  return StartEntryWithTime(path, flags, time_t());
+}
+
+static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
+  /* round up to an even number of seconds */
+  when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
+
+  struct tm* ptm;
+#if !defined(_WIN32)
+    struct tm tm_result;
+    ptm = localtime_r(&when, &tm_result);
+#else
+    ptm = localtime(&when);
+#endif
+
+  int year = ptm->tm_year;
+  if (year < 80) {
+    year = 80;
+  }
+
+  *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
+  *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+}
+
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+  if (state_ != State::kWritingZip) {
+    return kInvalidState;
+  }
+
+  FileInfo fileInfo = {};
+  fileInfo.path = std::string(path);
+  fileInfo.local_file_header_offset = current_offset_;
+
+  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(fileInfo.path.data()),
+                       fileInfo.path.size())) {
+    return kInvalidEntryName;
+  }
+
+  LocalFileHeader header = {};
+  header.lfh_signature = LocalFileHeader::kSignature;
+
+  // Set this flag to denote that a DataDescriptor struct will appear after the data,
+  // containing the crc and size fields.
+  header.gpb_flags |= kGPBDDFlagMask;
+
+  // For now, ignore the ZipWriter::kCompress flag.
+  fileInfo.compression_method = kCompressStored;
+  header.compression_method = fileInfo.compression_method;
+
+  ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
+  header.last_mod_time = fileInfo.last_mod_time;
+  header.last_mod_date = fileInfo.last_mod_date;
+
+  header.file_name_length = fileInfo.path.size();
+
+  off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
+  if ((flags & ZipWriter::kAlign32) && (offset & 0x03)) {
+    // Pad the extra field so the data will be aligned.
+    uint16_t padding = 4 - (offset % 4);
+    header.extra_field_length = padding;
+    offset += padding;
+  }
+
+  if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+    return HandleError(kIoError);
+  }
+
+  if (fwrite(path, sizeof(*path), fileInfo.path.size(), file_) != fileInfo.path.size()) {
+    return HandleError(kIoError);
+  }
+
+  if (fwrite("\0\0\0", 1, header.extra_field_length, file_) != header.extra_field_length) {
+    return HandleError(kIoError);
+  }
+
+  files_.emplace_back(std::move(fileInfo));
+
+  current_offset_ = offset;
+  state_ = State::kWritingEntry;
+  return kNoError;
+}
+
+int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
+  if (state_ != State::kWritingEntry) {
+    return HandleError(kInvalidState);
+  }
+
+  FileInfo& currentFile = files_.back();
+  if (currentFile.compression_method & kCompressDeflated) {
+    // TODO(adamlesinski): Implement compression using zlib deflate.
+    assert(false);
+  } else {
+    if (fwrite(data, 1, len, file_) != len) {
+      return HandleError(kIoError);
+    }
+    currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
+    currentFile.compressed_size += len;
+    current_offset_ += len;
+  }
+
+  currentFile.uncompressed_size += len;
+  return kNoError;
+}
+
+int32_t ZipWriter::FinishEntry() {
+  if (state_ != State::kWritingEntry) {
+    return kInvalidState;
+  }
+
+  const uint32_t sig = DataDescriptor::kOptSignature;
+  if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+    state_ = State::kError;
+    return kIoError;
+  }
+
+  FileInfo& currentFile = files_.back();
+  DataDescriptor dd = {};
+  dd.crc32 = currentFile.crc32;
+  dd.compressed_size = currentFile.compressed_size;
+  dd.uncompressed_size = currentFile.uncompressed_size;
+  if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+    return HandleError(kIoError);
+  }
+
+  current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+  state_ = State::kWritingZip;
+  return kNoError;
+}
+
+int32_t ZipWriter::Finish() {
+  if (state_ != State::kWritingZip) {
+    return kInvalidState;
+  }
+
+  off64_t startOfCdr = current_offset_;
+  for (FileInfo& file : files_) {
+    CentralDirectoryRecord cdr = {};
+    cdr.record_signature = CentralDirectoryRecord::kSignature;
+    cdr.gpb_flags |= kGPBDDFlagMask;
+    cdr.compression_method = file.compression_method;
+    cdr.last_mod_time = file.last_mod_time;
+    cdr.last_mod_date = file.last_mod_date;
+    cdr.crc32 = file.crc32;
+    cdr.compressed_size = file.compressed_size;
+    cdr.uncompressed_size = file.uncompressed_size;
+    cdr.file_name_length = file.path.size();
+    cdr.local_file_header_offset = file.local_file_header_offset;
+    if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+
+    if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
+      return HandleError(kIoError);
+    }
+
+    current_offset_ += sizeof(cdr) + file.path.size();
+  }
+
+  EocdRecord er = {};
+  er.eocd_signature = EocdRecord::kSignature;
+  er.disk_num = 1;
+  er.cd_start_disk = 1;
+  er.num_records_on_disk = files_.size();
+  er.num_records = files_.size();
+  er.cd_size = current_offset_ - startOfCdr;
+  er.cd_start_offset = startOfCdr;
+
+  if (fwrite(&er, sizeof(er), 1, file_) != 1) {
+    return HandleError(kIoError);
+  }
+
+  if (fflush(file_) != 0) {
+    return HandleError(kIoError);
+  }
+
+  current_offset_ += sizeof(er);
+  state_ = State::kDone;
+  return kNoError;
+}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
new file mode 100644
index 0000000..5269730
--- /dev/null
+++ b/libziparchive/zip_writer_test.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ziparchive/zip_archive.h"
+#include "ziparchive/zip_writer.h"
+
+#include <base/test_utils.h>
+#include <gtest/gtest.h>
+#include <memory>
+
+struct zipwriter : public ::testing::Test {
+  TemporaryFile* temp_file_;
+  int fd_;
+  FILE* file_;
+
+  void SetUp() override {
+    temp_file_ = new TemporaryFile();
+    fd_ = temp_file_->fd;
+    file_ = fdopen(fd_, "w");
+    ASSERT_NE(file_, nullptr);
+  }
+
+  void TearDown() override {
+    fclose(file_);
+    delete temp_file_;
+  }
+};
+
+TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
+  ZipWriter writer(file_);
+
+  const char* expected = "hello";
+
+  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
+  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
+  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
+  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(writer.Finish(), 0);
+
+  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+
+  ZipEntry data;
+  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
+  EXPECT_EQ(data.compressed_length, strlen(expected));
+  EXPECT_EQ(data.uncompressed_length, strlen(expected));
+  EXPECT_EQ(data.method, kCompressStored);
+
+  char buffer[6];
+  EXPECT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)),
+            0);
+  buffer[5] = 0;
+
+  EXPECT_STREQ(expected, buffer);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
+  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
+  ASSERT_EQ(writer.FinishEntry(), 0);
+
+  ASSERT_EQ(writer.StartEntry("file/file.txt", 0), 0);
+  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
+  ASSERT_EQ(writer.FinishEntry(), 0);
+
+  ASSERT_EQ(writer.StartEntry("file/file2.txt", 0), 0);
+  ASSERT_EQ(writer.FinishEntry(), 0);
+
+  ASSERT_EQ(writer.Finish(), 0);
+
+  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+
+  char buffer[4];
+  ZipEntry data;
+
+  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
+  EXPECT_EQ(data.method, kCompressStored);
+  EXPECT_EQ(data.compressed_length, 2u);
+  EXPECT_EQ(data.uncompressed_length, 2u);
+  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
+            0);
+  buffer[2] = 0;
+  EXPECT_STREQ("he", buffer);
+
+  ASSERT_EQ(FindEntry(handle, ZipString("file/file.txt"), &data), 0);
+  EXPECT_EQ(data.method, kCompressStored);
+  EXPECT_EQ(data.compressed_length, 3u);
+  EXPECT_EQ(data.uncompressed_length, 3u);
+  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
+            0);
+  buffer[3] = 0;
+  EXPECT_STREQ("llo", buffer);
+
+  ASSERT_EQ(FindEntry(handle, ZipString("file/file2.txt"), &data), 0);
+  EXPECT_EQ(data.method, kCompressStored);
+  EXPECT_EQ(data.compressed_length, 0u);
+  EXPECT_EQ(data.uncompressed_length, 0u);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(writer.StartEntry("align.txt", ZipWriter::kAlign32), 0);
+  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
+  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(writer.Finish(), 0);
+
+  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+
+  ZipEntry data;
+  ASSERT_EQ(FindEntry(handle, ZipString("align.txt"), &data), 0);
+  EXPECT_EQ(data.offset & 0x03, 0);
+}
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1b68386..1de8e64 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -225,8 +225,11 @@
     unsigned long maxSize = log_buffer_size(id);
     if (sizes > maxSize) {
         size_t sizeOver = sizes - ((maxSize * 9) / 10);
-        size_t elements = stats.elements(id);
-        size_t minElements = elements / 10;
+        size_t elements = stats.realElements(id);
+        size_t minElements = elements / 100;
+        if (minElements < minPrune) {
+            minElements = minPrune;
+        }
         unsigned long pruneRows = elements * sizeOver / sizes;
         if (pruneRows < minElements) {
             pruneRows = minElements;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 5223b40..7ed92e9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -86,6 +86,7 @@
 
 private:
 
+    static constexpr size_t minPrune = 4;
     static constexpr size_t maxPrune = 256;
 
     void maybePrune(log_id_t id);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index c3b10ad..fdb2576 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -27,6 +27,7 @@
     log_id_for_each(id) {
         mSizes[id] = 0;
         mElements[id] = 0;
+        mDroppedElements[id] = 0;
         mSizesTotal[id] = 0;
         mElementsTotal[id] = 0;
     }
@@ -93,6 +94,9 @@
     unsigned short size = element->getMsgLen();
     mSizes[log_id] -= size;
     --mElements[log_id];
+    if (element->getDropped()) {
+        --mDroppedElements[log_id];
+    }
 
     if (log_id == LOG_ID_KERNEL) {
         return;
@@ -119,6 +123,7 @@
     log_id_t log_id = element->getLogId();
     unsigned short size = element->getMsgLen();
     mSizes[log_id] -= size;
+    ++mDroppedElements[log_id];
 
     uidTable[log_id].drop(element->getUid(), element);
 
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 8b90c53..6943820 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -374,6 +374,7 @@
 class LogStatistics {
     size_t mSizes[LOG_ID_MAX];
     size_t mElements[LOG_ID_MAX];
+    size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
     bool enable;
@@ -404,7 +405,11 @@
     // entry->setDropped(1) must follow this call
     void drop(LogBufferElement *entry);
     // Correct for coalescing two entries referencing dropped content
-    void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+    void erase(LogBufferElement *element) {
+        log_id_t log_id = element->getLogId();
+        --mElements[log_id];
+        --mDroppedElements[log_id];
+    }
 
     std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
         return uidTable[id].sort(len);
@@ -413,6 +418,9 @@
     // fast track current value by id only
     size_t sizes(log_id_t id) const { return mSizes[id]; }
     size_t elements(log_id_t id) const { return mElements[id]; }
+    size_t realElements(log_id_t id) const {
+        return mElements[id] - mDroppedElements[id];
+    }
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 3deb7d3..8dbaad9 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -69,11 +69,11 @@
   libchrome-dbus \
   libchromeos-http \
   libchromeos-dbus \
-  libcutils \
   libdbus \
   libmetrics \
   libprotobuf-cpp-lite \
   librootdev \
+  libupdate_engine_client \
   libweaved \
 
 # Shared library for metrics.
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 8a00fc4..7e1e116 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -27,10 +27,9 @@
 static const char kFailedUploadCountName[] = "failed_upload_count";
 static const char kDefaultVersion[] = "0.0.0.0";
 
-// System properties used.
-static const char kProductIdProperty[] = "ro.product.product_id";
-static const char kChannelProperty[] = "ro.product.channel";
-static const char kProductVersionProperty[] = "ro.product.version";
+// Build time properties name.
+static const char kProductId[] = "product_id";
+static const char kProductVersion[] = "product_version";
 }  // namespace metrics
 
 #endif  // METRICS_CONSTANTS_H_
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 2ea5bbd..ed786e1 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -28,7 +28,7 @@
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <cutils/properties.h>
+#include <brillo/osrelease_reader.h>
 #include <dbus/dbus.h>
 #include <dbus/message.h>
 
@@ -140,7 +140,7 @@
     version_cumulative_cpu_use_->Set(0);
   }
 
-  return chromeos::DBusDaemon::Run();
+  return brillo::DBusDaemon::Run();
 }
 
 void MetricsDaemon::RunUploaderTest() {
@@ -153,23 +153,19 @@
 }
 
 uint32_t MetricsDaemon::GetOsVersionHash() {
-  static uint32_t cached_version_hash = 0;
-  static bool version_hash_is_cached = false;
-  if (version_hash_is_cached)
-    return cached_version_hash;
-  version_hash_is_cached = true;
-
-  char version[PROPERTY_VALUE_MAX];
-  // The version might not be set for development devices. In this case, use the
-  // zero version.
-  property_get(metrics::kProductVersionProperty, version,
-               metrics::kDefaultVersion);
-
-  cached_version_hash = base::Hash(version);
-  if (testing_) {
-    cached_version_hash = 42;  // return any plausible value for the hash
+  brillo::OsReleaseReader reader;
+  reader.Load();
+  string version;
+  if (!reader.GetString(metrics::kProductVersion, &version)) {
+    LOG(ERROR) << "failed to read the product version.";
+    version = metrics::kDefaultVersion;
   }
-  return cached_version_hash;
+
+  uint32_t version_hash = base::Hash(version);
+  if (testing_) {
+    version_hash = 42;  // return any plausible value for the hash
+  }
+  return version_hash;
 }
 
 void MetricsDaemon::Init(bool testing,
@@ -242,8 +238,8 @@
 }
 
 int MetricsDaemon::OnInit() {
-  int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
-      chromeos::Daemon::OnInit();
+  int return_code = dbus_enabled_ ? brillo::DBusDaemon::OnInit() :
+      brillo::Daemon::OnInit();
   if (return_code != EX_OK)
     return return_code;
 
@@ -326,7 +322,7 @@
           << error.name << ": " << error.message;
     }
   }
-  chromeos::DBusDaemon::OnShutdown(return_code);
+  brillo::DBusDaemon::OnShutdown(return_code);
 }
 
 void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
@@ -368,7 +364,7 @@
   if (!device_)
     return;
 
-  chromeos::VariantDictionary state_change{
+  brillo::VariantDictionary state_change{
     { "_metrics._AnalyticsReportingState",
       metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
   };
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 146108a..3d691c5 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -26,7 +26,7 @@
 #include <base/files/file_path.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/time/time.h>
-#include <chromeos/daemons/dbus_daemon.h>
+#include <brillo/daemons/dbus_daemon.h>
 #include <libweaved/command.h>
 #include <libweaved/device.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
@@ -39,7 +39,7 @@
 
 using chromeos_metrics::PersistentInteger;
 
-class MetricsDaemon : public chromeos::DBusDaemon {
+class MetricsDaemon : public brillo::DBusDaemon {
  public:
   MetricsDaemon();
   ~MetricsDaemon();
diff --git a/metricsd/metrics_daemon.rc b/metricsd/metrics_daemon.rc
index 0e1fcd5..0ee577e 100644
--- a/metricsd/metrics_daemon.rc
+++ b/metricsd/metrics_daemon.rc
@@ -1,4 +1,4 @@
-on boot
+on post-fs-data
     mkdir /data/misc/metrics 0770 system system
 
 service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
index df13944..50c279d 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -18,8 +18,8 @@
 #include <base/command_line.h>
 #include <base/logging.h>
 #include <base/strings/string_util.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
 #include <rootdev.h>
 
 #include "constants.h"
@@ -79,11 +79,11 @@
                 metrics::kMetricsDirectory,
                 "Root of the configuration files (testing only)");
 
-  chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
+  brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
 
   // Also log to stderr when not running as daemon.
-  chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader |
-                    (FLAGS_daemon ? 0 : chromeos::kLogToStderr));
+  brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader |
+                  (FLAGS_daemon ? 0 : brillo::kLogToStderr));
 
   if (FLAGS_daemon && daemon(0, 0) != 0) {
     return errno;
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
index 3a8fc3a..d3c9a23 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -20,7 +20,7 @@
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
 #include <base/strings/string_number_conversions.h>
-#include <chromeos/flag_helper.h>
+#include <brillo/flag_helper.h>
 #include <gtest/gtest.h>
 
 #include "constants.h"
@@ -43,7 +43,7 @@
 class MetricsDaemonTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    chromeos::FlagHelper::Init(0, nullptr, "");
+    brillo::FlagHelper::Init(0, nullptr, "");
     EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
     scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
     cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
index 953afc1..4b572a6 100644
--- a/metricsd/uploader/sender_http.cc
+++ b/metricsd/uploader/sender_http.cc
@@ -20,8 +20,8 @@
 
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
-#include <chromeos/http/http_utils.h>
-#include <chromeos/mime_utils.h>
+#include <brillo/http/http_utils.h>
+#include <brillo/mime_utils.h>
 
 HttpSender::HttpSender(const std::string server_url)
     : server_url_(server_url) {}
@@ -31,14 +31,14 @@
   const std::string hash =
       base::HexEncode(content_hash.data(), content_hash.size());
 
-  chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
-  chromeos::ErrorPtr error;
-  auto response = chromeos::http::PostTextAndBlock(
+  brillo::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
+  brillo::ErrorPtr error;
+  auto response = brillo::http::PostTextAndBlock(
       server_url_,
       content,
-      chromeos::mime::application::kWwwFormUrlEncoded,
+      brillo::mime::application::kWwwFormUrlEncoded,
       headers,
-      chromeos::http::Transport::CreateDefault(),
+      brillo::http::Transport::CreateDefault(),
       &error);
   if (!response || response->ExtractDataAsString() != "OK") {
     if (error) {
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 1d87be5..1995510 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -21,8 +21,9 @@
 #include <base/logging.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
-#include <cutils/properties.h>
+#include <brillo/osrelease_reader.h>
 #include <string>
+#include <update_engine/client.h>
 #include <vector>
 
 #include "constants.h"
@@ -73,16 +74,27 @@
   CHECK(!initialized_)
       << "this should be called only once in the metrics_daemon lifetime.";
 
-  profile_.product_id = GetProperty(metrics::kProductIdProperty);
+  brillo::OsReleaseReader reader;
+  std::string channel;
+  if (testing_) {
+    reader.LoadTestingOnly(metrics_directory_);
+    channel = "unknown";
+  } else {
+    reader.Load();
+    auto client = update_engine::UpdateEngineClient::CreateInstance();
+    if (!client->GetChannel(&channel)) {
+      LOG(ERROR) << "failed to read the current channel from update engine.";
+    }
+  }
 
-  if (profile_.product_id.empty()) {
-    LOG(ERROR) << "System property " << metrics::kProductIdProperty
-               << " is not set.";
+  if (!reader.GetString(metrics::kProductId, &profile_.product_id)) {
+    LOG(ERROR) << "product_id is not set.";
     return false;
   }
 
-  std::string channel = GetProperty(metrics::kChannelProperty);
-  profile_.version = GetProperty(metrics::kProductVersionProperty);
+  if (!reader.GetString(metrics::kProductVersion, &profile_.version)) {
+    LOG(ERROR) << "failed to read the product version";
+  }
 
   if (channel.empty() || profile_.version.empty()) {
     // If the channel or version is missing, the image is not official.
@@ -154,18 +166,6 @@
   return guid;
 }
 
-std::string SystemProfileCache::GetProperty(const std::string& name) {
-  if (testing_) {
-    std::string content;
-    base::ReadFileToString(metrics_directory_.Append(name), &content);
-    return content;
-  } else {
-    char value[PROPERTY_VALUE_MAX];
-    property_get(name.data(), value, "");
-    return std::string(value);
-  }
-}
-
 metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
     const std::string& channel) {
   if (channel == "stable") {
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 3410157..97fb33a 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -76,11 +76,6 @@
   // Initializes |profile_| only if it has not been yet initialized.
   bool InitializeOrCheck();
 
-  // Gets a system property as a string.
-  // When |testing_| is true, reads the value from |metrics_directory_|/|name|
-  // instead.
-  std::string GetProperty(const std::string& name);
-
   bool initialized_;
   bool testing_;
   base::FilePath metrics_directory_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 305fd0c..236376a 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -58,9 +58,11 @@
   }
 
   void SetTestingProperty(const std::string& name, const std::string& value) {
+    base::FilePath filepath = dir_.path().Append("etc/os-release.d").Append(name);
+    ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
     ASSERT_EQ(
         value.size(),
-        base::WriteFile(dir_.path().Append(name), value.data(), value.size()));
+        base::WriteFile(filepath, value.data(), value.size()));
   }
 
   base::ScopedTempDir dir_;
@@ -225,9 +227,8 @@
   SenderMock* sender = new SenderMock();
   upload_service_->sender_.reset(sender);
 
-  SetTestingProperty(metrics::kChannelProperty, "beta");
-  SetTestingProperty(metrics::kProductIdProperty, "hello");
-  SetTestingProperty(metrics::kProductVersionProperty, "1.2.3.4");
+  SetTestingProperty(metrics::kProductId, "hello");
+  SetTestingProperty(metrics::kProductVersion, "1.2.3.4");
 
   scoped_ptr<metrics::MetricSample> histogram =
       metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
@@ -242,8 +243,6 @@
   EXPECT_TRUE(sender->is_good_proto());
   EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
 
-  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA,
-            sender->last_message_proto().system_profile().channel());
   EXPECT_NE(0, sender->last_message_proto().client_id());
   EXPECT_NE(0, sender->last_message_proto().system_profile().build_timestamp());
   EXPECT_NE(0, sender->last_message_proto().session_id());
@@ -269,7 +268,7 @@
 }
 
 TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
-  SetTestingProperty(metrics::kProductIdProperty, "hello");
+  SetTestingProperty(metrics::kProductId, "hello");
   SystemProfileCache cache(true, dir_.path());
   cache.Initialize();
   int session_id = cache.profile_.session_id;