Merge "crash_reporter: Call dbus-send using chromeos::ProcessImpl"
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index f637073..84e3db6 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -31,10 +31,11 @@
 #include "base/test_utils.h"
 
 // All of these tests fail on Windows because they use the C Runtime open(),
-// but the adb_io APIs expect file descriptors from adb_open(). Also, the
-// android::base file APIs use the C Runtime which uses CR/LF translation by
-// default (changeable with _setmode()), but the adb_io APIs use adb_read()
-// and adb_write() which do no translation.
+// but the adb_io APIs expect file descriptors from adb_open(). This could
+// theoretically be fixed by making adb_read()/adb_write() fallback to using
+// read()/write() if an unrecognized fd is used, and by making adb_open() return
+// fds far from the range that open() returns. But all of that might defeat the
+// purpose of the tests.
 
 TEST(io, ReadFdExactly_whole) {
   const char expected[] = "Foobar";
diff --git a/base/file.cpp b/base/file.cpp
index 9a340b7..3468dcf 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,6 +28,10 @@
 #include "cutils/log.h"
 #include "utils/Compat.h"
 
+#if !defined(_WIN32)
+#define O_BINARY 0
+#endif
+
 namespace android {
 namespace base {
 
@@ -45,8 +49,7 @@
 bool ReadFileToString(const std::string& path, std::string* content) {
   content->clear();
 
-  int fd =
-      TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
   if (fd == -1) {
     return false;
   }
@@ -80,9 +83,8 @@
 #if !defined(_WIN32)
 bool WriteStringToFile(const std::string& content, const std::string& path,
                        mode_t mode, uid_t owner, gid_t group) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           mode));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
   if (fd == -1) {
     ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
     return false;
@@ -108,9 +110,8 @@
 #endif
 
 bool WriteStringToFile(const std::string& content, const std::string& path) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           DEFFILEMODE));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
   if (fd == -1) {
     return false;
   }
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index b0c5a12..22641e7 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -37,9 +37,9 @@
     return -1;
   }
   // Use open() to match the close() that TemporaryFile's destructor does.
-  // Note that on Windows, this does CR/LF translation and _setmode() should
-  // be used to change that if appropriate.
-  return open(template_name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+  // Use O_BINARY to match base file APIs.
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+              S_IRUSR | S_IWUSR);
 }
 
 char* mkdtemp(char* template_name) {
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 8b0f714..d77d41f 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <sys/mount.h>
 
+#include <cutils/properties.h>
+
 #include "fs_mgr_priv.h"
 
 struct fs_mgr_flag_values {
@@ -70,6 +72,7 @@
     { "zramsize=",   MF_ZRAMSIZE },
     { "verify",      MF_VERIFY },
     { "noemulatedsd", MF_NOEMULATEDSD },
+    { "slotselect",  MF_SLOTSELECT },
     { "defaults",    0 },
     { 0,             0 },
 };
@@ -307,6 +310,23 @@
         fstab->recs[cnt].partnum = flag_vals.partnum;
         fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
         fstab->recs[cnt].zram_size = flag_vals.zram_size;
+
+        /* If an A/B partition, modify block device to be the real block device */
+        if (fstab->recs[cnt].fs_mgr_flags & MF_SLOTSELECT) {
+            char propbuf[PROPERTY_VALUE_MAX];
+            char *tmp;
+
+            /* use the kernel parameter if set */
+            property_get("ro.boot.slot_suffix", propbuf, "");
+
+            if (asprintf(&tmp, "%s%s", fstab->recs[cnt].blk_device, propbuf) > 0) {
+                free(fstab->recs[cnt].blk_device);
+                fstab->recs[cnt].blk_device = tmp;
+            } else {
+                ERROR("Error updating block device name\n");
+                goto err;
+            }
+        }
         cnt++;
     }
     fclose(fstab_file);
@@ -448,3 +468,8 @@
 {
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
 }
+
+int fs_mgr_is_slotselect(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_SLOTSELECT;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index d56111a..cc02bac 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -77,6 +77,7 @@
 #define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
                                  external storage */
 #define MF_FILEENCRYPTION 0x2000
+#define MF_SLOTSELECT   0x8000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index 9c077d6..087a0c4 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -68,7 +68,8 @@
 #define ATRACE_TAG_BIONIC           (1<<16)
 #define ATRACE_TAG_POWER            (1<<17)
 #define ATRACE_TAG_PACKAGE_MANAGER  (1<<18)
-#define ATRACE_TAG_LAST             ATRACE_TAG_PACKAGE_MANAGER
+#define ATRACE_TAG_SYSTEM_SERVER    (1<<19)
+#define ATRACE_TAG_LAST             ATRACE_TAG_SYSTEM_SERVER
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/init/Android.mk b/init/Android.mk
index 58bff58..7670951 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,6 +45,7 @@
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     action.cpp \
+    import_parser.cpp \
     init_parser.cpp \
     log.cpp \
     parser.cpp \
diff --git a/init/action.cpp b/init/action.cpp
index dd366d3..c6cbc2e 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -21,46 +21,27 @@
 #include <base/strings.h>
 #include <base/stringprintf.h>
 
+#include "builtins.h"
 #include "error.h"
 #include "init_parser.h"
 #include "log.h"
 #include "property_service.h"
 #include "util.h"
 
-class Action::Command
-{
-public:
-    Command(int (*f)(const std::vector<std::string>& args),
-            const std::vector<std::string>& args,
-            const std::string& filename,
-            int line);
+using android::base::Join;
+using android::base::StringPrintf;
 
-    int InvokeFunc() const;
-    std::string BuildCommandString() const;
-    std::string BuildSourceString() const;
-
-private:
-    int (*func_)(const std::vector<std::string>& args);
-    const std::vector<std::string> args_;
-    const std::string filename_;
-    int line_;
-};
-
-Action::Command::Command(int (*f)(const std::vector<std::string>& args),
-                         const std::vector<std::string>& args,
-                         const std::string& filename,
-                         int line) :
-    func_(f), args_(args), filename_(filename), line_(line)
-{
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
+                 const std::string& filename, int line)
+    : func_(f), args_(args), filename_(filename), line_(line) {
 }
 
-int Action::Command::InvokeFunc() const
-{
+int Command::InvokeFunc() const {
     std::vector<std::string> expanded_args;
     expanded_args.resize(args_.size());
     expanded_args[0] = args_[0];
     for (std::size_t i = 1; i < args_.size(); ++i) {
-        if (expand_props(args_[i], &expanded_args[i]) == -1) {
+        if (!expand_props(args_[i], &expanded_args[i])) {
             ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
             return -EINVAL;
         }
@@ -69,51 +50,71 @@
     return func_(expanded_args);
 }
 
-std::string Action::Command::BuildCommandString() const
-{
-    return android::base::Join(args_, ' ');
+std::string Command::BuildCommandString() const {
+    return Join(args_, ' ');
 }
 
-std::string Action::Command::BuildSourceString() const
-{
+std::string Command::BuildSourceString() const {
     if (!filename_.empty()) {
-        return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
+        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
     } else {
         return std::string();
     }
 }
 
-Action::Action()
-{
+Action::Action(bool oneshot) : oneshot_(oneshot) {
 }
 
-void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
+const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+
+bool Action::AddCommand(const std::vector<std::string>& args,
+                        const std::string& filename, int line, std::string* err) {
+    if (!function_map_) {
+        *err = "no function map available";
+        return false;
+    }
+
+    if (args.empty()) {
+        *err = "command needed, but not provided";
+        return false;
+    }
+
+    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+    if (!function) {
+        return false;
+    }
+
+    AddCommand(function, args, filename, line);
+    return true;
+}
+
+void Action::AddCommand(BuiltinFunction f,
                         const std::vector<std::string>& args,
-                        const std::string& filename, int line)
-{
-    Action::Command* cmd = new Action::Command(f, args, filename, line);
-    commands_.push_back(cmd);
+                        const std::string& filename, int line) {
+    commands_.emplace_back(f, args, filename, line);
 }
 
-std::size_t Action::NumCommands() const
-{
-    return commands_.size();
-}
-
-void Action::ExecuteOneCommand(std::size_t command) const
-{
-    ExecuteCommand(*commands_[command]);
-}
-
-void Action::ExecuteAllCommands() const
-{
-    for (const auto& c : commands_) {
-        ExecuteCommand(*c);
+void Action::CombineAction(const Action& action) {
+    for (const auto& c : action.commands_) {
+        commands_.emplace_back(c);
     }
 }
 
-void Action::ExecuteCommand(const Command& command) const
-{
+std::size_t Action::NumCommands() const {
+    return commands_.size();
+}
+
+void Action::ExecuteOneCommand(std::size_t command) const {
+    ExecuteCommand(commands_[command]);
+}
+
+void Action::ExecuteAllCommands() const {
+    for (const auto& c : commands_) {
+        ExecuteCommand(c);
+    }
+}
+
+void Action::ExecuteCommand(const Command& command) const {
     Timer t;
     int result = command.InvokeFunc();
 
@@ -128,8 +129,7 @@
     }
 }
 
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
-{
+bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
     const static std::string prop_str("property:");
     std::string prop_name(trigger.substr(prop_str.length()));
     size_t equal_pos = prop_name.find('=');
@@ -149,8 +149,7 @@
     return true;
 }
 
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
-{
+bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
     const static std::string prop_str("property:");
     for (std::size_t i = 0; i < args.size(); ++i) {
         if (i % 2) {
@@ -179,21 +178,26 @@
     return true;
 }
 
-bool Action::InitSingleTrigger(const std::string& trigger)
-{
+bool Action::InitSingleTrigger(const std::string& trigger) {
     std::vector<std::string> name_vector{trigger};
     std::string err;
     return InitTriggers(name_vector, &err);
 }
 
+// This function checks that all property triggers are satisfied, that is
+// for each (name, value) in property_triggers_, check that the current
+// value of the property 'name' == value.
+//
+// It takes an optional (name, value) pair, which if provided must
+// be present in property_triggers_; it skips the check of the current
+// property value for this pair.
 bool Action::CheckPropertyTriggers(const std::string& name,
-                                   const std::string& value) const
-{
-    bool found = name.empty();
+                                   const std::string& value) const {
     if (property_triggers_.empty()) {
         return true;
     }
 
+    bool found = name.empty();
     for (const auto& t : property_triggers_) {
         const auto& trigger_name = t.first;
         const auto& trigger_value = t.second;
@@ -214,27 +218,23 @@
     return found;
 }
 
-bool Action::CheckEventTrigger(const std::string& trigger) const
-{
+bool Action::CheckEventTrigger(const std::string& trigger) const {
     return !event_trigger_.empty() &&
         trigger == event_trigger_ &&
         CheckPropertyTriggers();
 }
 
 bool Action::CheckPropertyTrigger(const std::string& name,
-                                  const std::string& value) const
-{
+                                  const std::string& value) const {
     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
 }
 
-bool Action::TriggersEqual(const class Action& other) const
-{
+bool Action::TriggersEqual(const Action& other) const {
     return property_triggers_ == other.property_triggers_ &&
         event_trigger_ == other.event_trigger_;
 }
 
-std::string Action::BuildTriggersString() const
-{
+std::string Action::BuildTriggersString() const {
     std::string result;
 
     for (const auto& t : property_triggers_) {
@@ -251,28 +251,26 @@
     return result;
 }
 
-void Action::DumpState() const
-{
+void Action::DumpState() const {
     std::string trigger_name = BuildTriggersString();
     INFO("on %s\n", trigger_name.c_str());
 
     for (const auto& c : commands_) {
-        std::string cmd_str = c->BuildCommandString();
+        std::string cmd_str = c.BuildCommandString();
         INFO(" %s\n", cmd_str.c_str());
     }
     INFO("\n");
 }
 
-
 class EventTrigger : public Trigger {
 public:
     EventTrigger(const std::string& trigger) : trigger_(trigger) {
     }
-    bool CheckTriggers(const Action* action) override {
-        return action->CheckEventTrigger(trigger_);
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckEventTrigger(trigger_);
     }
 private:
-    std::string trigger_;
+    const std::string trigger_;
 };
 
 class PropertyTrigger : public Trigger {
@@ -280,27 +278,26 @@
     PropertyTrigger(const std::string& name, const std::string& value)
         : name_(name), value_(value) {
     }
-    bool CheckTriggers(const Action* action) override {
-        return action->CheckPropertyTrigger(name_, value_);
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckPropertyTrigger(name_, value_);
     }
 private:
-    std::string name_;
-    std::string value_;
+    const std::string name_;
+    const std::string value_;
 };
 
 class BuiltinTrigger : public Trigger {
 public:
     BuiltinTrigger(Action* action) : action_(action) {
     }
-    bool CheckTriggers(const Action* action) override {
-        return action == action_;
+    bool CheckTriggers(const Action& action) const override {
+        return action_ == &action;
     }
 private:
-    Action* action_;
+    const Action* action_;
 };
 
-ActionManager::ActionManager() : current_command_(0)
-{
+ActionManager::ActionManager() : current_command_(0) {
 }
 
 ActionManager& ActionManager::GetInstance() {
@@ -308,45 +305,56 @@
     return instance;
 }
 
-void ActionManager::QueueEventTrigger(const std::string& trigger)
-{
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    auto old_action_it =
+        std::find_if(actions_.begin(), actions_.end(),
+                     [&action] (std::unique_ptr<Action>& a) {
+                         return action->TriggersEqual(*a);
+                     });
+
+    if (old_action_it != actions_.end()) {
+        (*old_action_it)->CombineAction(*action);
+    } else {
+        actions_.emplace_back(std::move(action));
+    }
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
     trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
 }
 
 void ActionManager::QueuePropertyTrigger(const std::string& name,
-                                         const std::string& value)
-{
+                                         const std::string& value) {
     trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
 }
 
-void ActionManager::QueueAllPropertyTriggers()
-{
+void ActionManager::QueueAllPropertyTriggers() {
     QueuePropertyTrigger("", "");
 }
 
-void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
-                                       const std::string& name)
-{
-    Action* act = new Action();
+void ActionManager::QueueBuiltinAction(BuiltinFunction func,
+                                       const std::string& name) {
+    auto action = std::make_unique<Action>(true);
     std::vector<std::string> name_vector{name};
 
-    if (!act->InitSingleTrigger(name)) {
+    if (!action->InitSingleTrigger(name)) {
         return;
     }
 
-    act->AddCommand(func, name_vector);
+    action->AddCommand(func, name_vector);
 
-    actions_.push_back(act);
-    trigger_queue_.push(std::make_unique<BuiltinTrigger>(act));
+    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+    actions_.emplace_back(std::move(action));
 }
 
 void ActionManager::ExecuteOneCommand() {
+    // Loop through the trigger queue until we have an action to execute
     while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
-        std::copy_if(actions_.begin(), actions_.end(),
-                     std::back_inserter(current_executing_actions_),
-                     [this] (Action* act) {
-                         return trigger_queue_.front()->CheckTriggers(act);
-                     });
+        for (const auto& action : actions_) {
+            if (trigger_queue_.front()->CheckTriggers(*action)) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
         trigger_queue_.pop();
     }
 
@@ -354,59 +362,67 @@
         return;
     }
 
-    Action* action = current_executing_actions_.back();
-    if (!action->NumCommands()) {
-        current_executing_actions_.pop_back();
-        return;
-    }
+    auto action = current_executing_actions_.front();
 
     if (current_command_ == 0) {
         std::string trigger_name = action->BuildTriggersString();
-        INFO("processing action %p (%s)\n", action, trigger_name.c_str());
+        INFO("processing action (%s)\n", trigger_name.c_str());
     }
 
-    action->ExecuteOneCommand(current_command_++);
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
     if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
         current_command_ = 0;
-        current_executing_actions_.pop_back();
+        if (action->oneshot()) {
+            auto eraser = [&action] (std::unique_ptr<Action>& a) {
+                return a.get() == action;
+            };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
     }
 }
 
-bool ActionManager::HasMoreCommands() const
-{
+bool ActionManager::HasMoreCommands() const {
     return !current_executing_actions_.empty() || !trigger_queue_.empty();
 }
 
-Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
-                                    std::string* err)
-{
-    if (triggers.size() < 1) {
-        *err = "actions must have a trigger\n";
-        return nullptr;
-    }
-
-    Action* act = new Action();
-    if (!act->InitTriggers(triggers, err)) {
-        return nullptr;
-    }
-
-    auto old_act_it =
-        std::find_if(actions_.begin(), actions_.end(),
-                     [&act] (Action* a) { return act->TriggersEqual(*a); });
-
-    if (old_act_it != actions_.end()) {
-        delete act;
-        return *old_act_it;
-    }
-
-    actions_.push_back(act);
-    return act;
-}
-
-void ActionManager::DumpState() const
-{
+void ActionManager::DumpState() const {
     for (const auto& a : actions_) {
         a->DumpState();
     }
     INFO("\n");
 }
+
+bool ActionParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        *err = "actions must have a trigger";
+        return false;
+    }
+
+    auto action = std::make_unique<Action>(false);
+    if (!action->InitTriggers(triggers, err)) {
+        return false;
+    }
+
+    action_ = std::move(action);
+    return true;
+}
+
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
+                                    const std::string& filename, int line,
+                                    std::string* err) const {
+    return action_ ? action_->AddCommand(args, filename, line, err) : false;
+}
+
+void ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        ActionManager::GetInstance().AddAction(std::move(action_));
+    }
+}
diff --git a/init/action.h b/init/action.h
index 5088c71..6dee2d0 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,13 +22,36 @@
 #include <string>
 #include <vector>
 
+#include "builtins.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+class Command {
+public:
+    Command(BuiltinFunction f, const std::vector<std::string>& args,
+            const std::string& filename, int line);
+
+    int InvokeFunc() const;
+    std::string BuildCommandString() const;
+    std::string BuildSourceString() const;
+
+private:
+    BuiltinFunction func_;
+    std::vector<std::string> args_;
+    std::string filename_;
+    int line_;
+};
+
 class Action {
 public:
-    Action();
+    Action(bool oneshot = false);
 
-    void AddCommand(int (*f)(const std::vector<std::string>& args),
+    bool AddCommand(const std::vector<std::string>& args,
+                    const std::string& filename, int line, std::string* err);
+    void AddCommand(BuiltinFunction f,
                     const std::vector<std::string>& args,
                     const std::string& filename = "", int line = 0);
+    void CombineAction(const Action& action);
     bool InitTriggers(const std::vector<std::string>& args, std::string* err);
     bool InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
@@ -37,13 +60,17 @@
     bool CheckEventTrigger(const std::string& trigger) const;
     bool CheckPropertyTrigger(const std::string& name,
                               const std::string& value) const;
-    bool TriggersEqual(const class Action& other) const;
+    bool TriggersEqual(const Action& other) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
 
-private:
-    class Command;
+    bool oneshot() const { return oneshot_; }
+    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+        function_map_ = function_map;
+    }
 
+
+private:
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
@@ -51,27 +78,28 @@
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
-    std::vector<Command*> commands_;
+    std::vector<Command> commands_;
+    bool oneshot_;
+    static const KeywordMap<BuiltinFunction>* function_map_;
 };
 
 class Trigger {
 public:
     virtual ~Trigger() { }
-    virtual bool CheckTriggers(const Action* action) = 0;
+    virtual bool CheckTriggers(const Action& action) const = 0;
 };
 
 class ActionManager {
 public:
     static ActionManager& GetInstance();
+
+    void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
     void QueuePropertyTrigger(const std::string& name, const std::string& value);
     void QueueAllPropertyTriggers();
-    void QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
-                            const std::string& name);
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
     void ExecuteOneCommand();
     bool HasMoreCommands() const;
-    Action* AddNewAction(const std::vector<std::string>& triggers,
-                         std::string* err);
     void DumpState() const;
 
 private:
@@ -80,10 +108,26 @@
     ActionManager(ActionManager const&) = delete;
     void operator=(ActionManager const&) = delete;
 
-    std::vector<Action*> actions_;
+    std::vector<std::unique_ptr<Action>> actions_;
     std::queue<std::unique_ptr<Trigger>> trigger_queue_;
-    std::vector<Action*> current_executing_actions_;
+    std::queue<const Action*> current_executing_actions_;
     std::size_t current_command_;
 };
 
+class ActionParser : public SectionParser {
+public:
+    ActionParser() : action_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    std::unique_ptr<Action> action_;
+};
+
 #endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index efaee1c..a768762 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "bootchart.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 
@@ -32,6 +31,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/file.h>
 
diff --git a/init/bootchart.h b/init/bootchart.h
index cf61d83..47eda7a 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -17,6 +17,10 @@
 #ifndef _BOOTCHART_H
 #define _BOOTCHART_H
 
+#include <string>
+#include <vector>
+
+int do_bootchart_init(const std::vector<std::string>& args);
 void bootchart_sample(int* timeout);
 
 #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 97151c0..3ffa2e8 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "builtins.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <mntent.h>
@@ -44,10 +46,10 @@
 #include <private/android_filesystem_config.h>
 
 #include "action.h"
+#include "bootchart.h"
 #include "devices.h"
 #include "init.h"
 #include "init_parser.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 #include "service.h"
@@ -60,8 +62,7 @@
 // System call provided by bionic but not in any header file.
 extern "C" int init_module(void *, unsigned long, const char *);
 
-static int insmod(const char *filename, const char *options)
-{
+static int insmod(const char *filename, const char *options) {
     std::string module;
     if (!read_file(filename, &module)) {
         return -1;
@@ -71,8 +72,7 @@
     return init_module(&module[0], module.size(), options);
 }
 
-static int __ifupdown(const char *interface, int up)
-{
+static int __ifupdown(const char *interface, int up) {
     struct ifreq ifr;
     int s, ret;
 
@@ -99,8 +99,7 @@
     return ret;
 }
 
-static void unmount_and_fsck(const struct mntent *entry)
-{
+static void unmount_and_fsck(const struct mntent *entry) {
     if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
         return;
 
@@ -160,8 +159,7 @@
     }
 }
 
-int do_class_start(const std::vector<std::string>& args)
-{
+static int do_class_start(const std::vector<std::string>& args) {
         /* Starting a class does not start services
          * which are explicitly disabled.  They must
          * be started individually.
@@ -171,27 +169,23 @@
     return 0;
 }
 
-int do_class_stop(const std::vector<std::string>& args)
-{
+static int do_class_stop(const std::vector<std::string>& args) {
     ServiceManager::GetInstance().
         ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
     return 0;
 }
 
-int do_class_reset(const std::vector<std::string>& args)
-{
+static int do_class_reset(const std::vector<std::string>& args) {
     ServiceManager::GetInstance().
         ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
     return 0;
 }
 
-int do_domainname(const std::vector<std::string>& args)
-{
+static int do_domainname(const std::vector<std::string>& args) {
     return write_file("/proc/sys/kernel/domainname", args[1].c_str());
 }
 
-int do_enable(const std::vector<std::string>& args)
-{
+static int do_enable(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         return -1;
@@ -199,7 +193,7 @@
     return svc->Enable();
 }
 
-int do_exec(const std::vector<std::string>& args) {
+static int do_exec(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
     if (!svc) {
         return -1;
@@ -211,23 +205,19 @@
     return 0;
 }
 
-int do_export(const std::vector<std::string>& args)
-{
+static int do_export(const std::vector<std::string>& args) {
     return add_environment(args[1].c_str(), args[2].c_str());
 }
 
-int do_hostname(const std::vector<std::string>& args)
-{
+static int do_hostname(const std::vector<std::string>& args) {
     return write_file("/proc/sys/kernel/hostname", args[1].c_str());
 }
 
-int do_ifup(const std::vector<std::string>& args)
-{
+static int do_ifup(const std::vector<std::string>& args) {
     return __ifupdown(args[1].c_str(), 1);
 }
 
-int do_insmod(const std::vector<std::string>& args)
-{
+static int do_insmod(const std::vector<std::string>& args) {
     std::string options;
 
     if (args.size() > 2) {
@@ -241,8 +231,7 @@
     return insmod(args[1].c_str(), options.c_str());
 }
 
-int do_mkdir(const std::vector<std::string>& args)
-{
+static int do_mkdir(const std::vector<std::string>& args) {
     mode_t mode = 0755;
     int ret;
 
@@ -310,8 +299,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(const std::vector<std::string>& args)
-{
+static int do_mount(const std::vector<std::string>& args) {
     char tmp[64];
     const char *source, *target, *system;
     const char *options = NULL;
@@ -411,8 +399,7 @@
 
 }
 
-static int wipe_data_via_recovery()
-{
+static int wipe_data_via_recovery() {
     mkdir("/cache/recovery", 0700);
     int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
     if (fd >= 0) {
@@ -427,16 +414,16 @@
     while (1) { pause(); }  // never reached
 }
 
-void import_late()
-{
+static void import_late() {
     static const std::vector<std::string> init_directories = {
         "/system/etc/init",
         "/vendor/etc/init",
         "/odm/etc/init"
     };
 
+    Parser& parser = Parser::GetInstance();
     for (const auto& dir : init_directories) {
-        init_parse_config(dir.c_str());
+        parser.ParseConfig(dir.c_str());
     }
 }
 
@@ -444,17 +431,13 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-int do_mount_all(const std::vector<std::string>& args)
-{
+static int do_mount_all(const std::vector<std::string>& args) {
     pid_t pid;
     int ret = -1;
     int child_ret = -1;
     int status;
     struct fstab *fstab;
 
-    if (args.size() != 2) {
-        return -1;
-    }
     const char* fstabfile = args[1].c_str();
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
@@ -535,8 +518,7 @@
     return ret;
 }
 
-int do_swapon_all(const std::vector<std::string>& args)
-{
+static int do_swapon_all(const std::vector<std::string>& args) {
     struct fstab *fstab;
     int ret;
 
@@ -547,16 +529,14 @@
     return ret;
 }
 
-int do_setprop(const std::vector<std::string>& args)
-{
+static int do_setprop(const std::vector<std::string>& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     property_set(name, value);
     return 0;
 }
 
-int do_setrlimit(const std::vector<std::string>& args)
-{
+static int do_setrlimit(const std::vector<std::string>& args) {
     struct rlimit limit;
     int resource;
     resource = std::stoi(args[1]);
@@ -565,8 +545,7 @@
     return setrlimit(resource, &limit);
 }
 
-int do_start(const std::vector<std::string>& args)
-{
+static int do_start(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         ERROR("do_start: Service %s not found\n", args[1].c_str());
@@ -577,8 +556,7 @@
     return 0;
 }
 
-int do_stop(const std::vector<std::string>& args)
-{
+static int do_stop(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         ERROR("do_stop: Service %s not found\n", args[1].c_str());
@@ -588,8 +566,7 @@
     return 0;
 }
 
-int do_restart(const std::vector<std::string>& args)
-{
+static int do_restart(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         ERROR("do_restart: Service %s not found\n", args[1].c_str());
@@ -599,8 +576,7 @@
     return 0;
 }
 
-int do_powerctl(const std::vector<std::string>& args)
-{
+static int do_powerctl(const std::vector<std::string>& args) {
     const char* command = args[1].c_str();
     int len = 0;
     unsigned int cmd = 0;
@@ -636,34 +612,26 @@
                                         callback_on_ro_remount);
 }
 
-int do_trigger(const std::vector<std::string>& args)
-{
+static int do_trigger(const std::vector<std::string>& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
 }
 
-int do_symlink(const std::vector<std::string>& args)
-{
+static int do_symlink(const std::vector<std::string>& args) {
     return symlink(args[1].c_str(), args[2].c_str());
 }
 
-int do_rm(const std::vector<std::string>& args)
-{
+static int do_rm(const std::vector<std::string>& args) {
     return unlink(args[1].c_str());
 }
 
-int do_rmdir(const std::vector<std::string>& args)
-{
+static int do_rmdir(const std::vector<std::string>& args) {
     return rmdir(args[1].c_str());
 }
 
-int do_sysclktz(const std::vector<std::string>& args)
-{
+static int do_sysclktz(const std::vector<std::string>& args) {
     struct timezone tz;
 
-    if (args.size() != 2)
-        return -1;
-
     memset(&tz, 0, sizeof(tz));
     tz.tz_minuteswest = std::stoi(args[1]);
     if (settimeofday(NULL, &tz))
@@ -671,7 +639,7 @@
     return 0;
 }
 
-int do_verity_load_state(const std::vector<std::string>& args) {
+static int do_verity_load_state(const std::vector<std::string>& args) {
     int mode = -1;
     int rc = fs_mgr_load_verity_state(&mode);
     if (rc == 0 && mode == VERITY_MODE_LOGGING) {
@@ -680,24 +648,23 @@
     return rc;
 }
 
-static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+static void verity_update_property(fstab_rec *fstab, const char *mount_point,
+                                   int mode, int status) {
     property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
                  android::base::StringPrintf("%d", mode).c_str());
 }
 
-int do_verity_update_state(const std::vector<std::string>& args) {
+static int do_verity_update_state(const std::vector<std::string>& args) {
     return fs_mgr_update_verity_state(verity_update_property);
 }
 
-int do_write(const std::vector<std::string>& args)
-{
+static int do_write(const std::vector<std::string>& args) {
     const char* path = args[1].c_str();
     const char* value = args[2].c_str();
     return write_file(path, value);
 }
 
-int do_copy(const std::vector<std::string>& args)
-{
+static int do_copy(const std::vector<std::string>& args) {
     char *buffer = NULL;
     int rc = 0;
     int fd1 = -1, fd2 = -1;
@@ -705,9 +672,6 @@
     int brtw, brtr;
     char *p;
 
-    if (args.size() != 3)
-        return -1;
-
     if (stat(args[1].c_str(), &info) < 0)
         return -1;
 
@@ -758,7 +722,7 @@
     return rc;
 }
 
-int do_chown(const std::vector<std::string>& args) {
+static int do_chown(const std::vector<std::string>& args) {
     /* GID is optional. */
     if (args.size() == 3) {
         if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
@@ -786,7 +750,7 @@
     return mode;
 }
 
-int do_chmod(const std::vector<std::string>& args) {
+static int do_chmod(const std::vector<std::string>& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return -errno;
@@ -794,7 +758,7 @@
     return 0;
 }
 
-int do_restorecon(const std::vector<std::string>& args) {
+static int do_restorecon(const std::vector<std::string>& args) {
     int ret = 0;
 
     for (auto it = std::next(args.begin()); it != args.end(); ++it) {
@@ -804,7 +768,7 @@
     return ret;
 }
 
-int do_restorecon_recursive(const std::vector<std::string>& args) {
+static int do_restorecon_recursive(const std::vector<std::string>& args) {
     int ret = 0;
 
     for (auto it = std::next(args.begin()); it != args.end(); ++it) {
@@ -814,12 +778,7 @@
     return ret;
 }
 
-int do_loglevel(const std::vector<std::string>& args) {
-    if (args.size() != 2) {
-        ERROR("loglevel: missing argument\n");
-        return -EINVAL;
-    }
-
+static int do_loglevel(const std::vector<std::string>& args) {
     int log_level = std::stoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
@@ -830,23 +789,16 @@
 }
 
 int do_load_persist_props(const std::vector<std::string>& args) {
-    if (args.size() == 1) {
-        load_persist_props();
-        return 0;
-    }
-    return -1;
+    load_persist_props();
+    return 0;
 }
 
-int do_load_all_props(const std::vector<std::string>& args) {
-    if (args.size() == 1) {
-        load_all_props();
-        return 0;
-    }
-    return -1;
+static int do_load_all_props(const std::vector<std::string>& args) {
+    load_all_props();
+    return 0;
 }
 
-int do_wait(const std::vector<std::string>& args)
-{
+static int do_wait(const std::vector<std::string>& args) {
     if (args.size() == 2) {
         return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
     } else if (args.size() == 3) {
@@ -858,8 +810,7 @@
 /*
  * Callback to make a directory from the ext4 code
  */
-static int do_installkeys_ensure_dir_exists(const char* dir)
-{
+static int do_installkeys_ensure_dir_exists(const char* dir) {
     if (make_dir(dir, 0700) && errno != EEXIST) {
         return -1;
     }
@@ -867,12 +818,7 @@
     return 0;
 }
 
-int do_installkey(const std::vector<std::string>& args)
-{
-    if (args.size() != 2) {
-        return -1;
-    }
-
+static int do_installkey(const std::vector<std::string>& args) {
     std::string prop_value = property_get("ro.crypto.type");
     if (prop_value != "file") {
         return 0;
@@ -881,3 +827,49 @@
     return e4crypt_create_device_key(args[1].c_str(),
                                      do_installkeys_ensure_dir_exists);
 }
+
+BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map builtin_functions = {
+        {"bootchart_init",          {0,     0,    do_bootchart_init}},
+        {"chmod",                   {2,     2,    do_chmod}},
+        {"chown",                   {2,     3,    do_chown}},
+        {"class_reset",             {1,     1,    do_class_reset}},
+        {"class_start",             {1,     1,    do_class_start}},
+        {"class_stop",              {1,     1,    do_class_stop}},
+        {"copy",                    {2,     2,    do_copy}},
+        {"domainname",              {1,     1,    do_domainname}},
+        {"enable",                  {1,     1,    do_enable}},
+        {"exec",                    {1,     kMax, do_exec}},
+        {"export",                  {2,     2,    do_export}},
+        {"hostname",                {1,     1,    do_hostname}},
+        {"ifup",                    {1,     1,    do_ifup}},
+        {"insmod",                  {1,     kMax, do_insmod}},
+        {"installkey",              {1,     1,    do_installkey}},
+        {"load_all_props",          {0,     0,    do_load_all_props}},
+        {"load_persist_props",      {0,     0,    do_load_persist_props}},
+        {"loglevel",                {1,     1,    do_loglevel}},
+        {"mkdir",                   {1,     4,    do_mkdir}},
+        {"mount_all",               {1,     1,    do_mount_all}},
+        {"mount",                   {3,     kMax, do_mount}},
+        {"powerctl",                {1,     1,    do_powerctl}},
+        {"restart",                 {1,     1,    do_restart}},
+        {"restorecon",              {1,     kMax, do_restorecon}},
+        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
+        {"rm",                      {1,     1,    do_rm}},
+        {"rmdir",                   {1,     1,    do_rmdir}},
+        {"setprop",                 {2,     2,    do_setprop}},
+        {"setrlimit",               {3,     3,    do_setrlimit}},
+        {"start",                   {1,     1,    do_start}},
+        {"stop",                    {1,     1,    do_stop}},
+        {"swapon_all",              {1,     1,    do_swapon_all}},
+        {"symlink",                 {2,     2,    do_symlink}},
+        {"sysclktz",                {1,     1,    do_sysclktz}},
+        {"trigger",                 {1,     1,    do_trigger}},
+        {"verity_load_state",       {0,     0,    do_verity_load_state}},
+        {"verity_update_state",     {0,     0,    do_verity_update_state}},
+        {"wait",                    {1,     2,    do_wait}},
+        {"write",                   {2,     2,    do_write}},
+    };
+    return builtin_functions;
+}
diff --git a/init/builtins.h b/init/builtins.h
new file mode 100644
index 0000000..53f4a71
--- /dev/null
+++ b/init/builtins.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _INIT_BUILTINS_H
+#define _INIT_BUILTINS_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "keyword_map.h"
+
+using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+public:
+    BuiltinFunctionMap() {
+    }
+private:
+    Map& map() const override;
+};
+
+#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
new file mode 100644
index 0000000..e2a0f83
--- /dev/null
+++ b/init/import_parser.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "import_parser.h"
+
+#include "errno.h"
+
+#include <string>
+#include <vector>
+
+#include "log.h"
+#include "util.h"
+
+bool ImportParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    if (args.size() != 2) {
+        *err = "single argument needed for import\n";
+        return false;
+    }
+
+    std::string conf_file;
+    bool ret = expand_props(args[1], &conf_file);
+    if (!ret) {
+        *err = "error while expanding import";
+        return false;
+    }
+
+    INFO("Added '%s' to import list\n", conf_file.c_str());
+    imports_.emplace_back(std::move(conf_file));
+    return true;
+}
+
+void ImportParser::EndFile(const std::string& filename) {
+    auto current_imports = std::move(imports_);
+    imports_.clear();
+    for (const auto& s : current_imports) {
+        if (!Parser::GetInstance().ParseConfig(s)) {
+            ERROR("could not import file '%s' from '%s': %s\n",
+                  s.c_str(), filename.c_str(), strerror(errno));
+        }
+    }
+}
diff --git a/init/import_parser.h b/init/import_parser.h
new file mode 100644
index 0000000..0e91025
--- /dev/null
+++ b/init/import_parser.h
@@ -0,0 +1,43 @@
+/*
+ * 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 _INIT_IMPORT_PARSER_H
+#define _INIT_IMPORT_PARSER_H
+
+#include "init_parser.h"
+
+#include <string>
+#include <vector>
+
+class ImportParser : public SectionParser {
+public:
+    ImportParser()  {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override {
+        return true;
+    }
+    void EndSection() override {
+    }
+    void EndFile(const std::string& filename) override;
+private:
+    std::vector<std::string> imports_;
+};
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index c94a6fe..ee1351d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -55,6 +55,7 @@
 #include "action.h"
 #include "bootchart.h"
 #include "devices.h"
+#include "import_parser.h"
 #include "init.h"
 #include "init_parser.h"
 #include "keychords.h"
@@ -604,7 +605,14 @@
     property_load_boot_defaults();
     start_property_service();
 
-    init_parse_config("/init.rc");
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+
+    Parser& parser = Parser::GetInstance();
+    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
+    parser.AddSectionParser("on", std::make_unique<ActionParser>());
+    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+    parser.ParseConfig("/init.rc");
 
     ActionManager& am = ActionManager::GetInstance();
 
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 12f44f7..02b3985 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,387 +14,117 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 
 #include "action.h"
-#include "init.h"
 #include "init_parser.h"
 #include "log.h"
 #include "parser.h"
-#include "property_service.h"
 #include "service.h"
 #include "util.h"
 
 #include <base/stringprintf.h>
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
 
-static list_declare(service_list);
-
-struct import {
-    struct listnode list;
-    const char *filename;
-};
-
-static void *parse_service(struct parse_state *state, int nargs, char **args);
-static void parse_line_service(struct parse_state *state, int nargs, char **args);
-
-static void *parse_action(struct parse_state *state, int nargs, char **args);
-static void parse_line_action(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define COMMAND 0x02
-#define OPTION  0x04
-
-#include "keywords.h"
-
-#define KEYWORD(symbol, flags, nargs, func) \
-    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
-
-static struct {
-    const char *name;
-    int (*func)(const std::vector<std::string>& args);
-    size_t nargs;
-    unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
-    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
-#include "keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_name(kw) (keyword_info[kw].name)
-#define kw_func(kw) (keyword_info[kw].func)
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-void dump_parser_state() {
-    ServiceManager::GetInstance().DumpState();
-    ActionManager::GetInstance().DumpState();
+Parser::Parser() {
 }
 
-static int lookup_keyword(const char *s)
-{
-    switch (*s++) {
-    case 'b':
-        if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
-        break;
-    case 'c':
-        if (!strcmp(s, "opy")) return K_copy;
-        if (!strcmp(s, "lass")) return K_class;
-        if (!strcmp(s, "lass_start")) return K_class_start;
-        if (!strcmp(s, "lass_stop")) return K_class_stop;
-        if (!strcmp(s, "lass_reset")) return K_class_reset;
-        if (!strcmp(s, "onsole")) return K_console;
-        if (!strcmp(s, "hown")) return K_chown;
-        if (!strcmp(s, "hmod")) return K_chmod;
-        if (!strcmp(s, "ritical")) return K_critical;
-        break;
-    case 'd':
-        if (!strcmp(s, "isabled")) return K_disabled;
-        if (!strcmp(s, "omainname")) return K_domainname;
-        break;
-    case 'e':
-        if (!strcmp(s, "nable")) return K_enable;
-        if (!strcmp(s, "xec")) return K_exec;
-        if (!strcmp(s, "xport")) return K_export;
-        break;
-    case 'g':
-        if (!strcmp(s, "roup")) return K_group;
-        break;
-    case 'h':
-        if (!strcmp(s, "ostname")) return K_hostname;
-        break;
-    case 'i':
-        if (!strcmp(s, "oprio")) return K_ioprio;
-        if (!strcmp(s, "fup")) return K_ifup;
-        if (!strcmp(s, "nsmod")) return K_insmod;
-        if (!strcmp(s, "mport")) return K_import;
-        if (!strcmp(s, "nstallkey")) return K_installkey;
-        break;
-    case 'k':
-        if (!strcmp(s, "eycodes")) return K_keycodes;
-        break;
-    case 'l':
-        if (!strcmp(s, "oglevel")) return K_loglevel;
-        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
-        if (!strcmp(s, "oad_all_props")) return K_load_all_props;
-        break;
-    case 'm':
-        if (!strcmp(s, "kdir")) return K_mkdir;
-        if (!strcmp(s, "ount_all")) return K_mount_all;
-        if (!strcmp(s, "ount")) return K_mount;
-        break;
-    case 'o':
-        if (!strcmp(s, "n")) return K_on;
-        if (!strcmp(s, "neshot")) return K_oneshot;
-        if (!strcmp(s, "nrestart")) return K_onrestart;
-        break;
-    case 'p':
-        if (!strcmp(s, "owerctl")) return K_powerctl;
-        break;
-    case 'r':
-        if (!strcmp(s, "estart")) return K_restart;
-        if (!strcmp(s, "estorecon")) return K_restorecon;
-        if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
-        if (!strcmp(s, "mdir")) return K_rmdir;
-        if (!strcmp(s, "m")) return K_rm;
-        break;
-    case 's':
-        if (!strcmp(s, "eclabel")) return K_seclabel;
-        if (!strcmp(s, "ervice")) return K_service;
-        if (!strcmp(s, "etenv")) return K_setenv;
-        if (!strcmp(s, "etprop")) return K_setprop;
-        if (!strcmp(s, "etrlimit")) return K_setrlimit;
-        if (!strcmp(s, "ocket")) return K_socket;
-        if (!strcmp(s, "tart")) return K_start;
-        if (!strcmp(s, "top")) return K_stop;
-        if (!strcmp(s, "wapon_all")) return K_swapon_all;
-        if (!strcmp(s, "ymlink")) return K_symlink;
-        if (!strcmp(s, "ysclktz")) return K_sysclktz;
-        break;
-    case 't':
-        if (!strcmp(s, "rigger")) return K_trigger;
-        break;
-    case 'u':
-        if (!strcmp(s, "ser")) return K_user;
-        break;
-    case 'v':
-        if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
-        if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
-        break;
-    case 'w':
-        if (!strcmp(s, "rite")) return K_write;
-        if (!strcmp(s, "ritepid")) return K_writepid;
-        if (!strcmp(s, "ait")) return K_wait;
-        break;
-    }
-    return K_UNKNOWN;
+Parser& Parser::GetInstance() {
+    static Parser instance;
+    return instance;
 }
 
-static void parse_line_no_op(struct parse_state*, int, char**) {
+void Parser::AddSectionParser(const std::string& name,
+                              std::unique_ptr<SectionParser> parser) {
+    section_parsers_[name] = std::move(parser);
 }
 
-int expand_props(const std::string& src, std::string* dst) {
-    const char *src_ptr = src.c_str();
-
-    if (!dst) {
-        return -1;
-    }
-
-    /* - variables can either be $x.y or ${x.y}, in case they are only part
-     *   of the string.
-     * - will accept $$ as a literal $.
-     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
-     *   bad things will happen
-     */
-    while (*src_ptr) {
-        const char *c;
-
-        c = strchr(src_ptr, '$');
-        if (!c) {
-            dst->append(src_ptr);
-            break;
-        }
-
-        dst->append(src_ptr, c);
-        c++;
-
-        if (*c == '$') {
-            dst->push_back(*(c++));
-            src_ptr = c;
-            continue;
-        } else if (*c == '\0') {
-            break;
-        }
-
-        std::string prop_name;
-        if (*c == '{') {
-            c++;
-            const char* end = strchr(c, '}');
-            if (!end) {
-                // failed to find closing brace, abort.
-                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
-                goto err;
-            }
-            prop_name = std::string(c, end);
-            c = end + 1;
-        } else {
-            prop_name = c;
-            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
-                  c);
-            c += prop_name.size();
-        }
-
-        if (prop_name.empty()) {
-            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
-            goto err;
-        }
-
-        std::string prop_val = property_get(prop_name.c_str());
-        if (prop_val.empty()) {
-            ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop_name.c_str(), src.c_str());
-            goto err;
-        }
-
-        dst->append(prop_val);
-        src_ptr = c;
-        continue;
-    }
-
-    return 0;
-err:
-    return -1;
-}
-
-static void parse_import(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs != 2) {
-        ERROR("single argument needed for import\n");
-        return;
-    }
-
-    std::string conf_file;
-    int ret = expand_props(args[1], &conf_file);
-    if (ret) {
-        ERROR("error while handling import on line '%d' in '%s'\n",
-              state->line, state->filename);
-        return;
-    }
-
-    struct import* import = (struct import*) calloc(1, sizeof(struct import));
-    import->filename = strdup(conf_file.c_str());
-
-    struct listnode *import_list = (listnode*) state->priv;
-    list_add_tail(import_list, &import->list);
-    INFO("Added '%s' to import list\n", import->filename);
-}
-
-static void parse_new_section(struct parse_state *state, int kw,
-                       int nargs, char **args)
-{
-    printf("[ %s %s ]\n", args[0],
-           nargs > 1 ? args[1] : "");
-    switch(kw) {
-    case K_service:
-        state->context = parse_service(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_service;
-            return;
-        }
-        break;
-    case K_on:
-        state->context = parse_action(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_action;
-            return;
-        }
-        break;
-    case K_import:
-        parse_import(state, nargs, args);
-        break;
-    }
-    state->parse_line = parse_line_no_op;
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
-    struct listnode import_list;
-    struct listnode *node;
-    char *args[INIT_PARSER_MAXARGS];
-
-    int nargs = 0;
-
+void Parser::ParseData(const std::string& filename, const std::string& data) {
     //TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
 
     parse_state state;
-    state.filename = fn;
+    state.filename = filename.c_str();
     state.line = 0;
     state.ptr = &data_copy[0];
     state.nexttoken = 0;
-    state.parse_line = parse_line_no_op;
 
-    list_init(&import_list);
-    state.priv = &import_list;
+    SectionParser* section_parser = nullptr;
+    std::vector<std::string> args;
 
     for (;;) {
         switch (next_token(&state)) {
         case T_EOF:
-            state.parse_line(&state, 0, 0);
-            goto parser_done;
+            if (section_parser) {
+                section_parser->EndSection();
+            }
+            return;
         case T_NEWLINE:
             state.line++;
-            if (nargs) {
-                int kw = lookup_keyword(args[0]);
-                if (kw_is(kw, SECTION)) {
-                    state.parse_line(&state, 0, 0);
-                    parse_new_section(&state, kw, nargs, args);
-                } else {
-                    state.parse_line(&state, nargs, args);
-                }
-                nargs = 0;
+            if (args.empty()) {
+                break;
             }
+            if (section_parsers_.count(args[0])) {
+                if (section_parser) {
+                    section_parser->EndSection();
+                }
+                section_parser = section_parsers_[args[0]].get();
+                std::string ret_err;
+                if (!section_parser->ParseSection(args, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                    section_parser = nullptr;
+                }
+            } else if (section_parser) {
+                std::string ret_err;
+                if (!section_parser->ParseLineSection(args, state.filename,
+                                                      state.line, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                }
+            }
+            args.clear();
             break;
         case T_TEXT:
-            if (nargs < INIT_PARSER_MAXARGS) {
-                args[nargs++] = state.text;
-            }
+            args.emplace_back(state.text);
             break;
         }
     }
-
-parser_done:
-    list_for_each(node, &import_list) {
-         struct import* import = node_to_item(node, struct import, list);
-         if (!init_parse_config(import->filename)) {
-             ERROR("could not import file '%s' from '%s': %s\n",
-                   import->filename, fn, strerror(errno));
-         }
-    }
 }
 
-static bool init_parse_config_file(const char* path) {
-    INFO("Parsing file %s...\n", path);
+bool Parser::ParseConfigFile(const std::string& path) {
+    INFO("Parsing file %s...\n", path.c_str());
     Timer t;
     std::string data;
-    if (!read_file(path, &data)) {
+    if (!read_file(path.c_str(), &data)) {
         return false;
     }
 
     data.push_back('\n'); // TODO: fix parse_config.
-    parse_config(path, data);
-    dump_parser_state();
+    ParseData(path, data);
+    for (const auto& sp : section_parsers_) {
+        sp.second->EndFile(path);
+    }
+    DumpState();
 
-    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
+    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
     return true;
 }
 
-static bool init_parse_config_dir(const char* path) {
-    INFO("Parsing directory %s...\n", path);
-    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir);
+bool Parser::ParseConfigDir(const std::string& path) {
+    INFO("Parsing directory %s...\n", path.c_str());
+    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
-        ERROR("Could not import directory '%s'\n", path);
+        ERROR("Could not import directory '%s'\n", path.c_str());
         return false;
     }
     dirent* current_file;
     while ((current_file = readdir(config_dir.get()))) {
         std::string current_path =
-            android::base::StringPrintf("%s/%s", path, current_file->d_name);
+            android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
         // Ignore directories and only process regular files.
         if (current_file->d_type == DT_REG) {
-            if (!init_parse_config_file(current_path.c_str())) {
+            if (!ParseConfigFile(current_path)) {
                 ERROR("could not import file '%s'\n", current_path.c_str());
             }
         }
@@ -402,97 +132,14 @@
     return true;
 }
 
-bool init_parse_config(const char* path) {
-    if (is_dir(path)) {
-        return init_parse_config_dir(path);
+bool Parser::ParseConfig(const std::string& path) {
+    if (is_dir(path.c_str())) {
+        return ParseConfigDir(path);
     }
-    return init_parse_config_file(path);
+    return ParseConfigFile(path);
 }
 
-static void *parse_service(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs < 3) {
-        parse_error(state, "services must have a name and a program\n");
-        return nullptr;
-    }
-    std::vector<std::string> str_args(args + 2, args + nargs);
-    std::string ret_err;
-    Service* svc = ServiceManager::GetInstance().AddNewService(args[1], "default",
-                                                               str_args, &ret_err);
-
-    if (!svc) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
-
-    return svc;
-}
-
-static void parse_line_service(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs == 0) {
-        return;
-    }
-
-    Service* svc = static_cast<Service*>(state->context);
-    int kw = lookup_keyword(args[0]);
-    std::vector<std::string> str_args(args, args + nargs);
-    std::string ret_err;
-    bool ret = svc->HandleLine(kw, str_args, &ret_err);
-
-    if (!ret) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
-}
-
-static void *parse_action(struct parse_state* state, int nargs, char **args)
-{
-    std::string ret_err;
-    std::vector<std::string> triggers(args + 1, args + nargs);
-    Action* ret = ActionManager::GetInstance().AddNewAction(triggers, &ret_err);
-
-    if (!ret) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
-
-    return ret;
-}
-
-bool add_command_to_action(Action* action, const std::vector<std::string>& args,
-                           const std::string& filename, int line, std::string* err)
-{
-    int kw;
-    size_t n;
-
-    kw = lookup_keyword(args[0].c_str());
-    if (!kw_is(kw, COMMAND)) {
-        *err = android::base::StringPrintf("invalid command '%s'\n", args[0].c_str());
-        return false;
-    }
-
-    n = kw_nargs(kw);
-    if (args.size() < n) {
-        *err = android::base::StringPrintf("%s requires %zu %s\n",
-                                           args[0].c_str(), n - 1,
-                                           n > 2 ? "arguments" : "argument");
-        return false;
-    }
-
-    action->AddCommand(kw_func(kw), args, filename, line);
-    return true;
-}
-
-static void parse_line_action(struct parse_state* state, int nargs, char **args)
-{
-    if (nargs == 0) {
-        return;
-    }
-
-    Action* action = static_cast<Action*>(state->context);
-    std::vector<std::string> str_args(args, args + nargs);
-    std::string ret_err;
-    bool ret = add_command_to_action(action, str_args, state->filename,
-                                     state->line, &ret_err);
-    if (!ret) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
+void Parser::DumpState() const {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
 }
diff --git a/init/init_parser.h b/init/init_parser.h
index 709dca8..5ed30ad 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -17,16 +17,39 @@
 #ifndef _INIT_INIT_PARSER_H_
 #define _INIT_INIT_PARSER_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
-#define INIT_PARSER_MAXARGS 64
+class SectionParser {
+public:
+    virtual ~SectionParser() {
+    }
+    virtual bool ParseSection(const std::vector<std::string>& args,
+                              std::string* err) = 0;
+    virtual bool ParseLineSection(const std::vector<std::string>& args,
+                                  const std::string& filename, int line,
+                                  std::string* err) const = 0;
+    virtual void EndSection() = 0;
+    virtual void EndFile(const std::string& filename) = 0;
+};
 
-class Action;
+class Parser {
+public:
+    static Parser& GetInstance();
+    void DumpState() const;
+    bool ParseConfig(const std::string& path);
+    void AddSectionParser(const std::string& name,
+                          std::unique_ptr<SectionParser> parser);
 
-bool init_parse_config(const char* path);
-int expand_props(const std::string& src, std::string* dst);
-bool add_command_to_action(Action* action, const std::vector<std::string>& args,
-                           const std::string& filename, int line, std::string* err);
+private:
+    Parser();
+
+    void ParseData(const std::string& filename, const std::string& data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
+
+    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+};
 
 #endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
new file mode 100644
index 0000000..dc2357b
--- /dev/null
+++ b/init/keyword_map.h
@@ -0,0 +1,77 @@
+/*
+ * 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 _INIT_KEYWORD_MAP_H_
+#define _INIT_KEYWORD_MAP_H_
+
+#include <map>
+#include <string>
+
+#include <base/stringprintf.h>
+
+template <typename Function>
+class KeywordMap {
+public:
+    using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
+    using Map = const std::map<std::string, FunctionInfo>;
+
+    virtual ~KeywordMap() {
+    }
+
+    const Function FindFunction(const std::string& keyword,
+                                size_t num_args,
+                                std::string* err) const {
+        using android::base::StringPrintf;
+
+        auto function_info_it = map().find(keyword);
+        if (function_info_it == map().end()) {
+            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
+            return nullptr;
+        }
+
+        auto function_info = function_info_it->second;
+
+        auto min_args = std::get<0>(function_info);
+        auto max_args = std::get<1>(function_info);
+        if (min_args == max_args && num_args != min_args) {
+            *err = StringPrintf("%s requires %zu argument%s",
+                                keyword.c_str(), min_args,
+                                (min_args > 1 || min_args == 0) ? "s" : "");
+            return nullptr;
+        }
+
+        if (num_args < min_args || num_args > max_args) {
+            if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
+                *err = StringPrintf("%s requires at least %zu argument%s",
+                                    keyword.c_str(), min_args,
+                                    min_args > 1 ? "s" : "");
+            } else {
+                *err = StringPrintf("%s requires between %zu and %zu arguments",
+                                    keyword.c_str(), min_args, max_args);
+            }
+            return nullptr;
+        }
+
+        return std::get<Function>(function_info);
+    }
+
+private:
+//Map of keyword ->
+//(minimum number of arguments, maximum number of arguments, function pointer)
+    virtual Map& map() const = 0;
+};
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
deleted file mode 100644
index 922feee..0000000
--- a/init/keywords.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef KEYWORD
-#include <string>
-#include <vector>
-int do_bootchart_init(const std::vector<std::string>& args);
-int do_class_start(const std::vector<std::string>& args);
-int do_class_stop(const std::vector<std::string>& args);
-int do_class_reset(const std::vector<std::string>& args);
-int do_domainname(const std::vector<std::string>& args);
-int do_enable(const std::vector<std::string>& args);
-int do_exec(const std::vector<std::string>& args);
-int do_export(const std::vector<std::string>& args);
-int do_hostname(const std::vector<std::string>& args);
-int do_ifup(const std::vector<std::string>& args);
-int do_insmod(const std::vector<std::string>& args);
-int do_installkey(const std::vector<std::string>& args);
-int do_mkdir(const std::vector<std::string>& args);
-int do_mount_all(const std::vector<std::string>& args);
-int do_mount(const std::vector<std::string>& args);
-int do_powerctl(const std::vector<std::string>& args);
-int do_restart(const std::vector<std::string>& args);
-int do_restorecon(const std::vector<std::string>& args);
-int do_restorecon_recursive(const std::vector<std::string>& args);
-int do_rm(const std::vector<std::string>& args);
-int do_rmdir(const std::vector<std::string>& args);
-int do_setprop(const std::vector<std::string>& args);
-int do_setrlimit(const std::vector<std::string>& args);
-int do_start(const std::vector<std::string>& args);
-int do_stop(const std::vector<std::string>& args);
-int do_swapon_all(const std::vector<std::string>& args);
-int do_trigger(const std::vector<std::string>& args);
-int do_symlink(const std::vector<std::string>& args);
-int do_sysclktz(const std::vector<std::string>& args);
-int do_write(const std::vector<std::string>& args);
-int do_copy(const std::vector<std::string>& args);
-int do_chown(const std::vector<std::string>& args);
-int do_chmod(const std::vector<std::string>& args);
-int do_loglevel(const std::vector<std::string>& args);
-int do_load_persist_props(const std::vector<std::string>& args);
-int do_load_all_props(const std::vector<std::string>& args);
-int do_verity_load_state(const std::vector<std::string>& args);
-int do_verity_update_state(const std::vector<std::string>& args);
-int do_wait(const std::vector<std::string>& args);
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
-enum {
-    K_UNKNOWN,
-#endif
-    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)
-    KEYWORD(chmod,       COMMAND, 2, do_chmod)
-    KEYWORD(chown,       COMMAND, 2, do_chown)
-    KEYWORD(class,       OPTION,  0, 0)
-    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
-    KEYWORD(class_start, COMMAND, 1, do_class_start)
-    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
-    KEYWORD(console,     OPTION,  0, 0)
-    KEYWORD(copy,        COMMAND, 2, do_copy)
-    KEYWORD(critical,    OPTION,  0, 0)
-    KEYWORD(disabled,    OPTION,  0, 0)
-    KEYWORD(domainname,  COMMAND, 1, do_domainname)
-    KEYWORD(enable,      COMMAND, 1, do_enable)
-    KEYWORD(exec,        COMMAND, 1, do_exec)
-    KEYWORD(export,      COMMAND, 2, do_export)
-    KEYWORD(group,       OPTION,  0, 0)
-    KEYWORD(hostname,    COMMAND, 1, do_hostname)
-    KEYWORD(ifup,        COMMAND, 1, do_ifup)
-    KEYWORD(import,      SECTION, 1, 0)
-    KEYWORD(insmod,      COMMAND, 1, do_insmod)
-    KEYWORD(installkey,  COMMAND, 1, do_installkey)
-    KEYWORD(ioprio,      OPTION,  0, 0)
-    KEYWORD(keycodes,    OPTION,  0, 0)
-    KEYWORD(load_all_props,        COMMAND, 0, do_load_all_props)
-    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
-    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
-    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
-    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)
-    KEYWORD(mount,       COMMAND, 3, do_mount)
-    KEYWORD(oneshot,     OPTION,  0, 0)
-    KEYWORD(onrestart,   OPTION,  0, 0)
-    KEYWORD(on,          SECTION, 0, 0)
-    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
-    KEYWORD(restart,     COMMAND, 1, do_restart)
-    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
-    KEYWORD(restorecon_recursive,  COMMAND, 1, do_restorecon_recursive)
-    KEYWORD(rm,          COMMAND, 1, do_rm)
-    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
-    KEYWORD(seclabel,    OPTION,  0, 0)
-    KEYWORD(service,     SECTION, 0, 0)
-    KEYWORD(setenv,      OPTION,  2, 0)
-    KEYWORD(setprop,     COMMAND, 2, do_setprop)
-    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
-    KEYWORD(socket,      OPTION,  0, 0)
-    KEYWORD(start,       COMMAND, 1, do_start)
-    KEYWORD(stop,        COMMAND, 1, do_stop)
-    KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
-    KEYWORD(symlink,     COMMAND, 1, do_symlink)
-    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
-    KEYWORD(trigger,     COMMAND, 1, do_trigger)
-    KEYWORD(user,        OPTION,  0, 0)
-    KEYWORD(verity_load_state,      COMMAND, 0, do_verity_load_state)
-    KEYWORD(verity_update_state,    COMMAND, 0, do_verity_update_state)
-    KEYWORD(wait,        COMMAND, 1, do_wait)
-    KEYWORD(write,       COMMAND, 2, do_write)
-    KEYWORD(writepid,    OPTION,  0, 0)
-#ifdef __MAKE_KEYWORD_ENUM__
-    KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/readme.txt b/init/readme.txt
index d70c6f3..bf440c2 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -2,8 +2,8 @@
 Android Init Language
 ---------------------
 
-The Android Init Language consists of four broad classes of statements,
-which are Actions, Commands, Services, and Options.
+The Android Init Language consists of five broad classes of statements,
+which are Actions, Commands, Services, Options, and Imports.
 
 All of these are line-oriented, consisting of tokens separated by
 whitespace.  The c-style backslash escapes may be used to insert
@@ -17,9 +17,37 @@
 or options belong to the section most recently declared.  Commands
 or options before the first section are ignored.
 
-Actions and Services have unique names.  If a second Action or Service
-is declared with the same name as an existing one, it is ignored as
-an error.  (??? should we override instead)
+Actions and Services have unique names.  If a second Action is defined
+with the same name as an existing one, its commands are appended to
+the commands of the existing action.  If a second Service is defined
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plaintext files that take the .rc file
+extension.  These are typically multiple of these in multiple
+locations on the system, described below.
+
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution.  It is responsible for the initial
+set up of the system.  It imports /init.${ro.hardware}.rc which is the
+primary vendor supplied .rc file.
+
+During the mount_all command, the init executable loads all of the
+files contained within the /{system,vendor,odm}/etc/init/ directories.
+These directories are intended for all Actions and Services used after
+file system mounting.
+
+The intention of these directories is as follows
+   1) /system/etc/init/ is for core system items such as
+      SurfaceFlinger and MediaService.
+   2) /vendor/etc/init/ is for SoC vendor items such as actions or
+      daemons needed for core SoC functionality.
+   3) /odm/etc/init/ is for device manufacturer items such as
+      actions or daemons needed for motion sensor or other peripheral
+      functionality.
 
 
 Actions
@@ -37,7 +65,7 @@
 
 Actions take the form of:
 
-on <trigger>
+on <trigger> [&& <trigger>]*
    <command>
    <command>
    <command>
@@ -117,25 +145,36 @@
 
 Triggers
 --------
-Triggers are strings which can be used to match certain kinds
-of events and used to cause an action to occur.
+Triggers are strings which can be used to match certain kinds of
+events and used to cause an action to occur.
 
-boot
-   This is the first trigger that will occur when init starts
-   (after /init.conf is loaded)
+Triggers are subdivided into event triggers and property triggers.
 
-<name>=<value>
-   Triggers of this form occur when the property <name> is set
-   to the specific value <value>.
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable.  These
+take the form of a simple string such as 'boot' or 'late-init'.
 
-   One can also test multiple properties to execute a group
-   of commands. For example:
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value.  These take the form of 'property:<name>=<value>' and
+'property:<name>=*' respectively.  Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
 
-   on property:test.a=1 && property:test.b=1
-       setprop test.c 1
+An Action can have multiple property triggers but may only have one
+event trigger.
 
-   The above stub sets test.c to 1 only when
-   both test.a=1 and test.b=1
+For example:
+'on boot && property:a=b' defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
+
+'on property:a=b && property:c=d' defines an action that is executed
+at three times,
+   1) During initial boot if property a=b and property c=d
+   2) Any time that property a transitions to value b, while property
+      c already equals d.
+   3) Any time that property c transitions to value d, while property
+      a already equals b.
 
 
 Commands
@@ -197,12 +236,6 @@
 ifup <interface>
    Bring the network interface <interface> online.
 
-import <path>
-   Parse an init config file, extending the current configuration.
-   If <path> is a directory, each file in the directory is parsed as
-   a config file. It is not recursive, nested directories will
-   not be parsed.
-
 insmod <path>
    Install the module at <path>
 
@@ -304,19 +337,30 @@
    it will be truncated. Properties are expanded within <content>.
 
 
+Imports
+-------
+The import keyword is not a command, but rather its own section and is
+handled immediately after the .rc file that contains it has finished
+being parsed.  It takes the below form:
+
+import <path>
+   Parse an init config file, extending the current configuration.
+   If <path> is a directory, each file in the directory is parsed as
+   a config file. It is not recursive, nested directories will
+   not be parsed.
+
+There are only two times where the init executable imports .rc files,
+   1) When it imports /init.rc during initial boot
+   2) When it imports /{system,vendor,odm}/etc/init/ during mount_all
+
+
 Properties
 ----------
-Init updates some system properties to provide some insight into
-what it's doing:
-
-init.action
-   Equal to the name of the action currently being executed or "" if none
-
-init.command
-   Equal to the command being executed or "" if none.
+Init provides information about the services that it is responsible
+for via the below properties.
 
 init.svc.<name>
-   State of a named service ("stopped", "running", "restarting")
+   State of a named service ("stopped", "stopping", "running", "restarting")
 
 
 Bootcharting
diff --git a/init/service.cpp b/init/service.cpp
index a370d25..a3c5ca4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -32,11 +32,13 @@
 #include "action.h"
 #include "init.h"
 #include "init_parser.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 #include "util.h"
 
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
 #define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ...
 #define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery
 
@@ -84,7 +86,7 @@
         return;
     }
 
-    std::string prop_name = android::base::StringPrintf("init.svc.%s", name_.c_str());
+    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
     if (prop_name.length() >= PROP_NAME_MAX) {
         // If the property name would be too long, we can't set it.
         ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
@@ -104,8 +106,7 @@
 
     // Remove any sockets we may have created.
     for (const auto& si : sockets_) {
-        std::string tmp = android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s",
-                                                      si.name.c_str());
+        std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
         unlink(tmp.c_str());
     }
 
@@ -168,148 +169,156 @@
     }
 }
 
-bool Service::HandleLine(int kw, const std::vector<std::string>& args, std::string* err) {
-    std::vector<std::string> str_args;
+bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
+    classname_ = args[1];
+    return true;
+}
 
-    ioprio_class_ = IoSchedClass_NONE;
+bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CONSOLE;
+    return true;
+}
 
-    switch (kw) {
-    case K_class:
-        if (args.size() != 2) {
-            *err = "class option requires a classname\n";
-            return false;
-        } else {
-            classname_ = args[1];
-        }
-        break;
-    case K_console:
-        flags_ |= SVC_CONSOLE;
-        break;
-    case K_disabled:
-        flags_ |= SVC_DISABLED;
-        flags_ |= SVC_RC_DISABLED;
-        break;
-    case K_ioprio:
-        if (args.size() != 3) {
-            *err = "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n";
-            return false;
-        } else {
-            ioprio_pri_ = std::stoul(args[2], 0, 8);
+bool Service::HandleCritical(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CRITICAL;
+    return true;
+}
 
-            if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
-                *err = "priority value must be range 0 - 7\n";
-                return false;
-            }
+bool Service::HandleDisabled(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_DISABLED;
+    flags_ |= SVC_RC_DISABLED;
+    return true;
+}
 
-            if (args[1] == "rt") {
-                ioprio_class_ = IoSchedClass_RT;
-            } else if (args[1] == "be") {
-                ioprio_class_ = IoSchedClass_BE;
-            } else if (args[1] == "idle") {
-                ioprio_class_ = IoSchedClass_IDLE;
-            } else {
-                *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>\n";
-                return false;
-            }
-        }
-        break;
-    case K_group:
-        if (args.size() < 2) {
-            *err = "group option requires a group id\n";
-            return false;
-        } else if (args.size() > NR_SVC_SUPP_GIDS + 2) {
-            *err = android::base::StringPrintf("group option accepts at most %d supp. groups\n",
-                                               NR_SVC_SUPP_GIDS);
-            return false;
-        } else {
-            gid_ = decode_uid(args[1].c_str());
-            for (std::size_t n = 2; n < args.size(); n++) {
-                supp_gids_.push_back(decode_uid(args[n].c_str()));
-            }
-        }
-        break;
-    case K_keycodes:
-        if (args.size() < 2) {
-            *err = "keycodes option requires atleast one keycode\n";
-            return false;
-        } else {
-            for (std::size_t i = 1; i < args.size(); i++) {
-                keycodes_.push_back(std::stoi(args[i]));
-            }
-        }
-        break;
-    case K_oneshot:
-        flags_ |= SVC_ONESHOT;
-        break;
-    case K_onrestart:
-        if (args.size() < 2) {
-            return false;
-        }
-        str_args.assign(args.begin() + 1, args.end());
-        add_command_to_action(&onrestart_, str_args, "", 0, err);
-        break;
-    case K_critical:
-        flags_ |= SVC_CRITICAL;
-        break;
-    case K_setenv: { /* name value */
-        if (args.size() < 3) {
-            *err = "setenv option requires name and value arguments\n";
-            return false;
-        }
-
-        envvars_.push_back({args[1], args[2]});
-        break;
-    }
-    case K_socket: {/* name type perm [ uid gid context ] */
-        if (args.size() < 4) {
-            *err = "socket option requires name, type, perm arguments\n";
-            return false;
-        }
-        if (args[2] != "dgram" && args[2] != "stream" &&
-            args[2] != "seqpacket") {
-            *err = "socket type must be 'dgram', 'stream' or 'seqpacket'\n";
-            return false;
-        }
-
-        int perm = std::stoul(args[3], 0, 8);
-        uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
-        gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
-        std::string socketcon = args.size() > 6 ? args[6] : "";
-
-        sockets_.push_back({args[1], args[2], uid, gid, perm, socketcon});
-        break;
-    }
-    case K_user:
-        if (args.size() != 2) {
-            *err = "user option requires a user id\n";
-            return false;
-        } else {
-            uid_ = decode_uid(args[1].c_str());
-        }
-        break;
-    case K_seclabel:
-        if (args.size() != 2) {
-            *err = "seclabel option requires a label string\n";
-            return false;
-        } else {
-            seclabel_ = args[1];
-        }
-        break;
-    case K_writepid:
-        if (args.size() < 2) {
-            *err = "writepid option requires at least one filename\n";
-            return false;
-        }
-        writepid_files_.assign(args.begin() + 1, args.end());
-        break;
-
-    default:
-        *err = android::base::StringPrintf("invalid option '%s'\n", args[0].c_str());
-        return false;
+bool Service::HandleGroup(const std::vector<std::string>& args, std::string* err) {
+    gid_ = decode_uid(args[1].c_str());
+    for (std::size_t n = 2; n < args.size(); n++) {
+        supp_gids_.emplace_back(decode_uid(args[n].c_str()));
     }
     return true;
 }
 
+bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
+    ioprio_pri_ = std::stoul(args[2], 0, 8);
+
+    if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
+        *err = "priority value must be range 0 - 7";
+        return false;
+    }
+
+    if (args[1] == "rt") {
+        ioprio_class_ = IoSchedClass_RT;
+    } else if (args[1] == "be") {
+        ioprio_class_ = IoSchedClass_BE;
+    } else if (args[1] == "idle") {
+        ioprio_class_ = IoSchedClass_IDLE;
+    } else {
+        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+        return false;
+    }
+
+    return true;
+}
+
+bool Service::HandleKeycodes(const std::vector<std::string>& args, std::string* err) {
+    for (std::size_t i = 1; i < args.size(); i++) {
+        keycodes_.emplace_back(std::stoi(args[i]));
+    }
+    return true;
+}
+
+bool Service::HandleOneshot(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_ONESHOT;
+    return true;
+}
+
+bool Service::HandleOnrestart(const std::vector<std::string>& args, std::string* err) {
+    std::vector<std::string> str_args(args.begin() + 1, args.end());
+    onrestart_.AddCommand(str_args, "", 0, err);
+    return true;
+}
+
+bool Service::HandleSeclabel(const std::vector<std::string>& args, std::string* err) {
+    seclabel_ = args[1];
+    return true;
+}
+
+bool Service::HandleSetenv(const std::vector<std::string>& args, std::string* err) {
+    envvars_.emplace_back(args[1], args[2]);
+    return true;
+}
+
+/* name type perm [ uid gid context ] */
+bool Service::HandleSocket(const std::vector<std::string>& args, std::string* err) {
+    if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
+        return false;
+    }
+
+    int perm = std::stoul(args[3], 0, 8);
+    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    std::string socketcon = args.size() > 6 ? args[6] : "";
+
+    sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
+    return true;
+}
+
+bool Service::HandleUser(const std::vector<std::string>& args, std::string* err) {
+    uid_ = decode_uid(args[1].c_str());
+    return true;
+}
+
+bool Service::HandleWritepid(const std::vector<std::string>& args, std::string* err) {
+    writepid_files_.assign(args.begin() + 1, args.end());
+    return true;
+}
+
+class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
+public:
+    OptionHandlerMap() {
+    }
+private:
+    Map& map() const override;
+};
+
+Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map option_handlers = {
+        {"class",       {1,     1,    &Service::HandleClass}},
+        {"console",     {0,     0,    &Service::HandleConsole}},
+        {"critical",    {0,     0,    &Service::HandleCritical}},
+        {"disabled",    {0,     0,    &Service::HandleDisabled}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
+        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
+        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
+        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
+        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
+        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
+        {"setenv",      {2,     2,    &Service::HandleSetenv}},
+        {"socket",      {3,     6,    &Service::HandleSocket}},
+        {"user",        {1,     1,    &Service::HandleUser}},
+        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
+    };
+    return option_handlers;
+}
+
+bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
+    if (args.empty()) {
+        *err = "option needed, but not provided";
+        return false;
+    }
+
+    static const OptionHandlerMap handler_map;
+    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);
+
+    if (!handler) {
+        return false;
+    }
+
+    return (this->*handler)(args, err);
+}
+
 bool Service::Start(const std::vector<std::string>& dynamic_args) {
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
@@ -396,7 +405,7 @@
         umask(077);
         if (properties_initialized()) {
             get_property_workspace(&fd, &sz);
-            std::string tmp = android::base::StringPrintf("%d,%d", dup(fd), sz);
+            std::string tmp = StringPrintf("%d,%d", dup(fd), sz);
             add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
         }
 
@@ -418,9 +427,9 @@
             }
         }
 
-        std::string pid_str = android::base::StringPrintf("%d", pid);
+        std::string pid_str = StringPrintf("%d", pid);
         for (const auto& file : writepid_files_) {
-            if (!android::base::WriteStringToFile(pid_str, file)) {
+            if (!WriteStringToFile(pid_str, file)) {
                 ERROR("couldn't write %s to %s: %s\n",
                       pid_str.c_str(), file.c_str(), strerror(errno));
             }
@@ -609,9 +618,8 @@
 }
 
 void Service::PublishSocket(const std::string& name, int fd) const {
-    std::string key = android::base::StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s",
-                                                  name.c_str());
-    std::string val = android::base::StringPrintf("%d", fd);
+    std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
+    std::string val = StringPrintf("%d", fd);
     add_environment(key.c_str(), val.c_str());
 
     /* make sure we don't close-on-exec */
@@ -628,31 +636,14 @@
     return instance;
 }
 
-Service* ServiceManager::AddNewService(const std::string& name,
-                                       const std::string& classname,
-                                       const std::vector<std::string>& args,
-                                       std::string* err) {
-    if (!IsValidName(name)) {
-        *err = android::base::StringPrintf("invalid service name '%s'\n", name.c_str());
-        return nullptr;
+void ServiceManager::AddService(std::unique_ptr<Service> service) {
+    Service* old_service = FindServiceByName(service->name());
+    if (old_service) {
+        ERROR("ignored duplicate definition of service '%s'",
+              service->name().c_str());
+        return;
     }
-
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
-    if (svc) {
-        *err = android::base::StringPrintf("ignored duplicate definition of service '%s'\n",
-                                           name.c_str());
-        return nullptr;
-    }
-
-    std::unique_ptr<Service> svc_p(new Service(name, classname, args));
-    if (!svc_p) {
-        ERROR("Couldn't allocate service for service '%s'", name.c_str());
-        return nullptr;
-    }
-    svc = svc_p.get();
-    services_.push_back(std::move(svc_p));
-
-    return svc;
+    services_.emplace_back(std::move(service));
 }
 
 Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
@@ -677,8 +668,7 @@
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
     exec_count_++;
-    std::string name = android::base::StringPrintf("exec %d (%s)", exec_count_,
-                                                   str_args[0].c_str());
+    std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
     unsigned flags = SVC_EXEC | SVC_ONESHOT;
 
     std::string seclabel = "";
@@ -770,8 +760,7 @@
     }
 }
 
-void ServiceManager::RemoveService(const Service& svc)
-{
+void ServiceManager::RemoveService(const Service& svc) {
     auto svc_it = std::find_if(services_.begin(), services_.end(),
                                [&svc] (const std::unique_ptr<Service>& s) {
                                    return svc.name() == s->name();
@@ -783,8 +772,44 @@
     services_.erase(svc_it);
 }
 
-bool ServiceManager::IsValidName(const std::string& name) const
-{
+void ServiceManager::DumpState() const {
+    for (const auto& s : services_) {
+        s->DumpState();
+    }
+    INFO("\n");
+}
+
+bool ServiceParser::ParseSection(const std::vector<std::string>& args,
+                                 std::string* err) {
+    if (args.size() < 3) {
+        *err = "services must have a name and a program";
+        return false;
+    }
+
+    const std::string& name = args[1];
+    if (!IsValidName(name)) {
+        *err = StringPrintf("invalid service name '%s'", name.c_str());
+        return false;
+    }
+
+    std::vector<std::string> str_args(args.begin() + 2, args.end());
+    service_ = std::make_unique<Service>(name, "default", str_args);
+    return true;
+}
+
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
+                                     const std::string& filename, int line,
+                                     std::string* err) const {
+    return service_ ? service_->HandleLine(args, err) : false;
+}
+
+void ServiceParser::EndSection() {
+    if (service_) {
+        ServiceManager::GetInstance().AddService(std::move(service_));
+    }
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
     if (name.size() > 16) {
         return false;
     }
@@ -795,11 +820,3 @@
     }
     return true;
 }
-
-void ServiceManager::DumpState() const
-{
-    for (const auto& s : services_) {
-        s->DumpState();
-    }
-    INFO("\n");
-}
diff --git a/init/service.h b/init/service.h
index 1ecf78a..10eb736 100644
--- a/init/service.h
+++ b/init/service.h
@@ -26,6 +26,8 @@
 #include <vector>
 
 #include "action.h"
+#include "init_parser.h"
+#include "keyword_map.h"
 
 #define SVC_DISABLED       0x001  // do not autostart with class
 #define SVC_ONESHOT        0x002  // do not restart on exit
@@ -73,7 +75,7 @@
             unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
             const std::string& seclabel,  const std::vector<std::string>& args);
 
-    bool HandleLine(int kw, const std::vector<std::string>& args, std::string* err);
+    bool HandleLine(const std::vector<std::string>& args, std::string* err);
     bool Start(const std::vector<std::string>& dynamic_args);
     bool Start();
     bool StartIfNotDisabled();
@@ -99,12 +101,31 @@
     const std::vector<std::string>& args() const { return args_; }
 
 private:
+    using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
+                                             std::string* err);
+    class OptionHandlerMap;
+
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
     void OpenConsole() const;
     void PublishSocket(const std::string& name, int fd) const;
 
+    bool HandleClass(const std::vector<std::string>& args, std::string* err);
+    bool HandleConsole(const std::vector<std::string>& args, std::string* err);
+    bool HandleCritical(const std::vector<std::string>& args, std::string* err);
+    bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
+    bool HandleGroup(const std::vector<std::string>& args, std::string* err);
+    bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
+    bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
+    bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
+    bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
+    bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
+    bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
+    bool HandleSocket(const std::vector<std::string>& args, std::string* err);
+    bool HandleUser(const std::vector<std::string>& args, std::string* err);
+    bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
+
     std::string name_;
     std::string classname_;
 
@@ -141,9 +162,7 @@
 public:
     static ServiceManager& GetInstance();
 
-    Service* AddNewService(const std::string& name, const std::string& classname,
-                                           const std::vector<std::string>& args,
-                                           std::string* err);
+    void AddService(std::unique_ptr<Service> service);
     Service* MakeExecOneshotService(const std::vector<std::string>& args);
     Service* FindServiceByName(const std::string& name) const;
     Service* FindServiceByPid(pid_t pid) const;
@@ -155,13 +174,30 @@
                              void (*func)(Service* svc)) const;
     void RemoveService(const Service& svc);
     void DumpState() const;
+
 private:
     ServiceManager();
 
-    bool IsValidName(const std::string& name) const;
-
     static int exec_count_; // Every service needs a unique name.
     std::vector<std::unique_ptr<Service>> services_;
 };
 
+class ServiceParser : public SectionParser {
+public:
+    ServiceParser() : service_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    bool IsValidName(const std::string& name) const;
+
+    std::unique_ptr<Service> service_;
+};
+
 #endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 497c606..09f4638 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -233,7 +233,6 @@
 
     data.push_back('\n'); // TODO: fix parse_config.
     parse_config(fn, data);
-    dump_parser_state();
     return 0;
 }
 
diff --git a/init/util.cpp b/init/util.cpp
index f6131e3..1eb90e0 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -43,6 +43,7 @@
 
 #include "init.h"
 #include "log.h"
+#include "property_service.h"
 #include "util.h"
 
 /*
@@ -476,3 +477,73 @@
     }
     return S_ISDIR(info.st_mode);
 }
+
+bool expand_props(const std::string& src, std::string* dst) {
+    const char* src_ptr = src.c_str();
+
+    if (!dst) {
+        return false;
+    }
+
+    /* - variables can either be $x.y or ${x.y}, in case they are only part
+     *   of the string.
+     * - will accept $$ as a literal $.
+     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+     *   bad things will happen
+     */
+    while (*src_ptr) {
+        const char* c;
+
+        c = strchr(src_ptr, '$');
+        if (!c) {
+            dst->append(src_ptr);
+            return true;
+        }
+
+        dst->append(src_ptr, c);
+        c++;
+
+        if (*c == '$') {
+            dst->push_back(*(c++));
+            src_ptr = c;
+            continue;
+        } else if (*c == '\0') {
+            return true;
+        }
+
+        std::string prop_name;
+        if (*c == '{') {
+            c++;
+            const char* end = strchr(c, '}');
+            if (!end) {
+                // failed to find closing brace, abort.
+                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
+                return false;
+            }
+            prop_name = std::string(c, end);
+            c = end + 1;
+        } else {
+            prop_name = c;
+            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
+                  c);
+            c += prop_name.size();
+        }
+
+        if (prop_name.empty()) {
+            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
+            return false;
+        }
+
+        std::string prop_val = property_get(prop_name.c_str());
+        if (prop_val.empty()) {
+            ERROR("property '%s' doesn't exist while expanding '%s'\n",
+                  prop_name.c_str(), src.c_str());
+            return false;
+        }
+
+        dst->append(prop_val);
+        src_ptr = c;
+    }
+
+    return true;
+}
diff --git a/init/util.h b/init/util.h
index f08cb8d..a33209e 100644
--- a/init/util.h
+++ b/init/util.h
@@ -65,4 +65,5 @@
 int restorecon_recursive(const char *pathname);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
+bool expand_props(const std::string& src, std::string* dst);
 #endif
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 89fa222..12dfa18 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -41,6 +41,28 @@
   serialization/metric_sample.cc \
   serialization/serialization_utils.cc
 
+metrics_tests_sources := \
+  metrics_daemon.cc \
+  metrics_daemon_test.cc \
+  metrics_library_test.cc \
+  persistent_integer.cc \
+  persistent_integer_test.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc \
+  serialization/serialization_utils_unittest.cc \
+  timer.cc \
+  timer_test.cc \
+  uploader/metrics_hashes.cc \
+  uploader/metrics_hashes_unittest.cc \
+  uploader/metrics_log_base.cc \
+  uploader/metrics_log_base_unittest.cc \
+  uploader/metrics_log.cc \
+  uploader/mock/sender_mock.cc \
+  uploader/sender_http.cc \
+  uploader/system_profile_cache.cc \
+  uploader/upload_service.cc \
+  uploader/upload_service_test.cc \
+
 metrics_CFLAGS := -Wall \
   -Wno-char-subscripts \
   -Wno-missing-field-initializers \
@@ -125,4 +147,25 @@
 include $(BUILD_PREBUILT)
 endif # INITRC_TEMPLATE
 
+# Unit tests for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_tests
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+  libmetrics \
+  libprotobuf-cpp-lite \
+  libchromeos-http \
+  libchromeos-dbus \
+  libcutils \
+  libdbus \
+
+LOCAL_SRC_FILES := $(metrics_tests_sources)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
+
+include $(BUILD_NATIVE_TEST)
+
 endif # HOST_OS == linux
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index 4917a7c..a956b69 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -129,6 +129,9 @@
   FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
   FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
 
+  void InitForTest(const std::string& uma_events_file,
+                   const std::string& consent_file);
+
   // Sets |*result| to whether or not the |mounts_file| indicates that
   // the |device_name| is currently mounted.  Uses |buffer| of
   // |buffer_size| to read the file.  Returns false if any error.
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 5855cee..cf4d79d 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -51,8 +51,6 @@
 
 namespace {
 
-#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
-
 const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
 const char kCrashReporterUserCrashSignal[] = "UserCrash";
 const char kCrashReporterMatchRule[] =
@@ -73,63 +71,55 @@
 const char kUncleanShutdownDetectedFile[] =
     "/var/run/unclean-shutdown-detected";
 
-}  // namespace
-
 // disk stats metrics
 
 // The {Read,Write}Sectors numbers are in sectors/second.
 // A sector is usually 512 bytes.
 
-const char MetricsDaemon::kMetricReadSectorsLongName[] =
-    "Platform.ReadSectorsLong";
-const char MetricsDaemon::kMetricWriteSectorsLongName[] =
-    "Platform.WriteSectorsLong";
-const char MetricsDaemon::kMetricReadSectorsShortName[] =
-    "Platform.ReadSectorsShort";
-const char MetricsDaemon::kMetricWriteSectorsShortName[] =
-    "Platform.WriteSectorsShort";
+const char kMetricReadSectorsLongName[] = "Platform.ReadSectorsLong";
+const char kMetricWriteSectorsLongName[] = "Platform.WriteSectorsLong";
+const char kMetricReadSectorsShortName[] = "Platform.ReadSectorsShort";
+const char kMetricWriteSectorsShortName[] = "Platform.WriteSectorsShort";
 
-const int MetricsDaemon::kMetricStatsShortInterval = 1;  // seconds
-const int MetricsDaemon::kMetricStatsLongInterval = 30;  // seconds
+const int kMetricStatsShortInterval = 1;  // seconds
+const int kMetricStatsLongInterval = 30;  // seconds
 
-const int MetricsDaemon::kMetricMeminfoInterval = 30;        // seconds
+const int kMetricMeminfoInterval = 30;        // seconds
 
 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
 // sectors.
-const int MetricsDaemon::kMetricSectorsIOMax = 500000;  // sectors/second
-const int MetricsDaemon::kMetricSectorsBuckets = 50;    // buckets
+const int kMetricSectorsIOMax = 500000;  // sectors/second
+const int kMetricSectorsBuckets = 50;    // buckets
 // Page size is 4k, sector size is 0.5k.  We're not interested in page fault
 // rates that the disk cannot sustain.
-const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
-const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
+const int kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
+const int kMetricPageFaultsBuckets = 50;
 
 // Major page faults, i.e. the ones that require data to be read from disk.
 
-const char MetricsDaemon::kMetricPageFaultsLongName[] =
-    "Platform.PageFaultsLong";
-const char MetricsDaemon::kMetricPageFaultsShortName[] =
-    "Platform.PageFaultsShort";
+const char kMetricPageFaultsLongName[] = "Platform.PageFaultsLong";
+const char kMetricPageFaultsShortName[] = "Platform.PageFaultsShort";
 
 // Swap in and Swap out
 
-const char MetricsDaemon::kMetricSwapInLongName[] =
-    "Platform.SwapInLong";
-const char MetricsDaemon::kMetricSwapInShortName[] =
-    "Platform.SwapInShort";
+const char kMetricSwapInLongName[] = "Platform.SwapInLong";
+const char kMetricSwapInShortName[] = "Platform.SwapInShort";
 
-const char MetricsDaemon::kMetricSwapOutLongName[] =
-    "Platform.SwapOutLong";
-const char MetricsDaemon::kMetricSwapOutShortName[] =
-    "Platform.SwapOutShort";
+const char kMetricSwapOutLongName[] = "Platform.SwapOutLong";
+const char kMetricSwapOutShortName[] = "Platform.SwapOutShort";
 
-const char MetricsDaemon::kMetricsProcStatFileName[] = "/proc/stat";
-const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11;
+const char kMetricsProcStatFileName[] = "/proc/stat";
+const char kVmStatFileName[] = "/proc/vmstat";
+const char kMeminfoFileName[] = "/proc/meminfo";
+const int kMetricsProcStatFirstLineItemsCount = 11;
 
 // Thermal CPU throttling.
 
-const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
+const char kMetricScaledCpuFrequencyName[] =
     "Platform.CpuFrequencyThermalScaling";
 
+}  // namespace
+
 // Zram sysfs entries.
 
 const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
@@ -195,10 +185,10 @@
 }
 
 void MetricsDaemon::RunUploaderTest() {
-  upload_service_.reset(new UploadService(new SystemProfileCache(true,
-                                                                 config_root_),
-                                          metrics_lib_,
-                                          server_));
+  upload_service_.reset(new UploadService(
+      new SystemProfileCache(true, base::FilePath(config_root_)),
+      metrics_lib_,
+      server_));
   upload_service_->Init(upload_interval_, metrics_file_);
   upload_service_->UploadEvent();
 }
@@ -227,18 +217,17 @@
                          bool uploader_active,
                          bool dbus_enabled,
                          MetricsLibraryInterface* metrics_lib,
-                         const string& vmstats_path,
                          const string& scaling_max_freq_path,
                          const string& cpuinfo_max_freq_path,
                          const base::TimeDelta& upload_interval,
                          const string& server,
                          const string& metrics_file,
                          const string& config_root) {
+  CHECK(metrics_lib);
   testing_ = testing;
   uploader_active_ = uploader_active;
   dbus_enabled_ = dbus_enabled;
   config_root_ = config_root;
-  DCHECK(metrics_lib != nullptr);
   metrics_lib_ = metrics_lib;
 
   upload_interval_ = upload_interval;
@@ -286,7 +275,6 @@
   weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
   version_cycle_.reset(new PersistentInteger("version.cycle"));
 
-  vmstats_path_ = vmstats_path;
   scaling_max_freq_path_ = scaling_max_freq_path;
   cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
 }
@@ -510,48 +498,22 @@
 
 bool MetricsDaemon::VmStatsParseStats(const char* stats,
                                       struct VmstatRecord* record) {
-  // a mapping of string name to field in VmstatRecord and whether we found it
-  struct mapping {
-    const string name;
-    uint64_t* value_p;
-    bool found;
-  } map[] =
-      { { .name = "pgmajfault",
-          .value_p = &record->page_faults_,
-          .found = false },
-        { .name = "pswpin",
-          .value_p = &record->swap_in_,
-          .found = false },
-        { .name = "pswpout",
-          .value_p = &record->swap_out_,
-          .found = false }, };
+  CHECK(stats);
+  CHECK(record);
+  base::StringPairs pairs;
+  base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);
 
-  // Each line in the file has the form
-  // <ID> <VALUE>
-  // for instance:
-  // nr_free_pages 213427
-  vector<string> lines;
-  Tokenize(stats, "\n", &lines);
-  for (vector<string>::iterator it = lines.begin();
-       it != lines.end(); ++it) {
-    vector<string> tokens;
-    base::SplitString(*it, ' ', &tokens);
-    if (tokens.size() == 2) {
-      for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
-        if (!tokens[0].compare(map[i].name)) {
-          if (!base::StringToUint64(tokens[1], map[i].value_p))
-            return false;
-          map[i].found = true;
-        }
-      }
-    } else {
-      LOG(WARNING) << "unexpected vmstat format";
+  for (base::StringPairs::iterator it = pairs.begin(); it != pairs.end(); ++it) {
+    if (it->first == "pgmajfault" &&
+        !base::StringToUint64(it->second, &record->page_faults_)) {
+      return false;
     }
-  }
-  // make sure we got all the stats
-  for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
-    if (map[i].found == false) {
-      LOG(WARNING) << "vmstat missing " << map[i].name;
+    if (it->first == "pswpin" &&
+        !base::StringToUint64(it->second, &record->swap_in_)) {
+      return false;
+    }
+    if (it->first == "pswpout" &&
+        !base::StringToUint64(it->second, &record->swap_out_)) {
       return false;
     }
   }
@@ -559,14 +521,12 @@
 }
 
 bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
+  CHECK(stats);
   string value_string;
-  FilePath* path = new FilePath(vmstats_path_);
-  if (!base::ReadFileToString(*path, &value_string)) {
-    delete path;
-    LOG(WARNING) << "cannot read " << vmstats_path_;
+  if (!base::ReadFileToString(base::FilePath(kVmStatFileName), &value_string)) {
+    LOG(WARNING) << "cannot read " << kVmStatFileName;
     return false;
   }
-  delete path;
   return VmStatsParseStats(value_string.c_str(), stats);
 }
 
@@ -748,7 +708,7 @@
 
 void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
   string meminfo_raw;
-  const FilePath meminfo_path("/proc/meminfo");
+  const FilePath meminfo_path(kMeminfoFileName);
   if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
     LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
     return;
@@ -907,11 +867,8 @@
     Tokenize(lines[iline], ": ", &tokens);
     if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
       // Name matches. Parse value and save.
-      char* rest;
-      (*fields)[ifield].value =
-          static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
-      if (*rest != '\0') {
-        LOG(WARNING) << "missing meminfo value";
+      if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
+        LOG(WARNING) << "Cound not convert " << tokens[1] << " to int";
         return false;
       }
       ifield++;
@@ -958,7 +915,7 @@
 
 bool MetricsDaemon::MemuseCallbackWork() {
   string meminfo_raw;
-  const FilePath meminfo_path("/proc/meminfo");
+  const FilePath meminfo_path(kMeminfoFileName);
   if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
     LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
     return false;
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 6f5a3bf..9180e23 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -45,7 +45,6 @@
             bool uploader_active,
             bool dbus_enabled,
             MetricsLibraryInterface* metrics_lib,
-            const std::string& vmstats_path,
             const std::string& cpuinfo_max_freq_path,
             const std::string& scaling_max_freq_path,
             const base::TimeDelta& upload_interval,
@@ -101,14 +100,6 @@
     kStatsLong,     // final wait before new collection
   };
 
-  // Data record for aggregating daily usage.
-  class UseRecord {
-   public:
-    UseRecord() : day_(0), seconds_(0) {}
-    int day_;
-    int seconds_;
-  };
-
   // Type of scale to use for meminfo histograms.  For most of them we use
   // percent of total RAM, but for some we use absolute numbers, usually in
   // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed
@@ -135,30 +126,6 @@
     uint64_t swap_out_;       // pages swapped out
   };
 
-  // Metric parameters.
-  static const char kMetricReadSectorsLongName[];
-  static const char kMetricReadSectorsShortName[];
-  static const char kMetricWriteSectorsLongName[];
-  static const char kMetricWriteSectorsShortName[];
-  static const char kMetricPageFaultsShortName[];
-  static const char kMetricPageFaultsLongName[];
-  static const char kMetricSwapInLongName[];
-  static const char kMetricSwapInShortName[];
-  static const char kMetricSwapOutLongName[];
-  static const char kMetricSwapOutShortName[];
-  static const char kMetricScaledCpuFrequencyName[];
-  static const int kMetricStatsShortInterval;
-  static const int kMetricStatsLongInterval;
-  static const int kMetricMeminfoInterval;
-  static const int kMetricSectorsIOMax;
-  static const int kMetricSectorsBuckets;
-  static const int kMetricPageFaultsMax;
-  static const int kMetricPageFaultsBuckets;
-  static const char kMetricsDiskStatsPath[];
-  static const char kMetricsVmStatsPath[];
-  static const char kMetricsProcStatFileName[];
-  static const int kMetricsProcStatFirstLineItemsCount;
-
   // Returns the active time since boot (uptime minus sleep time) in seconds.
   double GetActiveTime();
 
@@ -167,13 +134,6 @@
                                          DBusMessage* message,
                                          void* user_data);
 
-  // Updates the daily usage file, if necessary, by adding |seconds|
-  // of active use to the |day| since Epoch. If there's usage data for
-  // day in the past in the usage file, that data is sent to UMA and
-  // removed from the file. If there's already usage data for |day| in
-  // the usage file, the |seconds| are accumulated.
-  void LogDailyUseRecord(int day, int seconds);
-
   // Updates the active use time and logs time between user-space
   // process crashes.
   void ProcessUserCrash();
@@ -312,11 +272,6 @@
   // The metrics library handle.
   MetricsLibraryInterface* metrics_lib_;
 
-  // Timestamps last network state update.  This timestamp is used to
-  // sample the time from the network going online to going offline so
-  // TimeTicks ensures a monotonically increasing TimeDelta.
-  base::TimeTicks network_state_last_;
-
   // The last time that UpdateStats() was called.
   base::TimeTicks last_update_stats_time_;
 
@@ -369,7 +324,6 @@
   scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
   scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
 
-  std::string vmstats_path_;
   std::string scaling_max_freq_path_;
   std::string cpuinfo_max_freq_path_;
 
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
index c3d5cab..7f9ec43 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -75,7 +75,6 @@
               FLAGS_uploader | FLAGS_uploader_test,
               FLAGS_withdbus,
               &metrics_lib,
-              "/proc/vmstat",
               kScalingMaxFreqPath,
               kCpuinfoMaxFreqPath,
               base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
index 9b5b58e..0d2229c 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -22,11 +22,12 @@
 
 #include <base/at_exit.h>
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
+#include "constants.h"
 #include "metrics_daemon.h"
 #include "metrics_library_mock.h"
 #include "persistent_integer_mock.h"
@@ -45,7 +46,6 @@
 using ::testing::StrictMock;
 using chromeos_metrics::PersistentIntegerMock;
 
-static const char kFakeDiskStatsName[] = "fake-disk-stats";
 static const char kFakeDiskStatsFormat[] =
     "    1793     1788    %" PRIu64 "   105580    "
     "    196      175     %" PRIu64 "    30290    "
@@ -53,9 +53,6 @@
 static const uint64_t kFakeReadSectors[] = {80000, 100000};
 static const uint64_t kFakeWriteSectors[] = {3000, 4000};
 
-static const char kFakeVmStatsName[] = "fake-vm-stats";
-static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq";
-static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq";
 
 class MetricsDaemonTest : public testing::Test {
  protected:
@@ -63,78 +60,34 @@
   std::string kFakeDiskStats1;
 
   virtual void SetUp() {
+    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");
+    disk_stats_path_ = temp_dir_.path().Append("disk_stats");
+
     kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
                                            kFakeReadSectors[0],
                                            kFakeWriteSectors[0]);
     kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
                                            kFakeReadSectors[1],
                                            kFakeWriteSectors[1]);
-    CreateFakeDiskStatsFile(kFakeDiskStats0.c_str());
-    CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 10000000);
-    CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000);
 
-    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+    CreateFakeDiskStatsFile(kFakeDiskStats0);
+    CreateUint64ValueFile(cpu_max_freq_path_, 10000000);
+    CreateUint64ValueFile(scaling_max_freq_path_, 10000000);
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
     daemon_.Init(true,
                  false,
+                 true,
                  &metrics_lib_,
-                 kFakeDiskStatsName,
-                 kFakeVmStatsName,
-                 kFakeScalingMaxFreqPath,
-                 kFakeCpuinfoMaxFreqPath,
+                 scaling_max_freq_path_.value(),
+                 cpu_max_freq_path_.value(),
                  base::TimeDelta::FromMinutes(30),
-                 kMetricsServer,
-                 kMetricsFilePath,
+                 metrics::kMetricsServer,
+                 metrics::kMetricsEventsFilePath,
                  "/");
-
-    // Replace original persistent values with mock ones.
-    daily_active_use_mock_ =
-        new StrictMock<PersistentIntegerMock>("1.mock");
-    daemon_.daily_active_use_.reset(daily_active_use_mock_);
-
-    kernel_crash_interval_mock_ =
-        new StrictMock<PersistentIntegerMock>("2.mock");
-    daemon_.kernel_crash_interval_.reset(kernel_crash_interval_mock_);
-
-    user_crash_interval_mock_ =
-        new StrictMock<PersistentIntegerMock>("3.mock");
-    daemon_.user_crash_interval_.reset(user_crash_interval_mock_);
-
-    unclean_shutdown_interval_mock_ =
-        new StrictMock<PersistentIntegerMock>("4.mock");
-    daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_);
-  }
-
-  virtual void TearDown() {
-    EXPECT_EQ(0, unlink(kFakeDiskStatsName));
-    EXPECT_EQ(0, unlink(kFakeScalingMaxFreqPath));
-    EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath));
-  }
-
-  // Adds active use aggregation counters update expectations that the
-  // specified count will be added.
-  void ExpectActiveUseUpdate(int count) {
-    EXPECT_CALL(*daily_active_use_mock_, Add(count))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*kernel_crash_interval_mock_, Add(count))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*user_crash_interval_mock_, Add(count))
-        .Times(1)
-        .RetiresOnSaturation();
-  }
-
-  // As above, but ignore values of counter updates.
-  void IgnoreActiveUseUpdate() {
-    EXPECT_CALL(*daily_active_use_mock_, Add(_))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*kernel_crash_interval_mock_, Add(_))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*user_crash_interval_mock_, Add(_))
-        .Times(1)
-        .RetiresOnSaturation();
   }
 
   // Adds a metrics library mock expectation that the specified metric
@@ -177,19 +130,15 @@
   }
 
   // Creates or overwrites an input file containing fake disk stats.
-  void CreateFakeDiskStatsFile(const char* fake_stats) {
-    if (unlink(kFakeDiskStatsName) < 0) {
-      EXPECT_EQ(errno, ENOENT);
-    }
-    FILE* f = fopen(kFakeDiskStatsName, "w");
-    EXPECT_EQ(1, fwrite(fake_stats, strlen(fake_stats), 1, f));
-    EXPECT_EQ(0, fclose(f));
+  void CreateFakeDiskStatsFile(const string& fake_stats) {
+    EXPECT_EQ(base::WriteFile(disk_stats_path_,
+                              fake_stats.data(), fake_stats.size()),
+              fake_stats.size());
   }
 
   // Creates or overwrites the file in |path| so that it contains the printable
   // representation of |value|.
   void CreateUint64ValueFile(const base::FilePath& path, uint64_t value) {
-    base::DeleteFile(path, false);
     std::string value_string = base::Uint64ToString(value);
     ASSERT_EQ(value_string.length(),
               base::WriteFile(path, value_string.c_str(),
@@ -199,29 +148,19 @@
   // The MetricsDaemon under test.
   MetricsDaemon daemon_;
 
+  // Temporary directory used for tests.
+  base::ScopedTempDir temp_dir_;
+
+  // Path for the fake files.
+  base::FilePath scaling_max_freq_path_;
+  base::FilePath cpu_max_freq_path_;
+  base::FilePath disk_stats_path_;
+
   // Mocks. They are strict mock so that all unexpected
   // calls are marked as failures.
   StrictMock<MetricsLibraryMock> metrics_lib_;
-  StrictMock<PersistentIntegerMock>* daily_active_use_mock_;
-  StrictMock<PersistentIntegerMock>* kernel_crash_interval_mock_;
-  StrictMock<PersistentIntegerMock>* user_crash_interval_mock_;
-  StrictMock<PersistentIntegerMock>* unclean_shutdown_interval_mock_;
 };
 
-TEST_F(MetricsDaemonTest, CheckSystemCrash) {
-  static const char kKernelCrashDetected[] = "test-kernel-crash-detected";
-  EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-
-  base::FilePath crash_detected(kKernelCrashDetected);
-  base::WriteFile(crash_detected, "", 0);
-  EXPECT_TRUE(base::PathExists(crash_detected));
-  EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-  EXPECT_FALSE(base::PathExists(crash_detected));
-  EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-  EXPECT_FALSE(base::PathExists(crash_detected));
-  base::DeleteFile(crash_detected, false);
-}
-
 TEST_F(MetricsDaemonTest, MessageFilter) {
   // Ignore calls to SendToUMA.
   EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
@@ -232,7 +171,6 @@
   EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
   DeleteDBusMessage(msg);
 
-  IgnoreActiveUseUpdate();
   vector<string> signal_args;
   msg = NewDBusSignalString("/",
                             "org.chromium.CrashReporter",
@@ -260,25 +198,6 @@
                      /* min */ 1, /* max */ 100, /* buckets */ 50);
 }
 
-TEST_F(MetricsDaemonTest, ReportDiskStats) {
-  uint64_t read_sectors_now, write_sectors_now;
-  CreateFakeDiskStatsFile(kFakeDiskStats1.c_str());
-  daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
-  EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
-  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
-
-  MetricsDaemon::StatsState s_state = daemon_.stats_state_;
-  EXPECT_CALL(metrics_lib_,
-              SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30,
-                        _, _, _));
-  EXPECT_CALL(metrics_lib_,
-              SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30,
-                        _, _, _));
-  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, _));  // SendCpuThrottleMetrics
-  daemon_.StatsCallback();
-  EXPECT_TRUE(s_state != daemon_.stats_state_);
-}
-
 TEST_F(MetricsDaemonTest, ProcessMeminfo) {
   string meminfo =
       "MemTotal:        2000000 kB\nMemFree:          500000 kB\n"
@@ -337,24 +256,24 @@
   const int fake_max_freq = 2000000;
   int scaled_freq = 0;
   int max_freq = 0;
-  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath),
-                        fake_scaled_freq);
-  CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), fake_max_freq);
+  CreateUint64ValueFile(scaling_max_freq_path_, fake_scaled_freq);
+  CreateUint64ValueFile(cpu_max_freq_path_, fake_max_freq);
   EXPECT_TRUE(daemon_.testing_);
-  EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeScalingMaxFreqPath, &scaled_freq));
-  EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeCpuinfoMaxFreqPath, &max_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(scaling_max_freq_path_.value(),
+                                    &scaled_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(cpu_max_freq_path_.value(), &max_freq));
   EXPECT_EQ(fake_scaled_freq, scaled_freq);
   EXPECT_EQ(fake_max_freq, max_freq);
 }
 
 TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
-  CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 2001000);
+  CreateUint64ValueFile(cpu_max_freq_path_, 2001000);
   // Test the 101% and 100% cases.
-  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2001000);
+  CreateUint64ValueFile(scaling_max_freq_path_, 2001000);
   EXPECT_TRUE(daemon_.testing_);
   EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
   daemon_.SendCpuThrottleMetrics();
-  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2000000);
+  CreateUint64ValueFile(scaling_max_freq_path_, 2000000);
   EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101));
   daemon_.SendCpuThrottleMetrics();
 }
@@ -370,12 +289,14 @@
   const uint64_t page_size = 4096;
   const uint64_t zero_pages = 10 * 1000 * 1000 / page_size;
 
-  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kComprDataSizeName),
-                        compr_data_size);
-  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kOrigDataSizeName),
-                        orig_data_size);
-  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kZeroPagesName),
-                        zero_pages);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kComprDataSizeName),
+      compr_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kOrigDataSizeName),
+      orig_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kZeroPagesName), zero_pages);
 
   const uint64_t real_orig_size = orig_data_size + zero_pages * page_size;
   const uint64_t zero_ratio_percent =
@@ -390,11 +311,5 @@
   EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_pages, _, _, _));
   EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_ratio_percent, _, _, _));
 
-  EXPECT_TRUE(daemon_.ReportZram(base::FilePath(".")));
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-
-  return RUN_ALL_TESTS();
+  EXPECT_TRUE(daemon_.ReportZram(temp_dir_.path()));
 }
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
index c1998a6..5687f1b 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -140,6 +140,12 @@
   uma_events_file_ = metrics::kMetricsEventsFilePath;
 }
 
+void MetricsLibrary::InitForTest(const std::string& uma_events_file,
+                                 const std::string& consent_file) {
+  uma_events_file_ = uma_events_file;
+  consent_file_ = consent_file;
+}
+
 bool MetricsLibrary::SendToUMA(const std::string& name,
                                int sample,
                                int min,
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
index c58e3fb..7ade6ee 100644
--- a/metricsd/metrics_library_test.cc
+++ b/metricsd/metrics_library_test.cc
@@ -14,130 +14,52 @@
  * limitations under the License.
  */
 
-#include <cstring>
 
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <policy/mock_device_policy.h>
-#include <policy/libpolicy.h>
 
 #include "metrics/c_metrics_library.h"
 #include "metrics/metrics_library.h"
 
-using base::FilePath;
-using ::testing::_;
-using ::testing::Return;
-using ::testing::AnyNumber;
-
-static const FilePath kTestUMAEventsFile("test-uma-events");
-static const char kTestMounts[] = "test-mounts";
-
-ACTION_P(SetMetricsPolicy, enabled) {
-  *arg0 = enabled;
-  return true;
-}
 
 class MetricsLibraryTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    EXPECT_TRUE(lib_.uma_events_file_.empty());
-    lib_.Init();
-    EXPECT_FALSE(lib_.uma_events_file_.empty());
-    lib_.uma_events_file_ = kTestUMAEventsFile.value();
-    EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
-    device_policy_ = new policy::MockDevicePolicy();
-    EXPECT_CALL(*device_policy_, LoadPolicy())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(true));
-    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-        .Times(AnyNumber())
-        .WillRepeatedly(SetMetricsPolicy(true));
-    provider_ = new policy::PolicyProvider(device_policy_);
-    lib_.SetPolicyProvider(provider_);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    consent_file_ = temp_dir_.path().Append("consent");
+    uma_events_file_ = temp_dir_.path().Append("events");
+    lib_.InitForTest(uma_events_file_.value(), consent_file_.value());
+    EXPECT_EQ(0, WriteFile(uma_events_file_, "", 0));
     // Defeat metrics enabled caching between tests.
     lib_.cached_enabled_time_ = 0;
   }
 
-  virtual void TearDown() {
-    base::DeleteFile(FilePath(kTestMounts), false);
-    base::DeleteFile(kTestUMAEventsFile, false);
+  void SetMetricsConsent(bool enabled) {
+    if (enabled) {
+      ASSERT_EQ(base::WriteFile(consent_file_, "", 0), 0);
+    } else {
+      ASSERT_TRUE(base::DeleteFile(consent_file_, false));
+    }
   }
 
   void VerifyEnabledCacheHit(bool to_value);
   void VerifyEnabledCacheEviction(bool to_value);
 
   MetricsLibrary lib_;
-  policy::MockDevicePolicy* device_policy_;
-  policy::PolicyProvider* provider_;
+  base::ScopedTempDir temp_dir_;
+  base::FilePath consent_file_;
+  base::FilePath uma_events_file_;
 };
 
-TEST_F(MetricsLibraryTest, IsDeviceMounted) {
-  static const char kTestContents[] =
-      "0123456789abcde 0123456789abcde\nguestfs foo bar\n";
-  char buffer[1024];
-  int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 };
-  bool result;
-  EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
-                                    "nonexistent",
-                                    buffer,
-                                    1,
-                                    &result));
-  ASSERT_TRUE(base::WriteFile(base::FilePath(kTestMounts),
-                              kTestContents,
-                              strlen(kTestContents)));
-  EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
-                                    kTestMounts,
-                                    buffer,
-                                    0,
-                                    &result));
-  for (size_t i = 0; i < arraysize(block_sizes); ++i) {
-    EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_TRUE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("guestfs",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_TRUE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("0123456",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("9abcde",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("foo",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("bar",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-  }
-}
-
 TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(false));
+  SetMetricsConsent(false);
   EXPECT_FALSE(lib_.AreMetricsEnabled());
 }
 
 TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+  SetMetricsConsent(true);
   EXPECT_TRUE(lib_.AreMetricsEnabled());
 }
 
@@ -146,12 +68,12 @@
   // times in a row.
   for (int i = 0; i < 100; ++i) {
     lib_.cached_enabled_time_ = 0;
-    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-        .WillOnce(SetMetricsPolicy(!to_value));
-    ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
-    ON_CALL(*device_policy_, GetMetricsEnabled(_))
-        .WillByDefault(SetMetricsPolicy(to_value));
-    if (lib_.AreMetricsEnabled() == !to_value)
+    SetMetricsConsent(to_value);
+    lib_.AreMetricsEnabled();
+    // If we check the metrics status twice in a row, we use the cached value
+    // the second time.
+    SetMetricsConsent(!to_value);
+    if (lib_.AreMetricsEnabled() == to_value)
       return;
   }
   ADD_FAILURE() << "Did not see evidence of caching";
@@ -159,14 +81,12 @@
 
 void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
   lib_.cached_enabled_time_ = 0;
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(!to_value));
+  SetMetricsConsent(!to_value);
   ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(to_value));
-  ASSERT_LT(abs(static_cast<int>(time(nullptr) - lib_.cached_enabled_time_)),
-            5);
-  // Sleep one second (or cheat to be faster).
+
+  SetMetricsConsent(to_value);
+  // Sleep one second (or cheat to be faster) and check that we are not using
+  // the cached value.
   --lib_.cached_enabled_time_;
   ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
 }
@@ -177,50 +97,3 @@
   VerifyEnabledCacheEviction(false);
   VerifyEnabledCacheEviction(true);
 }
-
-class CMetricsLibraryTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    lib_ = CMetricsLibraryNew();
-    MetricsLibrary& ml = *reinterpret_cast<MetricsLibrary*>(lib_);
-    EXPECT_TRUE(ml.uma_events_file_.empty());
-    CMetricsLibraryInit(lib_);
-    EXPECT_FALSE(ml.uma_events_file_.empty());
-    ml.uma_events_file_ = kTestUMAEventsFile.value();
-    EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
-    device_policy_ = new policy::MockDevicePolicy();
-    EXPECT_CALL(*device_policy_, LoadPolicy())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(true));
-    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-        .Times(AnyNumber())
-        .WillRepeatedly(SetMetricsPolicy(true));
-    provider_ = new policy::PolicyProvider(device_policy_);
-    ml.SetPolicyProvider(provider_);
-    reinterpret_cast<MetricsLibrary*>(lib_)->cached_enabled_time_ = 0;
-  }
-
-  virtual void TearDown() {
-    CMetricsLibraryDelete(lib_);
-    base::DeleteFile(kTestUMAEventsFile, false);
-  }
-
-  CMetricsLibrary lib_;
-  policy::MockDevicePolicy* device_policy_;
-  policy::PolicyProvider* provider_;
-};
-
-TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) {
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(false));
-  EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_));
-}
-
-TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) {
-  EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_));
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
index 9fa5c1e..e849f00 100644
--- a/metricsd/persistent_integer.cc
+++ b/metricsd/persistent_integer.cc
@@ -28,18 +28,14 @@
 namespace chromeos_metrics {
 
 // Static class member instantiation.
-bool PersistentInteger::testing_ = false;
+std::string PersistentInteger::metrics_directory_ = metrics::kMetricsDirectory;
 
 PersistentInteger::PersistentInteger(const std::string& name) :
       value_(0),
       version_(kVersion),
       name_(name),
       synced_(false) {
-  if (testing_) {
-    backing_file_name_ = name_;
-  } else {
-    backing_file_name_ = metrics::kMetricsDirectory + name_;
-  }
+  backing_file_name_ = metrics_directory_ + name_;
 }
 
 PersistentInteger::~PersistentInteger() {}
@@ -100,8 +96,8 @@
   return read_succeeded;
 }
 
-void PersistentInteger::SetTestingMode(bool testing) {
-  testing_ = testing;
+void PersistentInteger::SetMetricsDirectory(const std::string& directory) {
+  metrics_directory_ = directory;
 }
 
 
diff --git a/metricsd/persistent_integer.h b/metricsd/persistent_integer.h
index fec001f..ecef3d1 100644
--- a/metricsd/persistent_integer.h
+++ b/metricsd/persistent_integer.h
@@ -50,10 +50,9 @@
   // Virtual only because of mock.
   virtual void Add(int64_t x);
 
-  // After calling with |testing| = true, changes some behavior for the purpose
-  // of testing.  For instance: instances created while testing use the current
-  // directory for the backing files.
-  static void SetTestingMode(bool testing);
+  // Sets the directory path for all persistent integers.
+  // This is used in unittests to change where the counters are stored.
+  static void SetMetricsDirectory(const std::string& directory);
 
  private:
   static const int kVersion = 1001;
@@ -70,8 +69,8 @@
   int32_t version_;
   std::string name_;
   std::string backing_file_name_;
+  static std::string metrics_directory_;
   bool synced_;
-  static bool testing_;
 };
 
 }  // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
index 19801f9..5e2067f 100644
--- a/metricsd/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -19,6 +19,7 @@
 #include <base/compiler_specific.h>
 #include <base/files/file_enumerator.h>
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 
 #include "persistent_integer.h"
 
@@ -30,7 +31,9 @@
 class PersistentIntegerTest : public testing::Test {
   void SetUp() override {
     // Set testing mode.
-    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
   }
 
   void TearDown() override {
@@ -45,6 +48,8 @@
       base::DeleteFile(name, false);
     }
   }
+
+  base::ScopedTempDir temp_dir_;
 };
 
 TEST_F(PersistentIntegerTest, BasicChecks) {
@@ -71,8 +76,3 @@
   pi.reset(new PersistentInteger(kBackingFileName));
   EXPECT_EQ(0, pi->Get());
 }
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/metricsd/uploader/mock/mock_system_profile_setter.h b/metricsd/uploader/mock/mock_system_profile_setter.h
index c714e9c..9b20291 100644
--- a/metricsd/uploader/mock/mock_system_profile_setter.h
+++ b/metricsd/uploader/mock/mock_system_profile_setter.h
@@ -26,7 +26,9 @@
 // Mock profile setter used for testing.
 class MockSystemProfileSetter : public SystemProfileSetter {
  public:
-  void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {}
+  bool Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {
+    return true;
+  }
 };
 
 #endif  // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 21ec229..8635fb0 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -61,7 +61,7 @@
 }
 
 SystemProfileCache::SystemProfileCache(bool testing,
-                                       const std::string& config_root)
+                                       const base::FilePath& config_root)
     : initialized_(false),
       testing_(testing),
       config_root_(config_root),
@@ -73,9 +73,7 @@
   CHECK(!initialized_)
       << "this should be called only once in the metrics_daemon lifetime.";
 
-  char property_value[PROPERTY_VALUE_MAX];
-  property_get(metrics::kBuildTargetIdProperty, property_value, "");
-  profile_.build_target_id = std::string(property_value);
+  profile_.build_target_id = GetProperty(metrics::kBuildTargetIdProperty);
 
   if (profile_.build_target_id.empty()) {
     LOG(ERROR) << "System property " << metrics::kBuildTargetIdProperty
@@ -83,11 +81,8 @@
     return false;
   }
 
-  property_get(metrics::kChannelProperty, property_value, "");
-  std::string channel(property_value);
-
-  property_get(metrics::kProductVersionProperty, property_value, "");
-  profile_.version = std::string(property_value);
+  std::string channel = GetProperty(metrics::kChannelProperty);
+  profile_.version = GetProperty(metrics::kProductVersionProperty);
 
   if (channel.empty() || profile_.version.empty()) {
     // If the channel or version is missing, the image is not official.
@@ -157,6 +152,18 @@
   return guid;
 }
 
+std::string SystemProfileCache::GetProperty(const std::string& name) {
+  if (testing_) {
+    std::string content;
+    base::ReadFileToString(config_root_.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 ac80b47..7157810 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include "base/compiler_specific.h"
+#include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "persistent_integer.h"
@@ -49,7 +50,7 @@
  public:
   SystemProfileCache();
 
-  SystemProfileCache(bool testing, const std::string& config_root);
+  SystemProfileCache(bool testing, const base::FilePath& config_root);
 
   // Populates the ProfileSystem protobuf with system information.
   bool Populate(metrics::ChromeUserMetricsExtension* metrics_proto) override;
@@ -75,9 +76,14 @@
   // 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 |config_root_|/|name|
+  // instead.
+  std::string GetProperty(const std::string& name);
+
   bool initialized_;
   bool testing_;
-  std::string config_root_;
+  base::FilePath config_root_;
   scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
   SystemProfile profile_;
 };
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index cbb5277..40c235d 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -16,11 +16,13 @@
 
 #include <gtest/gtest.h>
 
-#include "base/at_exit.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/logging.h"
-#include "base/sys_info.h"
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/logging.h>
+#include <base/sys_info.h>
+
+#include "constants.h"
 #include "metrics_library_mock.h"
 #include "serialization/metric_sample.h"
 #include "uploader/metrics_log.h"
@@ -34,35 +36,35 @@
 
 class UploadServiceTest : public testing::Test {
  protected:
-  UploadServiceTest()
-      : cache_(true, "/"),
-        upload_service_(new MockSystemProfileSetter(), &metrics_lib_,
-                        kMetricsServer, true),
-        exit_manager_(new base::AtExitManager()) {
-    sender_ = new SenderMock;
-    upload_service_.sender_.reset(sender_);
-    upload_service_.Init(base::TimeDelta::FromMinutes(30), kMetricsFilePath);
-  }
-
   virtual void SetUp() {
     CHECK(dir_.CreateUniqueTempDir());
-    upload_service_.GatherHistograms();
-    upload_service_.Reset();
-    sender_->Reset();
+    upload_service_.reset(new UploadService(new MockSystemProfileSetter(),
+                                            &metrics_lib_, "", true));
 
-    chromeos_metrics::PersistentInteger::SetTestingMode(true);
-    cache_.session_id_.reset(new chromeos_metrics::PersistentInteger(
-        dir_.path().Append("session_id").value()));
+    upload_service_->sender_.reset(new SenderMock);
+    event_file_ = dir_.path().Append("event");
+    upload_service_->Init(base::TimeDelta::FromMinutes(30), event_file_.value());
+    upload_service_->GatherHistograms();
+    upload_service_->Reset();
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        dir_.path().value());
   }
 
   scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
     return metrics::MetricSample::CrashSample(name);
   }
 
+  void SetTestingProperty(const std::string& name, const std::string& value) {
+    ASSERT_EQ(
+        value.size(),
+        base::WriteFile(dir_.path().Append(name), value.data(), value.size()));
+  }
+
+  base::FilePath event_file_;
+
   base::ScopedTempDir dir_;
-  SenderMock* sender_;
-  SystemProfileCache cache_;
-  UploadService upload_service_;
+  scoped_ptr<UploadService> upload_service_;
   MetricsLibraryMock metrics_lib_;
 
   scoped_ptr<base::AtExitManager> exit_manager_;
@@ -70,18 +72,18 @@
 
 // Tests that the right crash increments a values.
 TEST_F(UploadServiceTest, LogUserCrash) {
-  upload_service_.AddSample(*Crash("user").get());
+  upload_service_->AddSample(*Crash("user").get());
 
-  MetricsLog* log = upload_service_.current_log_.get();
+  MetricsLog* log = upload_service_->current_log_.get();
   metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
 
   EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
 }
 
 TEST_F(UploadServiceTest, LogUncleanShutdown) {
-  upload_service_.AddSample(*Crash("uncleanshutdown"));
+  upload_service_->AddSample(*Crash("uncleanshutdown"));
 
-  EXPECT_EQ(1, upload_service_.current_log_
+  EXPECT_EQ(1, upload_service_->current_log_
                    ->uma_proto()
                    ->system_profile()
                    .stability()
@@ -89,9 +91,9 @@
 }
 
 TEST_F(UploadServiceTest, LogKernelCrash) {
-  upload_service_.AddSample(*Crash("kernel"));
+  upload_service_->AddSample(*Crash("kernel"));
 
-  EXPECT_EQ(1, upload_service_.current_log_
+  EXPECT_EQ(1, upload_service_->current_log_
                    ->uma_proto()
                    ->system_profile()
                    .stability()
@@ -99,47 +101,56 @@
 }
 
 TEST_F(UploadServiceTest, UnknownCrashIgnored) {
-  upload_service_.AddSample(*Crash("foo"));
+  upload_service_->AddSample(*Crash("foo"));
 
   // The log should be empty.
-  EXPECT_FALSE(upload_service_.current_log_);
+  EXPECT_FALSE(upload_service_->current_log_);
 }
 
 TEST_F(UploadServiceTest, FailedSendAreRetried) {
-  sender_->set_should_succeed(false);
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
 
-  upload_service_.AddSample(*Crash("user"));
-  upload_service_.UploadEvent();
-  EXPECT_EQ(1, sender_->send_call_count());
-  std::string sent_string = sender_->last_message();
+  sender->set_should_succeed(false);
 
-  upload_service_.UploadEvent();
-  EXPECT_EQ(2, sender_->send_call_count());
-  EXPECT_EQ(sent_string, sender_->last_message());
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
+  EXPECT_EQ(1, sender->send_call_count());
+  std::string sent_string = sender->last_message();
+
+  upload_service_->UploadEvent();
+  EXPECT_EQ(2, sender->send_call_count());
+  EXPECT_EQ(sent_string, sender->last_message());
 }
 
 TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
-  sender_->set_should_succeed(false);
-  upload_service_.AddSample(*Crash("user"));
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  sender->set_should_succeed(false);
+
+  upload_service_->AddSample(*Crash("user"));
 
   for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
-    upload_service_.UploadEvent();
+    upload_service_->UploadEvent();
   }
 
-  EXPECT_TRUE(upload_service_.staged_log_);
-  upload_service_.UploadEvent();
-  EXPECT_FALSE(upload_service_.staged_log_);
+  EXPECT_TRUE(upload_service_->staged_log_);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->staged_log_);
 }
 
 TEST_F(UploadServiceTest, EmptyLogsAreNotSent) {
-  upload_service_.UploadEvent();
-  EXPECT_FALSE(upload_service_.current_log_);
-  EXPECT_EQ(0, sender_->send_call_count());
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
+  EXPECT_EQ(0, sender->send_call_count());
 }
 
 TEST_F(UploadServiceTest, LogEmptyByDefault) {
   UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
-                               kMetricsServer);
+                               "");
 
   // current_log_ should be initialized later as it needs AtExitManager to exit
   // in order to gather system information from SysInfo.
@@ -147,39 +158,42 @@
 }
 
 TEST_F(UploadServiceTest, CanSendMultipleTimes) {
-  upload_service_.AddSample(*Crash("user"));
-  upload_service_.UploadEvent();
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
 
-  std::string first_message = sender_->last_message();
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
 
-  upload_service_.AddSample(*Crash("kernel"));
-  upload_service_.UploadEvent();
+  std::string first_message = sender->last_message();
 
-  EXPECT_NE(first_message, sender_->last_message());
+  upload_service_->AddSample(*Crash("kernel"));
+  upload_service_->UploadEvent();
+
+  EXPECT_NE(first_message, sender->last_message());
 }
 
 TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
-  upload_service_.AddSample(*Crash("user"));
+  upload_service_->AddSample(*Crash("user"));
 
-  EXPECT_TRUE(upload_service_.current_log_);
+  EXPECT_TRUE(upload_service_->current_log_);
 
-  upload_service_.UploadEvent();
-  EXPECT_FALSE(upload_service_.current_log_);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
 }
 
 TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
   scoped_ptr<metrics::MetricSample> histogram =
       metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
-  upload_service_.AddSample(*histogram.get());
+  upload_service_->AddSample(*histogram.get());
 
 
   scoped_ptr<metrics::MetricSample> histogram2 =
       metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
-  upload_service_.AddSample(*histogram2.get());
+  upload_service_->AddSample(*histogram2.get());
 
-  upload_service_.GatherHistograms();
+  upload_service_->GatherHistograms();
   metrics::ChromeUserMetricsExtension* proto =
-      upload_service_.current_log_->uma_proto();
+      upload_service_->current_log_->uma_proto();
   EXPECT_EQ(1, proto->histogram_event().size());
 }
 
@@ -190,46 +204,41 @@
       metrics::SystemProfileProto::CHANNEL_UNKNOWN);
 
   EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
-            SystemProfileCache::ProtoChannelFromString("dev-channel"));
+            SystemProfileCache::ProtoChannelFromString("dev"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_STABLE,
+            SystemProfileCache::ProtoChannelFromString("stable"));
 
   EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
-            SystemProfileCache::ProtoChannelFromString("dev-channel test"));
+            SystemProfileCache::ProtoChannelFromString("this is a test"));
 }
 
 TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
-  std::string name("os name");
-  std::string content(
-      "CHROMEOS_RELEASE_NAME=" + name +
-      "\nCHROMEOS_RELEASE_VERSION=version\n"
-      "CHROMEOS_RELEASE_DESCRIPTION=description beta-channel test\n"
-      "CHROMEOS_RELEASE_TRACK=beta-channel\n"
-      "CHROMEOS_RELEASE_BUILD_TYPE=developer build\n"
-      "CHROMEOS_RELEASE_BOARD=myboard");
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
 
-  base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time());
+  SetTestingProperty(metrics::kChannelProperty, "beta");
+  SetTestingProperty(metrics::kBuildTargetIdProperty, "hello");
+  SetTestingProperty(metrics::kProductVersionProperty, "1.2.3.4");
+
   scoped_ptr<metrics::MetricSample> histogram =
       metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
-  SystemProfileCache* local_cache_ = new SystemProfileCache(true, "/");
-  local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger(
-        dir_.path().Append("session_id").value()));
-
-  upload_service_.system_profile_setter_.reset(local_cache_);
   // Reset to create the new log with the profile setter.
-  upload_service_.Reset();
-  upload_service_.AddSample(*histogram.get());
-  upload_service_.UploadEvent();
+  upload_service_->system_profile_setter_.reset(
+      new SystemProfileCache(true, dir_.path()));
+  upload_service_->Reset();
+  upload_service_->AddSample(*histogram.get());
+  upload_service_->UploadEvent();
 
-  EXPECT_EQ(1, sender_->send_call_count());
-  EXPECT_TRUE(sender_->is_good_proto());
-  EXPECT_EQ(1, sender_->last_message_proto().histogram_event().size());
+  EXPECT_EQ(1, sender->send_call_count());
+  EXPECT_TRUE(sender->is_good_proto());
+  EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
 
-  EXPECT_EQ(name, sender_->last_message_proto().system_profile().os().name());
   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());
+            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());
 }
 
 TEST_F(UploadServiceTest, PersistentGUID) {
@@ -252,15 +261,11 @@
 }
 
 TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
-  cache_.Initialize();
-  int session_id = cache_.profile_.session_id;
-  cache_.initialized_ = false;
-  cache_.Initialize();
-  EXPECT_EQ(cache_.profile_.session_id, session_id + 1);
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-
-  return RUN_ALL_TESTS();
+  SetTestingProperty(metrics::kBuildTargetIdProperty, "hello");
+  SystemProfileCache cache(true, dir_.path());
+  cache.Initialize();
+  int session_id = cache.profile_.session_id;
+  cache.initialized_ = false;
+  cache.Initialize();
+  EXPECT_EQ(cache.profile_.session_id, session_id + 1);
 }